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
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
34 #include "errorlist.h"
36 #include "FloatList.h"
37 #include "funcrequest.h"
42 #include "lyxrow_funcs.h"
43 #include "paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
49 #include "frontends/font_metrics.h"
50 #include "frontends/LyXView.h"
52 #include "insets/insetbibitem.h"
53 #include "insets/insetenv.h"
54 #include "insets/insetfloat.h"
55 #include "insets/insetwrap.h"
57 #include "support/lstrings.h"
58 #include "support/textutils.h"
59 #include "support/tostr.h"
60 #include "support/std_sstream.h"
62 #include <boost/tuple/tuple.hpp>
65 using lyx::paroffset_type;
66 using lyx::support::bformat;
69 using std::ostringstream;
73 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
74 ParagraphList & paragraphs)
75 : height(0), width(0),
76 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
77 in_inset_(ininset), paragraphs_(¶graphs),
83 void LyXText::init(BufferView * bview)
87 ParagraphList::iterator const beg = ownerParagraphs().begin();
88 ParagraphList::iterator const end = ownerParagraphs().end();
89 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
96 current_font = getFont(beg, 0);
98 redoParagraphs(beg, end);
99 setCursorIntern(0, 0);
100 selection.cursor = cursor;
106 // Gets the fully instantiated font at a given position in a paragraph
107 // Basically the same routine as Paragraph::getFont() in paragraph.C.
108 // The difference is that this one is used for displaying, and thus we
109 // are allowed to make cosmetic improvements. For instance make footnotes
111 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
113 BOOST_ASSERT(pos >= 0);
115 LyXLayout_ptr const & layout = pit->layout();
117 BufferParams const & params = bv()->buffer()->params();
118 pos_type const body_pos = pit->beginningOfBody();
120 // We specialize the 95% common case:
121 if (!pit->getDepth()) {
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
126 return f.realize(layout->reslabelfont);
128 return f.realize(layout->resfont);
131 // The uncommon case need not be optimized as much
134 layoutfont = layout->labelfont;
136 layoutfont = layout->font;
138 LyXFont font = pit->getFontSettings(params, pos);
139 font.realize(layoutfont);
142 pit->inInset()->getDrawFont(font);
144 // Realize with the fonts of lesser depth.
145 //font.realize(outerFont(pit, ownerParagraphs()));
146 font.realize(defaultfont_);
152 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
154 LyXLayout_ptr const & layout = pit->layout();
156 if (!pit->getDepth())
157 return layout->resfont;
159 LyXFont font = layout->font;
160 // Realize with the fonts of lesser depth.
161 //font.realize(outerFont(pit, ownerParagraphs()));
162 font.realize(defaultfont_);
168 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
170 LyXLayout_ptr const & layout = pit->layout();
172 if (!pit->getDepth())
173 return layout->reslabelfont;
175 LyXFont font = layout->labelfont;
176 // Realize with the fonts of lesser depth.
177 font.realize(outerFont(pit, ownerParagraphs()));
178 font.realize(defaultfont_);
184 void LyXText::setCharFont(ParagraphList::iterator pit,
185 pos_type pos, LyXFont const & fnt,
188 BufferParams const & params = bv()->buffer()->params();
189 LyXFont font = getFont(pit, pos);
190 font.update(fnt, params.language, toggleall);
191 // Let the insets convert their font
192 if (pit->isInset(pos)) {
193 InsetOld * inset = pit->getInset(pos);
194 if (isEditableInset(inset)) {
195 static_cast<UpdatableInset *>(inset)
196 ->setFont(bv(), fnt, toggleall, true);
200 // Plug through to version below:
201 setCharFont(pit, pos, font);
205 void LyXText::setCharFont(
206 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
209 LyXLayout_ptr const & layout = pit->layout();
211 // Get concrete layout font to reduce against
214 if (pos < pit->beginningOfBody())
215 layoutfont = layout->labelfont;
217 layoutfont = layout->font;
219 // Realize against environment font information
220 if (pit->getDepth()) {
221 ParagraphList::iterator tp = pit;
222 while (!layoutfont.resolved() &&
223 tp != ownerParagraphs().end() &&
225 tp = outerHook(tp, ownerParagraphs());
226 if (tp != ownerParagraphs().end())
227 layoutfont.realize(tp->layout()->font);
231 layoutfont.realize(defaultfont_);
233 // Now, reduce font against full layout font
234 font.reduce(layoutfont);
236 pit->setFont(pos, font);
240 InsetOld * LyXText::getInset() const
242 ParagraphList::iterator pit = cursorPar();
243 pos_type const pos = cursor.pos();
245 if (pos < pit->size() && pit->isInset(pos)) {
246 return pit->getInset(pos);
252 void LyXText::toggleInset()
254 InsetOld * inset = getInset();
255 // is there an editable inset at cursor position?
256 if (!isEditableInset(inset)) {
257 // No, try to see if we are inside a collapsable inset
258 if (inset_owner && inset_owner->owner()
259 && inset_owner->owner()->isOpen()) {
260 bv()->unlockInset(inset_owner->owner());
261 inset_owner->owner()->close(bv());
262 bv()->getLyXText()->cursorRight(bv());
266 //bv()->owner()->message(inset->editMessage());
268 // do we want to keep this?? (JMarc)
269 if (!isHighlyEditableInset(inset))
270 recUndo(cursor.par());
277 bv()->updateInset(inset);
281 /* used in setlayout */
282 // Asger is not sure we want to do this...
283 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
286 LyXLayout_ptr const & layout = par.layout();
287 pos_type const psize = par.size();
290 for (pos_type pos = 0; pos < psize; ++pos) {
291 if (pos < par.beginningOfBody())
292 layoutfont = layout->labelfont;
294 layoutfont = layout->font;
296 LyXFont tmpfont = par.getFontSettings(params, pos);
297 tmpfont.reduce(layoutfont);
298 par.setFont(pos, tmpfont);
303 ParagraphList::iterator
304 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
305 LyXCursor & send_cur,
306 string const & layout)
308 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
309 ParagraphList::iterator undoendpit = endpit;
310 ParagraphList::iterator pars_end = ownerParagraphs().end();
312 if (endpit != pars_end && endpit->getDepth()) {
313 while (endpit != pars_end && endpit->getDepth()) {
317 } else if (endpit != pars_end) {
318 // because of parindents etc.
322 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
324 // ok we have a selection. This is always between sstart_cur
325 // and sel_end cursor
327 ParagraphList::iterator pit = getPar(sstart_cur);
328 ParagraphList::iterator epit = boost::next(getPar(send_cur));
330 BufferParams const & bufparams = bv()->buffer()->params();
331 LyXLayout_ptr const & lyxlayout =
332 bufparams.getLyXTextClass()[layout];
335 pit->applyLayout(lyxlayout);
336 makeFontEntriesLayoutSpecific(bufparams, *pit);
337 pit->params().spaceTop(lyxlayout->fill_top ?
338 VSpace(VSpace::VFILL)
339 : VSpace(VSpace::NONE));
340 pit->params().spaceBottom(lyxlayout->fill_bottom ?
341 VSpace(VSpace::VFILL)
342 : VSpace(VSpace::NONE));
343 if (lyxlayout->margintype == MARGIN_MANUAL)
344 pit->setLabelWidthString(lyxlayout->labelstring());
345 cur.par(std::distance(ownerParagraphs().begin(), pit));
347 } while (pit != epit);
353 // set layout over selection and make a total rebreak of those paragraphs
354 void LyXText::setLayout(string const & layout)
356 LyXCursor tmpcursor = cursor; // store the current cursor
358 // if there is no selection just set the layout
359 // of the current paragraph
360 if (!selection.set()) {
361 selection.start = cursor; // dummy selection
362 selection.end = cursor;
365 // special handling of new environment insets
366 BufferParams const & params = bv()->buffer()->params();
367 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
368 if (lyxlayout->is_environment) {
369 // move everything in a new environment inset
370 lyxerr << "setting layout " << layout << endl;
371 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
372 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
373 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
374 InsetOld * inset = new InsetEnvironment(params, layout);
375 if (bv()->insertInset(inset)) {
377 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
384 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
385 selection.end, layout);
386 redoParagraphs(getPar(selection.start), endpit);
388 // we have to reset the selection, because the
389 // geometry could have changed
390 setCursor(selection.start.par(), selection.start.pos(), false);
391 selection.cursor = cursor;
392 setCursor(selection.end.par(), selection.end.pos(), false);
396 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
400 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
402 ParagraphList::iterator pit = cursorPar();
403 ParagraphList::iterator end = cursorPar();
404 ParagraphList::iterator start = pit;
406 if (selection.set()) {
407 pit = getPar(selection.start);
408 end = getPar(selection.end);
412 ParagraphList::iterator pastend = boost::next(end);
415 recUndo(parOffset(start), parOffset(end));
417 bool changed = false;
419 int prev_after_depth = 0;
420 #warning parlist ... could be nicer ?
421 if (start != ownerParagraphs().begin()) {
422 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
426 int const depth = pit->params().depth();
427 if (type == bv_funcs::INC_DEPTH) {
428 if (depth < prev_after_depth
429 && pit->layout()->labeltype != LABEL_BIBLIO) {
432 pit->params().depth(depth + 1);
437 pit->params().depth(depth - 1);
440 prev_after_depth = pit->getMaxDepthAfter();
452 redoParagraphs(start, pastend);
454 // We need to actually move the text->cursor. I don't
455 // understand why ...
456 LyXCursor tmpcursor = cursor;
458 // we have to reset the visual selection because the
459 // geometry could have changed
460 if (selection.set()) {
461 setCursor(selection.start.par(), selection.start.pos());
462 selection.cursor = cursor;
463 setCursor(selection.end.par(), selection.end.pos());
466 // this handles the counter labels, and also fixes up
467 // depth values for follow-on (child) paragraphs
471 setCursor(tmpcursor.par(), tmpcursor.pos());
477 // set font over selection and make a total rebreak of those paragraphs
478 void LyXText::setFont(LyXFont const & font, bool toggleall)
480 // if there is no selection just set the current_font
481 if (!selection.set()) {
482 // Determine basis font
484 if (cursor.pos() < cursorPar()->beginningOfBody()) {
485 layoutfont = getLabelFont(cursorPar());
487 layoutfont = getLayoutFont(cursorPar());
489 // Update current font
490 real_current_font.update(font,
491 bv()->buffer()->params().language,
494 // Reduce to implicit settings
495 current_font = real_current_font;
496 current_font.reduce(layoutfont);
497 // And resolve it completely
498 real_current_font.realize(layoutfont);
503 LyXCursor tmpcursor = cursor; // store the current cursor
505 // ok we have a selection. This is always between sel_start_cursor
506 // and sel_end cursor
508 recUndo(selection.start.par(), selection.end.par());
510 cursor = selection.start;
511 while (cursor.par() != selection.end.par() ||
512 cursor.pos() < selection.end.pos())
514 if (cursor.pos() < cursorPar()->size()) {
515 // an open footnote should behave like a closed one
516 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
517 cursor.pos(cursor.pos() + 1);
520 cursor.par(cursor.par() + 1);
525 redoParagraph(getPar(selection.start));
527 // we have to reset the selection, because the
528 // geometry could have changed, but we keep
529 // it for user convenience
530 setCursor(selection.start.par(), selection.start.pos());
531 selection.cursor = cursor;
532 setCursor(selection.end.par(), selection.end.pos());
534 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
535 tmpcursor.boundary());
539 // important for the screen
542 // the cursor set functions have a special mechanism. When they
543 // realize, that you left an empty paragraph, they will delete it.
545 // need the selection cursor:
546 void LyXText::setSelection()
548 TextCursor::setSelection();
553 void LyXText::clearSelection()
555 TextCursor::clearSelection();
557 // reset this in the bv_owner!
558 if (bv_owner && bv_owner->text)
559 bv_owner->text->xsel_cache.set(false);
563 void LyXText::cursorHome()
565 ParagraphList::iterator cpit = cursorPar();
566 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
570 void LyXText::cursorEnd()
572 ParagraphList::iterator cpit = cursorPar();
573 pos_type end = cpit->getRow(cursor.pos())->endpos();
574 /* if not on the last row of the par, put the cursor before
576 setCursor(cpit, end == cpit->size() ? end : end - 1);
580 void LyXText::cursorTop()
582 setCursor(ownerParagraphs().begin(), 0);
586 void LyXText::cursorBottom()
588 ParagraphList::iterator lastpit =
589 boost::prior(ownerParagraphs().end());
590 setCursor(lastpit, lastpit->size());
594 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
596 // If the mask is completely neutral, tell user
597 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
598 // Could only happen with user style
599 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
603 // Try implicit word selection
604 // If there is a change in the language the implicit word selection
606 LyXCursor resetCursor = cursor;
607 bool implicitSelection =
608 font.language() == ignore_language
609 && font.number() == LyXFont::IGNORE
610 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
613 setFont(font, toggleall);
615 // Implicit selections are cleared afterwards
616 //and cursor is set to the original position.
617 if (implicitSelection) {
619 cursor = resetCursor;
620 setCursor(cursorPar(), cursor.pos());
621 selection.cursor = cursor;
626 string LyXText::getStringToIndex()
628 // Try implicit word selection
629 // If there is a change in the language the implicit word selection
631 LyXCursor const reset_cursor = cursor;
632 bool const implicitSelection =
633 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
636 if (!selection.set())
637 bv()->owner()->message(_("Nothing to index!"));
638 else if (selection.start.par() != selection.end.par())
639 bv()->owner()->message(_("Cannot index more than one paragraph!"));
641 idxstring = selectionAsString(*bv()->buffer(), false);
643 // Reset cursors to their original position.
644 cursor = reset_cursor;
645 setCursor(cursorPar(), cursor.pos());
646 selection.cursor = cursor;
648 // Clear the implicit selection.
649 if (implicitSelection)
656 // the DTP switches for paragraphs. LyX will store them in the first
657 // physical paragraph. When a paragraph is broken, the top settings rest,
658 // the bottom settings are given to the new one. So I can make sure,
659 // they do not duplicate themself and you cannnot make dirty things with
662 void LyXText::setParagraph(
663 VSpace const & space_top,
664 VSpace const & space_bottom,
665 Spacing const & spacing,
667 string const & labelwidthstring,
670 LyXCursor tmpcursor = cursor;
671 if (!selection.set()) {
672 selection.start = cursor;
673 selection.end = cursor;
676 // make sure that the depth behind the selection are restored, too
677 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
678 ParagraphList::iterator undoendpit = endpit;
679 ParagraphList::iterator pars_end = ownerParagraphs().end();
681 if (endpit != pars_end && endpit->getDepth()) {
682 while (endpit != pars_end && endpit->getDepth()) {
686 } else if (endpit != pars_end) {
687 // because of parindents etc.
691 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
694 int tmppit = selection.end.par();
696 while (tmppit != selection.start.par() - 1) {
697 setCursor(tmppit, 0);
699 ParagraphList::iterator const pit = cursorPar();
700 ParagraphParameters & params = pit->params();
701 params.spaceTop(space_top);
702 params.spaceBottom(space_bottom);
703 params.spacing(spacing);
705 // does the layout allow the new alignment?
706 LyXLayout_ptr const & layout = pit->layout();
708 if (align == LYX_ALIGN_LAYOUT)
709 align = layout->align;
710 if (align & layout->alignpossible) {
711 if (align == layout->align)
712 params.align(LYX_ALIGN_LAYOUT);
716 pit->setLabelWidthString(labelwidthstring);
717 params.noindent(noindent);
721 redoParagraphs(getPar(selection.start), endpit);
724 setCursor(selection.start.par(), selection.start.pos());
725 selection.cursor = cursor;
726 setCursor(selection.end.par(), selection.end.pos());
728 setCursor(tmpcursor.par(), tmpcursor.pos());
730 bv()->updateInset(inset_owner);
736 string expandLabel(LyXTextClass const & textclass,
737 LyXLayout_ptr const & layout, bool appendix)
739 string fmt = appendix ?
740 layout->labelstring_appendix() : layout->labelstring();
742 // handle 'inherited level parts' in 'fmt',
743 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
744 size_t const i = fmt.find('@', 0);
745 if (i != string::npos) {
746 size_t const j = fmt.find('@', i + 1);
747 if (j != string::npos) {
748 string parent(fmt, i + 1, j - i - 1);
749 string label = expandLabel(textclass, textclass[parent], appendix);
750 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
754 return textclass.counters().counterLabel(fmt);
758 void incrementItemDepth(ParagraphList::iterator pit,
759 ParagraphList::iterator first_pit)
761 int const cur_labeltype = pit->layout()->labeltype;
763 if (cur_labeltype != LABEL_ENUMERATE &&
764 cur_labeltype != LABEL_ITEMIZE)
767 int const cur_depth = pit->getDepth();
769 ParagraphList::iterator prev_pit = boost::prior(pit);
771 int const prev_depth = prev_pit->getDepth();
772 int const prev_labeltype = prev_pit->layout()->labeltype;
773 if (prev_depth == 0 && cur_depth > 0) {
774 if (prev_labeltype == cur_labeltype) {
775 pit->itemdepth = prev_pit->itemdepth + 1;
778 } else if (prev_depth < cur_depth) {
779 if (prev_labeltype == cur_labeltype) {
780 pit->itemdepth = prev_pit->itemdepth + 1;
783 } else if (prev_depth == cur_depth) {
784 if (prev_labeltype == cur_labeltype) {
785 pit->itemdepth = prev_pit->itemdepth;
789 if (prev_pit == first_pit)
797 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
798 ParagraphList::iterator firstpit,
804 int const cur_depth = pit->getDepth();
805 ParagraphList::iterator prev_pit = boost::prior(pit);
807 int const prev_depth = prev_pit->getDepth();
808 int const prev_labeltype = prev_pit->layout()->labeltype;
809 if (prev_depth <= cur_depth) {
810 if (prev_labeltype != LABEL_ENUMERATE) {
811 switch (pit->itemdepth) {
813 counters.reset("enumi");
815 counters.reset("enumii");
817 counters.reset("enumiii");
819 counters.reset("enumiv");
825 if (prev_pit == firstpit)
835 // set the counter of a paragraph. This includes the labels
836 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
838 BufferParams const & bufparams = buf.params();
839 LyXTextClass const & textclass = bufparams.getLyXTextClass();
840 LyXLayout_ptr const & layout = pit->layout();
841 ParagraphList::iterator first_pit = ownerParagraphs().begin();
842 Counters & counters = textclass.counters();
847 if (pit == first_pit) {
848 pit->params().appendix(pit->params().startOfAppendix());
850 pit->params().appendix(boost::prior(pit)->params().appendix());
851 if (!pit->params().appendix() &&
852 pit->params().startOfAppendix()) {
853 pit->params().appendix(true);
854 textclass.counters().reset();
857 // Maybe we have to increment the item depth.
858 incrementItemDepth(pit, first_pit);
861 // erase what was there before
862 pit->params().labelString(string());
864 if (layout->margintype == MARGIN_MANUAL) {
865 if (pit->params().labelWidthString().empty())
866 pit->setLabelWidthString(layout->labelstring());
868 pit->setLabelWidthString(string());
871 // is it a layout that has an automatic label?
872 if (layout->labeltype == LABEL_COUNTER) {
873 BufferParams const & bufparams = buf.params();
874 LyXTextClass const & textclass = bufparams.getLyXTextClass();
875 counters.step(layout->counter);
876 string label = expandLabel(textclass, layout, pit->params().appendix());
877 pit->params().labelString(label);
878 } else if (layout->labeltype == LABEL_ITEMIZE) {
879 // At some point of time we should do something more
880 // clever here, like:
881 // pit->params().labelString(
882 // bufparams.user_defined_bullet(pit->itemdepth).getText());
883 // for now, use a simple hardcoded label
885 switch (pit->itemdepth) {
900 pit->params().labelString(itemlabel);
901 } else if (layout->labeltype == LABEL_ENUMERATE) {
902 // Maybe we have to reset the enumeration counter.
903 resetEnumCounterIfNeeded(pit, first_pit, counters);
906 // Yes I know this is a really, really! bad solution
908 string enumcounter = "enum";
910 switch (pit->itemdepth) {
922 // not a valid enumdepth...
926 counters.step(enumcounter);
928 pit->params().labelString(counters.enumLabel(enumcounter));
929 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
930 counters.step("bibitem");
931 int number = counters.value("bibitem");
932 if (pit->bibitem()) {
933 pit->bibitem()->setCounter(number);
934 pit->params().labelString(layout->labelstring());
936 // In biblio should't be following counters but...
938 string s = buf.B_(layout->labelstring());
941 if (layout->labeltype == LABEL_SENSITIVE) {
942 ParagraphList::iterator end = ownerParagraphs().end();
943 ParagraphList::iterator tmppit = pit;
946 while (tmppit != end && tmppit->inInset()
947 // the single '=' is intended below
948 && (in = tmppit->inInset()->owner()))
950 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
951 in->lyxCode() == InsetOld::WRAP_CODE) {
955 Paragraph const * owner = &ownerPar(buf, in);
957 for ( ; tmppit != end; ++tmppit)
958 if (&*tmppit == owner)
966 if (in->lyxCode() == InsetOld::FLOAT_CODE)
967 type = static_cast<InsetFloat*>(in)->params().type;
968 else if (in->lyxCode() == InsetOld::WRAP_CODE)
969 type = static_cast<InsetWrap*>(in)->params().type;
973 Floating const & fl = textclass.floats().getType(type);
975 counters.step(fl.type());
977 // Doesn't work... yet.
978 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
980 // par->SetLayout(0);
981 // s = layout->labelstring;
982 s = _("Senseless: ");
985 pit->params().labelString(s);
991 // Updates all counters. Paragraphs with changed label string will be rebroken
992 void LyXText::updateCounters()
995 bv()->buffer()->params().getLyXTextClass().counters().reset();
997 ParagraphList::iterator beg = ownerParagraphs().begin();
998 ParagraphList::iterator end = ownerParagraphs().end();
999 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1000 string const oldLabel = pit->params().labelString();
1002 size_t maxdepth = 0;
1004 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1006 if (pit->params().depth() > maxdepth)
1007 pit->params().depth(maxdepth);
1009 // setCounter can potentially change the labelString.
1010 setCounter(*bv()->buffer(), pit);
1012 string const & newLabel = pit->params().labelString();
1014 if (oldLabel != newLabel)
1020 void LyXText::insertInset(InsetOld * inset)
1022 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1024 recUndo(cursor.par());
1026 cursorPar()->insertInset(cursor.pos(), inset);
1027 // Just to rebreak and refresh correctly.
1028 // The character will not be inserted a second time
1029 insertChar(Paragraph::META_INSET);
1030 // If we enter a highly editable inset the cursor should be before
1031 // the inset. After an Undo LyX tries to call inset->edit(...)
1032 // and fails if the cursor is behind the inset and getInset
1033 // does not return the inset!
1034 if (isHighlyEditableInset(inset))
1040 void LyXText::cutSelection(bool doclear, bool realcut)
1042 // Stuff what we got on the clipboard. Even if there is no selection.
1044 // There is a problem with having the stuffing here in that the
1045 // larger the selection the slower LyX will get. This can be
1046 // solved by running the line below only when the selection has
1047 // finished. The solution used currently just works, to make it
1048 // faster we need to be more clever and probably also have more
1049 // calls to stuffClipboard. (Lgb)
1050 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1052 // This doesn't make sense, if there is no selection
1053 if (!selection.set())
1056 // OK, we have a selection. This is always between selection.start
1057 // and selection.end
1059 // make sure that the depth behind the selection are restored, too
1060 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1061 ParagraphList::iterator undoendpit = endpit;
1062 ParagraphList::iterator pars_end = ownerParagraphs().end();
1064 if (endpit != pars_end && endpit->getDepth()) {
1065 while (endpit != pars_end && endpit->getDepth()) {
1067 undoendpit = endpit;
1069 } else if (endpit != pars_end) {
1070 // because of parindents etc.
1074 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1076 endpit = getPar(selection.end.par());
1077 int endpos = selection.end.pos();
1079 BufferParams const & bufparams = bv()->buffer()->params();
1080 boost::tie(endpit, endpos) = realcut ?
1081 CutAndPaste::cutSelection(bufparams,
1083 getPar(selection.start.par()), endpit,
1084 selection.start.pos(), endpos,
1085 bufparams.textclass,
1087 : CutAndPaste::eraseSelection(bufparams,
1089 getPar(selection.start.par()), endpit,
1090 selection.start.pos(), endpos,
1092 // sometimes necessary
1094 getPar(selection.start.par())->stripLeadingSpaces();
1096 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1097 // cutSelection can invalidate the cursor so we need to set
1099 // we prefer the end for when tracking changes
1101 cursor.par(parOffset(endpit));
1103 // need a valid cursor. (Lgb)
1106 setCursor(cursorPar(), cursor.pos());
1107 selection.cursor = cursor;
1112 void LyXText::copySelection()
1114 // stuff the selection onto the X clipboard, from an explicit copy request
1115 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1117 // this doesnt make sense, if there is no selection
1118 if (!selection.set())
1121 // ok we have a selection. This is always between selection.start
1122 // and sel_end cursor
1124 // copy behind a space if there is one
1125 while (getPar(selection.start)->size() > selection.start.pos()
1126 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1127 && (selection.start.par() != selection.end.par()
1128 || selection.start.pos() < selection.end.pos()))
1129 selection.start.pos(selection.start.pos() + 1);
1131 CutAndPaste::copySelection(getPar(selection.start.par()),
1132 getPar(selection.end.par()),
1133 selection.start.pos(), selection.end.pos(),
1134 bv()->buffer()->params().textclass);
1138 void LyXText::pasteSelection(size_t sel_index)
1140 // this does not make sense, if there is nothing to paste
1141 if (!CutAndPaste::checkPastePossible())
1144 recUndo(cursor.par());
1146 ParagraphList::iterator endpit;
1151 boost::tie(ppp, endpit) =
1152 CutAndPaste::pasteSelection(*bv()->buffer(),
1154 cursorPar(), cursor.pos(),
1155 bv()->buffer()->params().textclass,
1157 bufferErrors(*bv()->buffer(), el);
1158 bv()->showErrorList(_("Paste"));
1160 redoParagraphs(cursorPar(), endpit);
1162 setCursor(cursor.par(), cursor.pos());
1165 selection.cursor = cursor;
1166 setCursor(ppp.first, ppp.second);
1172 void LyXText::setSelectionRange(lyx::pos_type length)
1177 selection.cursor = cursor;
1184 // simple replacing. The font of the first selected character is used
1185 void LyXText::replaceSelectionWithString(string const & str)
1187 recUndo(cursor.par());
1190 if (!selection.set()) { // create a dummy selection
1191 selection.end = cursor;
1192 selection.start = cursor;
1195 // Get font setting before we cut
1196 pos_type pos = selection.end.pos();
1197 LyXFont const font = getPar(selection.start)
1198 ->getFontSettings(bv()->buffer()->params(),
1199 selection.start.pos());
1201 // Insert the new string
1202 string::const_iterator cit = str.begin();
1203 string::const_iterator end = str.end();
1204 for (; cit != end; ++cit) {
1205 getPar(selection.end)->insertChar(pos, (*cit), font);
1209 // Cut the selection
1210 cutSelection(true, false);
1216 // needed to insert the selection
1217 void LyXText::insertStringAsLines(string const & str)
1219 ParagraphList::iterator pit = cursorPar();
1220 pos_type pos = cursor.pos();
1221 ParagraphList::iterator endpit = boost::next(cursorPar());
1223 recUndo(cursor.par());
1225 // only to be sure, should not be neccessary
1228 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1230 redoParagraphs(cursorPar(), endpit);
1231 setCursor(cursorPar(), cursor.pos());
1232 selection.cursor = cursor;
1233 setCursor(pit, pos);
1238 // turns double-CR to single CR, others where converted into one
1239 // blank. Then InsertStringAsLines is called
1240 void LyXText::insertStringAsParagraphs(string const & str)
1242 string linestr(str);
1243 bool newline_inserted = false;
1244 string::size_type const siz = linestr.length();
1246 for (string::size_type i = 0; i < siz; ++i) {
1247 if (linestr[i] == '\n') {
1248 if (newline_inserted) {
1249 // we know that \r will be ignored by
1250 // InsertStringA. Of course, it is a dirty
1251 // trick, but it works...
1252 linestr[i - 1] = '\r';
1256 newline_inserted = true;
1258 } else if (IsPrintable(linestr[i])) {
1259 newline_inserted = false;
1262 insertStringAsLines(linestr);
1266 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1268 setCursor(parOffset(pit), pos);
1272 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1274 LyXCursor old_cursor = cursor;
1275 setCursorIntern(par, pos, setfont, boundary);
1276 return deleteEmptyParagraphMechanism(old_cursor);
1280 void LyXText::redoCursor()
1282 #warning maybe the same for selections?
1283 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1287 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1288 pos_type pos, bool boundary)
1290 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1294 cur.boundary(boundary);
1296 // no rows, no fun...
1297 if (ownerParagraphs().begin()->rows.empty())
1300 // get the cursor y position in text
1302 ParagraphList::iterator pit = getPar(par);
1303 Row const & row = *pit->getRow(pos);
1304 int y = pit->y + row.y_offset();
1306 // y is now the beginning of the cursor row
1307 y += row.baseline();
1308 // y is now the cursor baseline
1311 pos_type const end = row.endpos();
1313 // None of these should happen, but we're scaredy-cats
1315 lyxerr << "dont like -1" << endl;
1318 BOOST_ASSERT(false);
1319 } else if (pos > pit->size()) {
1320 lyxerr << "dont like 1, pos: " << pos
1321 << " size: " << pit->size()
1322 << " row.pos():" << row.pos()
1323 << " paroffset: " << par << endl;
1326 BOOST_ASSERT(false);
1327 } else if (pos > end) {
1328 lyxerr << "dont like 2 please report" << endl;
1329 // This shouldn't happen.
1332 BOOST_ASSERT(false);
1333 } else if (pos < row.pos()) {
1334 lyxerr << "dont like 3 please report pos:" << pos
1335 << " size: " << pit->size()
1336 << " row.pos():" << row.pos()
1337 << " paroffset: " << par << endl;
1340 BOOST_ASSERT(false);
1342 // now get the cursors x position
1343 cur.x(int(getCursorX(pit, row, pos, boundary)));
1344 bv()->x_target(cur.x());
1348 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1349 pos_type pos, bool boundary) const
1351 pos_type cursor_vpos = 0;
1353 double fill_separator = row.fill_separator();
1354 double fill_hfill = row.fill_hfill();
1355 double fill_label_hfill = row.fill_label_hfill();
1356 pos_type const row_pos = row.pos();
1357 pos_type const end = row.endpos();
1360 cursor_vpos = row_pos;
1361 else if (pos >= end && !boundary)
1362 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1364 else if (pos > row_pos && (pos >= end || boundary))
1365 // Place cursor after char at (logical) position pos - 1
1366 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1367 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1369 // Place cursor before char at (logical) position pos
1370 cursor_vpos = (bidi.level(pos) % 2 == 0)
1371 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1373 pos_type body_pos = pit->beginningOfBody();
1375 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1378 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1379 pos_type pos = bidi.vis2log(vpos);
1380 if (body_pos > 0 && pos == body_pos - 1) {
1381 x += fill_label_hfill
1382 + font_metrics::width(pit->layout()->labelsep,
1384 if (pit->isLineSeparator(body_pos - 1))
1385 x -= singleWidth(pit, body_pos - 1);
1388 if (hfillExpansion(*pit, row, pos)) {
1389 x += singleWidth(pit, pos);
1390 if (pos >= body_pos)
1393 x += fill_label_hfill;
1394 } else if (pit->isSeparator(pos)) {
1395 x += singleWidth(pit, pos);
1396 if (pos >= body_pos)
1397 x += fill_separator;
1399 x += singleWidth(pit, pos);
1405 void LyXText::setCursorIntern(paroffset_type par,
1406 pos_type pos, bool setfont, bool boundary)
1408 setCursor(cursor, par, pos, boundary);
1414 void LyXText::setCurrentFont()
1416 pos_type pos = cursor.pos();
1417 ParagraphList::iterator pit = cursorPar();
1419 if (cursor.boundary() && pos > 0)
1423 if (pos == pit->size())
1425 else // potentional bug... BUG (Lgb)
1426 if (pit->isSeparator(pos)) {
1427 if (pos > pit->getRow(pos)->pos() &&
1428 bidi.level(pos) % 2 ==
1429 bidi.level(pos - 1) % 2)
1431 else if (pos + 1 < pit->size())
1436 BufferParams const & bufparams = bv()->buffer()->params();
1437 current_font = pit->getFontSettings(bufparams, pos);
1438 real_current_font = getFont(pit, pos);
1440 if (cursor.pos() == pit->size() &&
1441 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1442 !cursor.boundary()) {
1443 Language const * lang =
1444 pit->getParLanguage(bufparams);
1445 current_font.setLanguage(lang);
1446 current_font.setNumber(LyXFont::OFF);
1447 real_current_font.setLanguage(lang);
1448 real_current_font.setNumber(LyXFont::OFF);
1453 // returns the column near the specified x-coordinate of the row
1454 // x is set to the real beginning of this column
1455 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1456 Row const & row, int & x, bool & boundary) const
1458 double tmpx = row.x();
1459 double fill_separator = row.fill_separator();
1460 double fill_hfill = row.fill_hfill();
1461 double fill_label_hfill = row.fill_label_hfill();
1463 pos_type vc = row.pos();
1464 pos_type end = row.endpos();
1466 LyXLayout_ptr const & layout = pit->layout();
1468 bool left_side = false;
1470 pos_type body_pos = pit->beginningOfBody();
1471 double last_tmpx = tmpx;
1475 !pit->isLineSeparator(body_pos - 1)))
1478 // check for empty row
1484 while (vc < end && tmpx <= x) {
1485 c = bidi.vis2log(vc);
1487 if (body_pos > 0 && c == body_pos - 1) {
1488 tmpx += fill_label_hfill +
1489 font_metrics::width(layout->labelsep, getLabelFont(pit));
1490 if (pit->isLineSeparator(body_pos - 1))
1491 tmpx -= singleWidth(pit, body_pos - 1);
1494 if (hfillExpansion(*pit, row, c)) {
1495 tmpx += singleWidth(pit, c);
1499 tmpx += fill_label_hfill;
1500 } else if (pit->isSeparator(c)) {
1501 tmpx += singleWidth(pit, c);
1503 tmpx += fill_separator;
1505 tmpx += singleWidth(pit, c);
1510 if ((tmpx + last_tmpx) / 2 > x) {
1515 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1518 // This (rtl_support test) is not needed, but gives
1519 // some speedup if rtl_support == false
1520 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1522 // If lastrow is false, we don't need to compute
1523 // the value of rtl.
1524 bool const rtl = (lastrow)
1525 ? pit->isRightToLeftPar(bv()->buffer()->params())
1528 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1529 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1531 else if (vc == row.pos()) {
1532 c = bidi.vis2log(vc);
1533 if (bidi.level(c) % 2 == 1)
1536 c = bidi.vis2log(vc - 1);
1537 bool const rtl = (bidi.level(c) % 2 == 1);
1538 if (left_side == rtl) {
1540 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1544 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1545 if (bidi.level(end -1) % 2 == 0)
1546 tmpx -= singleWidth(pit, end - 1);
1548 tmpx += singleWidth(pit, end - 1);
1558 void LyXText::setCursorFromCoordinates(int x, int y)
1560 LyXCursor old_cursor = cursor;
1561 setCursorFromCoordinates(cursor, x, y);
1563 deleteEmptyParagraphMechanism(old_cursor);
1567 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1569 // Get the row first.
1570 ParagraphList::iterator pit;
1571 Row const & row = *getRowNearY(y, pit);
1572 y = pit->y + row.y_offset();
1575 pos_type const column = getColumnNearX(pit, row, x, bound);
1576 cur.par(parOffset(pit));
1577 cur.pos(row.pos() + column);
1579 cur.y(y + row.baseline());
1581 cur.boundary(bound);
1585 void LyXText::cursorLeft(bool internal)
1587 if (cursor.pos() > 0) {
1588 bool boundary = cursor.boundary();
1589 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1590 if (!internal && !boundary &&
1591 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1592 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1593 } else if (cursor.par() != 0) {
1594 // steps into the paragraph above
1595 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1600 void LyXText::cursorRight(bool internal)
1602 bool const at_end = (cursor.pos() == cursorPar()->size());
1603 bool const at_newline = !at_end &&
1604 cursorPar()->isNewline(cursor.pos());
1606 if (!internal && cursor.boundary() && !at_newline)
1607 setCursor(cursor.par(), cursor.pos(), true, false);
1609 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1610 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1612 setCursor(cursor.par(), cursor.pos(), true, true);
1613 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1614 setCursor(cursor.par() + 1, 0);
1618 void LyXText::cursorUp(bool selecting)
1620 ParagraphList::iterator cpit = cursorPar();
1621 Row const & crow = *cpit->getRow(cursor.pos());
1623 int x = bv()->x_target();
1624 int y = cursor.y() - crow.baseline() - 1;
1625 setCursorFromCoordinates(x, y);
1627 int topy = bv_owner->top_y();
1628 int y1 = cursor.y() - topy;
1631 InsetOld * inset_hit = checkInsetHit(x, y1);
1632 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1633 inset_hit->edit(bv(), x, y - (y2 - y1));
1634 bv()->cursor().push(inset_hit, inset_hit->getText(0));
1638 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1639 crow.baseline() << endl;
1640 setCursorFromCoordinates(bv()->x_target(),
1641 cursor.y() - crow.baseline() - 1);
1646 void LyXText::cursorDown(bool selecting)
1648 ParagraphList::iterator cpit = cursorPar();
1649 Row const & crow = *cpit->getRow(cursor.pos());
1651 int x = bv()->x_target();
1652 int y = cursor.y() - crow.baseline() + crow.height() + 1;
1653 setCursorFromCoordinates(x, y);
1655 int topy = bv_owner->top_y();
1656 int y1 = cursor.y() - topy;
1659 InsetOld * inset_hit = checkInsetHit(x, y1);
1660 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1661 bv()->cursor().push(inset_hit, inset_hit->getText(0));
1662 inset_hit->edit(bv(), x, y - (y2 - y1));
1666 setCursorFromCoordinates(bv()->x_target(),
1667 cursor.y() - crow.baseline() + crow.height() + 1);
1672 void LyXText::cursorUpParagraph()
1674 ParagraphList::iterator cpit = cursorPar();
1675 if (cursor.pos() > 0)
1677 else if (cpit != ownerParagraphs().begin())
1678 setCursor(boost::prior(cpit), 0);
1682 void LyXText::cursorDownParagraph()
1684 ParagraphList::iterator pit = cursorPar();
1685 ParagraphList::iterator next_pit = boost::next(pit);
1687 if (next_pit != ownerParagraphs().end())
1688 setCursor(next_pit, 0);
1690 setCursor(pit, pit->size());
1694 // fix the cursor `cur' after a characters has been deleted at `where'
1695 // position. Called by deleteEmptyParagraphMechanism
1696 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1698 // if cursor is not in the paragraph where the delete occured,
1700 if (cur.par() != where.par())
1703 // if cursor position is after the place where the delete occured,
1705 if (cur.pos() > where.pos())
1706 cur.pos(cur.pos()-1);
1708 // check also if we don't want to set the cursor on a spot behind the
1709 // pagragraph because we erased the last character.
1710 if (cur.pos() > getPar(cur)->size())
1711 cur.pos(getPar(cur)->size());
1713 // recompute row et al. for this cursor
1714 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1718 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1720 // Would be wrong to delete anything if we have a selection.
1721 if (selection.set())
1724 // Don't do anything if the cursor is invalid
1725 if (old_cursor.par() == -1)
1728 // We allow all kinds of "mumbo-jumbo" when freespacing.
1729 ParagraphList::iterator const old_pit = getPar(old_cursor);
1730 if (old_pit->isFreeSpacing())
1733 /* Ok I'll put some comments here about what is missing.
1734 I have fixed BackSpace (and thus Delete) to not delete
1735 double-spaces automagically. I have also changed Cut,
1736 Copy and Paste to hopefully do some sensible things.
1737 There are still some small problems that can lead to
1738 double spaces stored in the document file or space at
1739 the beginning of paragraphs. This happens if you have
1740 the cursor between to spaces and then save. Or if you
1741 cut and paste and the selection have a space at the
1742 beginning and then save right after the paste. I am
1743 sure none of these are very hard to fix, but I will
1744 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1745 that I can get some feedback. (Lgb)
1748 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1749 // delete the LineSeparator.
1752 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1753 // delete the LineSeparator.
1756 // If the pos around the old_cursor were spaces, delete one of them.
1757 if (old_cursor.par() != cursor.par()
1758 || old_cursor.pos() != cursor.pos()) {
1760 // Only if the cursor has really moved
1761 if (old_cursor.pos() > 0
1762 && old_cursor.pos() < old_pit->size()
1763 && old_pit->isLineSeparator(old_cursor.pos())
1764 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1765 bool erased = old_pit->erase(old_cursor.pos() - 1);
1766 redoParagraph(old_pit);
1770 #ifdef WITH_WARNINGS
1771 #warning This will not work anymore when we have multiple views of the same buffer
1772 // In this case, we will have to correct also the cursors held by
1773 // other bufferviews. It will probably be easier to do that in a more
1774 // automated way in LyXCursor code. (JMarc 26/09/2001)
1776 // correct all cursors held by the LyXText
1777 fixCursorAfterDelete(cursor, old_cursor);
1778 fixCursorAfterDelete(selection.cursor, old_cursor);
1779 fixCursorAfterDelete(selection.start, old_cursor);
1780 fixCursorAfterDelete(selection.end, old_cursor);
1785 // don't delete anything if this is the ONLY paragraph!
1786 if (ownerParagraphs().size() == 1)
1789 // Do not delete empty paragraphs with keepempty set.
1790 if (old_pit->allowEmpty())
1793 // only do our magic if we changed paragraph
1794 if (old_cursor.par() == cursor.par())
1797 // record if we have deleted a paragraph
1798 // we can't possibly have deleted a paragraph before this point
1799 bool deleted = false;
1801 if (old_pit->empty()
1802 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1803 // ok, we will delete something
1804 LyXCursor tmpcursor;
1808 bool selection_position_was_oldcursor_position =
1809 selection.cursor.par() == old_cursor.par()
1810 && selection.cursor.pos() == old_cursor.pos();
1813 cursor = old_cursor; // that undo can restore the right cursor position
1815 ParagraphList::iterator endpit = boost::next(old_pit);
1816 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1819 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1823 ParagraphList::iterator tmppit = cursorPar();
1825 ownerParagraphs().erase(old_pit);
1826 // update cursor par offset
1827 cursor.par(parOffset(tmppit));
1831 setCursorIntern(cursor.par(), cursor.pos());
1833 if (selection_position_was_oldcursor_position) {
1834 // correct selection
1835 selection.cursor = cursor;
1842 if (old_pit->stripLeadingSpaces()) {
1843 redoParagraph(old_pit);
1845 setCursorIntern(cursor.par(), cursor.pos());
1846 selection.cursor = cursor;
1852 ParagraphList & LyXText::ownerParagraphs() const
1854 return *paragraphs_;
1858 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1860 recordUndo(Undo::ATOMIC, this, first, last);
1864 void LyXText::recUndo(lyx::paroffset_type par) const
1866 recordUndo(Undo::ATOMIC, this, par, par);
1870 bool LyXText::isInInset() const
1872 // Sub-level has non-null bv owner and non-null inset owner.
1873 return inset_owner != 0;
1877 int defaultRowHeight()
1879 LyXFont const font(LyXFont::ALL_SANE);
1880 return int(font_metrics::maxHeight(font) * 1.2);