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"
46 #include "PosIterator.h"
50 #include "frontends/font_metrics.h"
51 #include "frontends/LyXView.h"
53 #include "insets/insetbibitem.h"
54 #include "insets/insetenv.h"
55 #include "insets/insetfloat.h"
56 #include "insets/insetwrap.h"
58 #include "support/lstrings.h"
59 #include "support/textutils.h"
60 #include "support/tostr.h"
61 #include "support/std_sstream.h"
63 #include <boost/tuple/tuple.hpp>
66 using lyx::paroffset_type;
67 using lyx::support::bformat;
70 using std::ostringstream;
74 LyXText::LyXText(BufferView * bv, InsetText * inset, bool in_inset,
75 ParagraphList & paragraphs)
76 : height(0), width(0), textwidth_(bv ? bv->workWidth() : 100),
77 inset_owner(inset), bv_owner(bv),
78 in_inset_(in_inset), paragraphs_(¶graphs), xo_(0), yo_(0)
82 void LyXText::init(BufferView * bview)
86 ParagraphList::iterator const beg = ownerParagraphs().begin();
87 ParagraphList::iterator const end = ownerParagraphs().end();
88 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
94 current_font = getFont(beg, 0);
96 redoParagraphs(beg, end);
97 setCursorIntern(0, 0);
98 selection.cursor = cursor;
104 // Gets the fully instantiated font at a given position in a paragraph
105 // Basically the same routine as Paragraph::getFont() in paragraph.C.
106 // The difference is that this one is used for displaying, and thus we
107 // are allowed to make cosmetic improvements. For instance make footnotes
109 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
111 BOOST_ASSERT(pos >= 0);
113 LyXLayout_ptr const & layout = pit->layout();
115 BufferParams const & params = bv()->buffer()->params();
116 pos_type const body_pos = pit->beginOfBody();
118 // We specialize the 95% common case:
119 if (!pit->getDepth()) {
120 LyXFont f = pit->getFontSettings(params, pos);
122 pit->inInset()->getDrawFont(f);
123 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
124 return f.realize(layout->reslabelfont);
126 return f.realize(layout->resfont);
129 // The uncommon case need not be optimized as much
132 layoutfont = layout->labelfont;
134 layoutfont = layout->font;
136 LyXFont font = pit->getFontSettings(params, pos);
137 font.realize(layoutfont);
140 pit->inInset()->getDrawFont(font);
142 // Realize with the fonts of lesser depth.
143 //font.realize(outerFont(pit, ownerParagraphs()));
144 font.realize(defaultfont_);
150 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
152 LyXLayout_ptr const & layout = pit->layout();
154 if (!pit->getDepth())
155 return layout->resfont;
157 LyXFont font = layout->font;
158 // Realize with the fonts of lesser depth.
159 //font.realize(outerFont(pit, ownerParagraphs()));
160 font.realize(defaultfont_);
166 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
168 LyXLayout_ptr const & layout = pit->layout();
170 if (!pit->getDepth())
171 return layout->reslabelfont;
173 LyXFont font = layout->labelfont;
174 // Realize with the fonts of lesser depth.
175 font.realize(outerFont(pit, ownerParagraphs()));
176 font.realize(defaultfont_);
182 void LyXText::setCharFont(
183 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
186 LyXLayout_ptr const & layout = pit->layout();
188 // Get concrete layout font to reduce against
191 if (pos < pit->beginOfBody())
192 layoutfont = layout->labelfont;
194 layoutfont = layout->font;
196 // Realize against environment font information
197 if (pit->getDepth()) {
198 ParagraphList::iterator tp = pit;
199 while (!layoutfont.resolved() &&
200 tp != ownerParagraphs().end() &&
202 tp = outerHook(tp, ownerParagraphs());
203 if (tp != ownerParagraphs().end())
204 layoutfont.realize(tp->layout()->font);
208 layoutfont.realize(defaultfont_);
210 // Now, reduce font against full layout font
211 font.reduce(layoutfont);
213 pit->setFont(pos, font);
217 InsetOld * LyXText::getInset() const
219 ParagraphList::iterator pit = cursorPar();
220 pos_type const pos = cursor.pos();
222 if (pos < pit->size() && pit->isInset(pos)) {
223 return pit->getInset(pos);
229 void LyXText::toggleInset()
231 InsetOld * inset = getInset();
232 // is there an editable inset at cursor position?
233 if (!isEditableInset(inset)) {
234 // No, try to see if we are inside a collapsable inset
235 if (inset_owner && inset_owner->owner()
236 && inset_owner->owner()->isOpen()) {
238 inset_owner->owner()->close();
239 bv()->getLyXText()->cursorRight(true);
240 bv()->updateParagraphDialog();
244 //bv()->owner()->message(inset->editMessage());
246 // do we want to keep this?? (JMarc)
247 if (!isHighlyEditableInset(inset))
248 recUndo(cursor.par());
258 // Asger is not sure we want to do this...
259 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
262 LyXLayout_ptr const & layout = par.layout();
263 pos_type const psize = par.size();
266 for (pos_type pos = 0; pos < psize; ++pos) {
267 if (pos < par.beginOfBody())
268 layoutfont = layout->labelfont;
270 layoutfont = layout->font;
272 LyXFont tmpfont = par.getFontSettings(params, pos);
273 tmpfont.reduce(layoutfont);
274 par.setFont(pos, tmpfont);
279 ParagraphList::iterator
280 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
281 LyXCursor & send_cur,
282 string const & layout)
284 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
285 ParagraphList::iterator undoendpit = endpit;
286 ParagraphList::iterator pars_end = ownerParagraphs().end();
288 if (endpit != pars_end && endpit->getDepth()) {
289 while (endpit != pars_end && endpit->getDepth()) {
293 } else if (endpit != pars_end) {
294 // because of parindents etc.
298 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
300 // ok we have a selection. This is always between sstart_cur
301 // and sel_end cursor
303 ParagraphList::iterator pit = getPar(sstart_cur);
304 ParagraphList::iterator epit = boost::next(getPar(send_cur));
306 BufferParams const & bufparams = bv()->buffer()->params();
307 LyXLayout_ptr const & lyxlayout =
308 bufparams.getLyXTextClass()[layout];
311 pit->applyLayout(lyxlayout);
312 makeFontEntriesLayoutSpecific(bufparams, *pit);
313 pit->params().spaceTop(lyxlayout->fill_top ?
314 VSpace(VSpace::VFILL)
315 : VSpace(VSpace::NONE));
316 pit->params().spaceBottom(lyxlayout->fill_bottom ?
317 VSpace(VSpace::VFILL)
318 : VSpace(VSpace::NONE));
319 if (lyxlayout->margintype == MARGIN_MANUAL)
320 pit->setLabelWidthString(lyxlayout->labelstring());
321 cur.par(std::distance(ownerParagraphs().begin(), pit));
323 } while (pit != epit);
329 // set layout over selection and make a total rebreak of those paragraphs
330 void LyXText::setLayout(string const & layout)
334 // special handling of new environment insets
335 BufferParams const & params = bv()->buffer()->params();
336 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
337 if (lyxlayout->is_environment) {
338 // move everything in a new environment inset
339 lyxerr << "setting layout " << layout << endl;
340 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
341 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
342 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
343 InsetOld * inset = new InsetEnvironment(params, layout);
344 if (bv()->insertInset(inset)) {
346 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
352 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
353 selection.end, layout);
354 redoParagraphs(getPar(selection.start), endpit);
363 void getSelectionSpan(LyXText & text,
364 ParagraphList::iterator & beg,
365 ParagraphList::iterator & end)
367 if (!text.selection.set()) {
368 beg = text.cursorPar();
369 end = boost::next(beg);
371 beg = text.getPar(text.selection.start);
372 end = boost::next(text.getPar(text.selection.end));
377 bool changeDepthAllowed(bv_funcs::DEPTH_CHANGE type,
378 Paragraph const & par,
381 if (par.layout()->labeltype == LABEL_BIBLIO)
383 int const depth = par.params().depth();
384 if (type == bv_funcs::INC_DEPTH && depth < max_depth)
386 if (type == bv_funcs::DEC_DEPTH && depth > 0)
395 bool LyXText::changeDepthAllowed(bv_funcs::DEPTH_CHANGE type)
397 ParagraphList::iterator beg, end;
398 getSelectionSpan(*this, beg, end);
400 if (beg != ownerParagraphs().begin())
401 max_depth = boost::prior(beg)->getMaxDepthAfter();
403 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
404 if (::changeDepthAllowed(type, *pit, max_depth))
406 max_depth = pit->getMaxDepthAfter();
412 void LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type)
414 ParagraphList::iterator beg, end;
415 getSelectionSpan(*this, beg, end);
417 recUndo(parOffset(beg), parOffset(end) - 1);
420 if (beg != ownerParagraphs().begin())
421 max_depth = boost::prior(beg)->getMaxDepthAfter();
423 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
424 if (::changeDepthAllowed(type, *pit, max_depth)) {
425 int const depth = pit->params().depth();
426 if (type == bv_funcs::INC_DEPTH)
427 pit->params().depth(depth + 1);
429 pit->params().depth(depth - 1);
431 max_depth = pit->getMaxDepthAfter();
433 // this handles the counter labels, and also fixes up
434 // depth values for follow-on (child) paragraphs
440 // set font over selection and make a total rebreak of those paragraphs
441 void LyXText::setFont(LyXFont const & font, bool toggleall)
443 // if there is no selection just set the current_font
444 if (!selection.set()) {
445 // Determine basis font
447 if (cursor.pos() < cursorPar()->beginOfBody()) {
448 layoutfont = getLabelFont(cursorPar());
450 layoutfont = getLayoutFont(cursorPar());
452 // Update current font
453 real_current_font.update(font,
454 bv()->buffer()->params().language,
457 // Reduce to implicit settings
458 current_font = real_current_font;
459 current_font.reduce(layoutfont);
460 // And resolve it completely
461 real_current_font.realize(layoutfont);
466 // ok we have a selection.
467 recUndo(selection.start.par(), selection.end.par());
470 ParagraphList::iterator beg = getPar(selection.start.par());
471 ParagraphList::iterator end = getPar(selection.end.par());
473 PosIterator pos(&ownerParagraphs(), beg, selection.start.pos());
474 PosIterator posend(&ownerParagraphs(), end, selection.end.pos());
476 BufferParams const & params = bv()->buffer()->params();
478 for (; pos != posend; ++pos) {
479 LyXFont f = getFont(pos.pit(), pos.pos());
480 f.update(font, params.language, toggleall);
481 setCharFont(pos.pit(), pos.pos(), f);
486 redoParagraphs(beg, ++end);
491 // important for the screen
494 // the cursor set functions have a special mechanism. When they
495 // realize, that you left an empty paragraph, they will delete it.
497 // need the selection cursor:
498 void LyXText::setSelection()
500 TextCursor::setSelection();
504 void LyXText::clearSelection()
506 TextCursor::clearSelection();
508 // reset this in the bv()!
509 if (bv() && bv()->text)
510 bv()->text->xsel_cache.set(false);
514 void LyXText::cursorHome()
516 ParagraphList::iterator cpit = cursorPar();
517 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
521 void LyXText::cursorEnd()
523 ParagraphList::iterator cpit = cursorPar();
524 pos_type end = cpit->getRow(cursor.pos())->endpos();
525 // if not on the last row of the par, put the cursor before
527 setCursor(cpit, end == cpit->size() ? end : end - 1);
531 void LyXText::cursorTop()
533 setCursor(ownerParagraphs().begin(), 0);
537 void LyXText::cursorBottom()
539 ParagraphList::iterator lastpit =
540 boost::prior(ownerParagraphs().end());
541 setCursor(lastpit, lastpit->size());
545 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
547 // If the mask is completely neutral, tell user
548 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
549 // Could only happen with user style
550 bv()->owner()->message(_("No font change defined. "
551 "Use Character under the Layout menu to define font change."));
555 // Try implicit word selection
556 // If there is a change in the language the implicit word selection
558 LyXCursor resetCursor = cursor;
559 bool implicitSelection =
560 font.language() == ignore_language
561 && font.number() == LyXFont::IGNORE
562 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
565 setFont(font, toggleall);
567 // Implicit selections are cleared afterwards
568 //and cursor is set to the original position.
569 if (implicitSelection) {
571 cursor = resetCursor;
572 setCursor(cursorPar(), cursor.pos());
573 selection.cursor = cursor;
578 string LyXText::getStringToIndex()
580 // Try implicit word selection
581 // If there is a change in the language the implicit word selection
583 LyXCursor const reset_cursor = cursor;
584 bool const implicitSelection =
585 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
588 if (!selection.set())
589 bv()->owner()->message(_("Nothing to index!"));
590 else if (selection.start.par() != selection.end.par())
591 bv()->owner()->message(_("Cannot index more than one paragraph!"));
593 idxstring = selectionAsString(*bv()->buffer(), false);
595 // Reset cursors to their original position.
596 cursor = reset_cursor;
597 setCursor(cursorPar(), cursor.pos());
598 selection.cursor = cursor;
600 // Clear the implicit selection.
601 if (implicitSelection)
608 // the DTP switches for paragraphs. LyX will store them in the first
609 // physical paragraph. When a paragraph is broken, the top settings rest,
610 // the bottom settings are given to the new one. So I can make sure,
611 // they do not duplicate themself and you cannot play dirty tricks with
614 void LyXText::setParagraph(VSpace const & space_top,
615 VSpace const & space_bottom,
616 Spacing const & spacing,
618 string const & labelwidthstring,
622 // make sure that the depth behind the selection are restored, too
623 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
624 ParagraphList::iterator undoendpit = endpit;
625 ParagraphList::iterator pars_end = ownerParagraphs().end();
627 if (endpit != pars_end && endpit->getDepth()) {
628 while (endpit != pars_end && endpit->getDepth()) {
632 } else if (endpit != pars_end) {
633 // because of parindents etc.
637 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
639 int tmppit = selection.end.par();
641 while (tmppit != selection.start.par() - 1) {
642 setCursor(tmppit, 0);
644 ParagraphList::iterator const pit = cursorPar();
645 ParagraphParameters & params = pit->params();
646 params.spaceTop(space_top);
647 params.spaceBottom(space_bottom);
648 params.spacing(spacing);
650 // does the layout allow the new alignment?
651 LyXLayout_ptr const & layout = pit->layout();
653 if (align == LYX_ALIGN_LAYOUT)
654 align = layout->align;
655 if (align & layout->alignpossible) {
656 if (align == layout->align)
657 params.align(LYX_ALIGN_LAYOUT);
661 pit->setLabelWidthString(labelwidthstring);
662 params.noindent(noindent);
666 redoParagraphs(getPar(selection.start), endpit);
671 string expandLabel(LyXTextClass const & textclass,
672 LyXLayout_ptr const & layout, bool appendix)
674 string fmt = appendix ?
675 layout->labelstring_appendix() : layout->labelstring();
677 // handle 'inherited level parts' in 'fmt',
678 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
679 size_t const i = fmt.find('@', 0);
680 if (i != string::npos) {
681 size_t const j = fmt.find('@', i + 1);
682 if (j != string::npos) {
683 string parent(fmt, i + 1, j - i - 1);
684 string label = expandLabel(textclass, textclass[parent], appendix);
685 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
689 return textclass.counters().counterLabel(fmt);
695 void incrementItemDepth(ParagraphList::iterator pit,
696 ParagraphList::iterator first_pit)
698 int const cur_labeltype = pit->layout()->labeltype;
700 if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
703 int const cur_depth = pit->getDepth();
705 ParagraphList::iterator prev_pit = boost::prior(pit);
707 int const prev_depth = prev_pit->getDepth();
708 int const prev_labeltype = prev_pit->layout()->labeltype;
709 if (prev_depth == 0 && cur_depth > 0) {
710 if (prev_labeltype == cur_labeltype) {
711 pit->itemdepth = prev_pit->itemdepth + 1;
714 } else if (prev_depth < cur_depth) {
715 if (prev_labeltype == cur_labeltype) {
716 pit->itemdepth = prev_pit->itemdepth + 1;
719 } else if (prev_depth == cur_depth) {
720 if (prev_labeltype == cur_labeltype) {
721 pit->itemdepth = prev_pit->itemdepth;
725 if (prev_pit == first_pit)
733 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
734 ParagraphList::iterator firstpit,
740 int const cur_depth = pit->getDepth();
741 ParagraphList::iterator prev_pit = boost::prior(pit);
743 int const prev_depth = prev_pit->getDepth();
744 int const prev_labeltype = prev_pit->layout()->labeltype;
745 if (prev_depth <= cur_depth) {
746 if (prev_labeltype != LABEL_ENUMERATE) {
747 switch (pit->itemdepth) {
749 counters.reset("enumi");
751 counters.reset("enumii");
753 counters.reset("enumiii");
755 counters.reset("enumiv");
761 if (prev_pit == firstpit)
771 // set the counter of a paragraph. This includes the labels
772 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
774 BufferParams const & bufparams = buf.params();
775 LyXTextClass const & textclass = bufparams.getLyXTextClass();
776 LyXLayout_ptr const & layout = pit->layout();
777 ParagraphList::iterator first_pit = ownerParagraphs().begin();
778 Counters & counters = textclass.counters();
783 if (pit == first_pit) {
784 pit->params().appendix(pit->params().startOfAppendix());
786 pit->params().appendix(boost::prior(pit)->params().appendix());
787 if (!pit->params().appendix() &&
788 pit->params().startOfAppendix()) {
789 pit->params().appendix(true);
790 textclass.counters().reset();
793 // Maybe we have to increment the item depth.
794 incrementItemDepth(pit, first_pit);
797 // erase what was there before
798 pit->params().labelString(string());
800 if (layout->margintype == MARGIN_MANUAL) {
801 if (pit->params().labelWidthString().empty())
802 pit->setLabelWidthString(layout->labelstring());
804 pit->setLabelWidthString(string());
807 // is it a layout that has an automatic label?
808 if (layout->labeltype == LABEL_COUNTER) {
809 BufferParams const & bufparams = buf.params();
810 LyXTextClass const & textclass = bufparams.getLyXTextClass();
811 counters.step(layout->counter);
812 string label = expandLabel(textclass, layout, pit->params().appendix());
813 pit->params().labelString(label);
814 } else if (layout->labeltype == LABEL_ITEMIZE) {
815 // At some point of time we should do something more
816 // clever here, like:
817 // pit->params().labelString(
818 // bufparams.user_defined_bullet(pit->itemdepth).getText());
819 // for now, use a simple hardcoded label
821 switch (pit->itemdepth) {
836 pit->params().labelString(itemlabel);
837 } else if (layout->labeltype == LABEL_ENUMERATE) {
838 // Maybe we have to reset the enumeration counter.
839 resetEnumCounterIfNeeded(pit, first_pit, counters);
842 // Yes I know this is a really, really! bad solution
844 string enumcounter = "enum";
846 switch (pit->itemdepth) {
858 // not a valid enumdepth...
862 counters.step(enumcounter);
864 pit->params().labelString(counters.enumLabel(enumcounter));
865 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
866 counters.step("bibitem");
867 int number = counters.value("bibitem");
868 if (pit->bibitem()) {
869 pit->bibitem()->setCounter(number);
870 pit->params().labelString(layout->labelstring());
872 // In biblio should't be following counters but...
874 string s = buf.B_(layout->labelstring());
877 if (layout->labeltype == LABEL_SENSITIVE) {
878 ParagraphList::iterator end = ownerParagraphs().end();
879 ParagraphList::iterator tmppit = pit;
882 while (tmppit != end && tmppit->inInset()
883 // the single '=' is intended below
884 && (in = tmppit->inInset()->owner()))
886 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
887 in->lyxCode() == InsetOld::WRAP_CODE) {
891 Paragraph const * owner = &ownerPar(buf, in);
893 for ( ; tmppit != end; ++tmppit)
894 if (&*tmppit == owner)
902 if (in->lyxCode() == InsetOld::FLOAT_CODE)
903 type = static_cast<InsetFloat*>(in)->params().type;
904 else if (in->lyxCode() == InsetOld::WRAP_CODE)
905 type = static_cast<InsetWrap*>(in)->params().type;
909 Floating const & fl = textclass.floats().getType(type);
911 counters.step(fl.type());
913 // Doesn't work... yet.
914 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
916 // par->SetLayout(0);
917 // s = layout->labelstring;
918 s = _("Senseless: ");
921 pit->params().labelString(s);
927 // Updates all counters.
928 void LyXText::updateCounters()
931 bv()->buffer()->params().getLyXTextClass().counters().reset();
933 bool update_pos = false;
935 ParagraphList::iterator beg = ownerParagraphs().begin();
936 ParagraphList::iterator end = ownerParagraphs().end();
937 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
938 string const oldLabel = pit->params().labelString();
941 maxdepth = boost::prior(pit)->getMaxDepthAfter();
943 if (pit->params().depth() > maxdepth)
944 pit->params().depth(maxdepth);
946 // setCounter can potentially change the labelString.
947 setCounter(*bv()->buffer(), pit);
948 string const & newLabel = pit->params().labelString();
949 if (oldLabel != newLabel) {
950 redoParagraphInternal(pit);
956 updateParPositions();
960 void LyXText::insertInset(InsetOld * inset)
962 if (!cursorPar()->insetAllowed(inset->lyxCode()))
965 recUndo(cursor.par());
967 cursorPar()->insertInset(cursor.pos(), inset);
968 // Just to rebreak and refresh correctly.
969 // The character will not be inserted a second time
970 insertChar(Paragraph::META_INSET);
971 // If we enter a highly editable inset the cursor should be before
972 // the inset. After an undo LyX tries to call inset->edit(...)
973 // and fails if the cursor is behind the inset and getInset
974 // does not return the inset!
975 if (isHighlyEditableInset(inset))
982 void LyXText::cutSelection(bool doclear, bool realcut)
984 // Stuff what we got on the clipboard. Even if there is no selection.
986 // There is a problem with having the stuffing here in that the
987 // larger the selection the slower LyX will get. This can be
988 // solved by running the line below only when the selection has
989 // finished. The solution used currently just works, to make it
990 // faster we need to be more clever and probably also have more
991 // calls to stuffClipboard. (Lgb)
992 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
994 // This doesn't make sense, if there is no selection
995 if (!selection.set())
998 // OK, we have a selection. This is always between selection.start
1001 // make sure that the depth behind the selection are restored, too
1002 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1003 ParagraphList::iterator undoendpit = endpit;
1004 ParagraphList::iterator pars_end = ownerParagraphs().end();
1006 if (endpit != pars_end && endpit->getDepth()) {
1007 while (endpit != pars_end && endpit->getDepth()) {
1009 undoendpit = endpit;
1011 } else if (endpit != pars_end) {
1012 // because of parindents etc.
1016 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1018 endpit = getPar(selection.end.par());
1019 int endpos = selection.end.pos();
1021 BufferParams const & bufparams = bv()->buffer()->params();
1022 boost::tie(endpit, endpos) = realcut ?
1023 CutAndPaste::cutSelection(bufparams,
1025 getPar(selection.start.par()), endpit,
1026 selection.start.pos(), endpos,
1027 bufparams.textclass,
1029 : CutAndPaste::eraseSelection(bufparams,
1031 getPar(selection.start.par()), endpit,
1032 selection.start.pos(), endpos,
1034 // sometimes necessary
1036 getPar(selection.start.par())->stripLeadingSpaces();
1038 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1039 // cutSelection can invalidate the cursor so we need to set
1041 // we prefer the end for when tracking changes
1043 cursor.par(parOffset(endpit));
1045 // need a valid cursor. (Lgb)
1048 setCursor(cursorPar(), cursor.pos());
1049 selection.cursor = cursor;
1054 void LyXText::copySelection()
1056 // stuff the selection onto the X clipboard, from an explicit copy request
1057 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1059 // this doesnt make sense, if there is no selection
1060 if (!selection.set())
1063 // ok we have a selection. This is always between selection.start
1064 // and sel_end cursor
1066 // copy behind a space if there is one
1067 while (getPar(selection.start)->size() > selection.start.pos()
1068 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1069 && (selection.start.par() != selection.end.par()
1070 || selection.start.pos() < selection.end.pos()))
1071 selection.start.pos(selection.start.pos() + 1);
1073 CutAndPaste::copySelection(getPar(selection.start.par()),
1074 getPar(selection.end.par()),
1075 selection.start.pos(), selection.end.pos(),
1076 bv()->buffer()->params().textclass);
1080 void LyXText::pasteSelection(size_t sel_index)
1082 // this does not make sense, if there is nothing to paste
1083 if (!CutAndPaste::checkPastePossible())
1086 recUndo(cursor.par());
1088 ParagraphList::iterator endpit;
1093 boost::tie(ppp, endpit) =
1094 CutAndPaste::pasteSelection(*bv()->buffer(),
1096 cursorPar(), cursor.pos(),
1097 bv()->buffer()->params().textclass,
1099 bufferErrors(*bv()->buffer(), el);
1100 bv()->showErrorList(_("Paste"));
1102 redoParagraphs(cursorPar(), endpit);
1104 setCursor(cursor.par(), cursor.pos());
1107 selection.cursor = cursor;
1108 setCursor(ppp.first, ppp.second);
1114 void LyXText::setSelectionRange(lyx::pos_type length)
1119 selection.cursor = cursor;
1126 // simple replacing. The font of the first selected character is used
1127 void LyXText::replaceSelectionWithString(string const & str)
1129 recUndo(cursor.par());
1132 if (!selection.set()) { // create a dummy selection
1133 selection.end = cursor;
1134 selection.start = cursor;
1137 // Get font setting before we cut
1138 pos_type pos = selection.end.pos();
1139 LyXFont const font = getPar(selection.start)
1140 ->getFontSettings(bv()->buffer()->params(),
1141 selection.start.pos());
1143 // Insert the new string
1144 string::const_iterator cit = str.begin();
1145 string::const_iterator end = str.end();
1146 for (; cit != end; ++cit) {
1147 getPar(selection.end)->insertChar(pos, (*cit), font);
1151 // Cut the selection
1152 cutSelection(true, false);
1158 // needed to insert the selection
1159 void LyXText::insertStringAsLines(string const & str)
1161 ParagraphList::iterator pit = cursorPar();
1162 pos_type pos = cursor.pos();
1163 ParagraphList::iterator endpit = boost::next(cursorPar());
1165 recUndo(cursor.par());
1167 // only to be sure, should not be neccessary
1170 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1172 redoParagraphs(cursorPar(), endpit);
1173 setCursor(cursorPar(), cursor.pos());
1174 selection.cursor = cursor;
1175 setCursor(pit, pos);
1180 // turns double-CR to single CR, others where converted into one
1181 // blank. Then InsertStringAsLines is called
1182 void LyXText::insertStringAsParagraphs(string const & str)
1184 string linestr(str);
1185 bool newline_inserted = false;
1186 string::size_type const siz = linestr.length();
1188 for (string::size_type i = 0; i < siz; ++i) {
1189 if (linestr[i] == '\n') {
1190 if (newline_inserted) {
1191 // we know that \r will be ignored by
1192 // InsertStringA. Of course, it is a dirty
1193 // trick, but it works...
1194 linestr[i - 1] = '\r';
1198 newline_inserted = true;
1200 } else if (IsPrintable(linestr[i])) {
1201 newline_inserted = false;
1204 insertStringAsLines(linestr);
1208 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1210 setCursor(parOffset(pit), pos);
1214 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1216 LyXCursor old_cursor = cursor;
1217 setCursorIntern(par, pos, setfont, boundary);
1218 return deleteEmptyParagraphMechanism(old_cursor);
1222 void LyXText::redoCursor()
1224 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1226 if (!selection.set())
1229 LyXCursor tmpcursor = cursor;
1230 setCursor(selection.cursor.par(), selection.cursor.pos());
1231 selection.cursor = cursor;
1232 setCursor(tmpcursor.par(), tmpcursor.pos());
1237 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1238 pos_type pos, bool boundary)
1240 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1244 cur.boundary(boundary);
1246 // no rows, no fun...
1247 if (ownerParagraphs().begin()->rows.empty())
1250 // get the cursor y position in text
1252 ParagraphList::iterator pit = getPar(par);
1253 Row const & row = *pit->getRow(pos);
1255 int y = pit->y + row.y_offset();
1257 // y is now the beginning of the cursor row
1258 y += row.baseline();
1259 // y is now the cursor baseline
1262 pos_type const end = row.endpos();
1264 // None of these should happen, but we're scaredy-cats
1266 lyxerr << "dont like -1" << endl;
1269 BOOST_ASSERT(false);
1270 } else if (pos > pit->size()) {
1271 lyxerr << "dont like 1, pos: " << pos
1272 << " size: " << pit->size()
1273 << " row.pos():" << row.pos()
1274 << " paroffset: " << par << endl;
1277 BOOST_ASSERT(false);
1278 } else if (pos > end) {
1279 lyxerr << "dont like 2 please report" << endl;
1280 // This shouldn't happen.
1283 BOOST_ASSERT(false);
1284 } else if (pos < row.pos()) {
1285 lyxerr << "dont like 3 please report pos:" << pos
1286 << " size: " << pit->size()
1287 << " row.pos():" << row.pos()
1288 << " paroffset: " << par << endl;
1291 BOOST_ASSERT(false);
1293 // now get the cursors x position
1294 cur.x(int(getCursorX(pit, row, pos, boundary)));
1298 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1299 pos_type pos, bool boundary) const
1301 pos_type cursor_vpos = 0;
1303 double fill_separator = row.fill_separator();
1304 double fill_hfill = row.fill_hfill();
1305 double fill_label_hfill = row.fill_label_hfill();
1306 pos_type const row_pos = row.pos();
1307 pos_type const end = row.endpos();
1310 cursor_vpos = row_pos;
1311 else if (pos >= end && !boundary)
1312 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1314 else if (pos > row_pos && (pos >= end || boundary))
1315 // Place cursor after char at (logical) position pos - 1
1316 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1317 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1319 // Place cursor before char at (logical) position pos
1320 cursor_vpos = (bidi.level(pos) % 2 == 0)
1321 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1323 pos_type body_pos = pit->beginOfBody();
1325 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1328 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1329 pos_type pos = bidi.vis2log(vpos);
1330 if (body_pos > 0 && pos == body_pos - 1) {
1331 x += fill_label_hfill
1332 + font_metrics::width(pit->layout()->labelsep,
1334 if (pit->isLineSeparator(body_pos - 1))
1335 x -= singleWidth(pit, body_pos - 1);
1338 if (hfillExpansion(*pit, row, pos)) {
1339 x += singleWidth(pit, pos);
1340 if (pos >= body_pos)
1343 x += fill_label_hfill;
1344 } else if (pit->isSeparator(pos)) {
1345 x += singleWidth(pit, pos);
1346 if (pos >= body_pos)
1347 x += fill_separator;
1349 x += singleWidth(pit, pos);
1355 void LyXText::setCursorIntern(paroffset_type par,
1356 pos_type pos, bool setfont, bool boundary)
1358 setCursor(cursor, par, pos, boundary);
1359 bv()->x_target(cursor.x() + xo_);
1365 void LyXText::setCurrentFont()
1367 pos_type pos = cursor.pos();
1368 ParagraphList::iterator pit = cursorPar();
1370 if (cursor.boundary() && pos > 0)
1374 if (pos == pit->size())
1376 else // potentional bug... BUG (Lgb)
1377 if (pit->isSeparator(pos)) {
1378 if (pos > pit->getRow(pos)->pos() &&
1379 bidi.level(pos) % 2 ==
1380 bidi.level(pos - 1) % 2)
1382 else if (pos + 1 < pit->size())
1387 BufferParams const & bufparams = bv()->buffer()->params();
1388 current_font = pit->getFontSettings(bufparams, pos);
1389 real_current_font = getFont(pit, pos);
1391 if (cursor.pos() == pit->size() &&
1392 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1393 !cursor.boundary()) {
1394 Language const * lang =
1395 pit->getParLanguage(bufparams);
1396 current_font.setLanguage(lang);
1397 current_font.setNumber(LyXFont::OFF);
1398 real_current_font.setLanguage(lang);
1399 real_current_font.setNumber(LyXFont::OFF);
1404 // returns the column near the specified x-coordinate of the row
1405 // x is set to the real beginning of this column
1406 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1407 Row const & row, int & x, bool & boundary) const
1409 double tmpx = row.x();
1410 double fill_separator = row.fill_separator();
1411 double fill_hfill = row.fill_hfill();
1412 double fill_label_hfill = row.fill_label_hfill();
1414 pos_type vc = row.pos();
1415 pos_type end = row.endpos();
1417 LyXLayout_ptr const & layout = pit->layout();
1419 bool left_side = false;
1421 pos_type body_pos = pit->beginOfBody();
1422 double last_tmpx = tmpx;
1426 !pit->isLineSeparator(body_pos - 1)))
1429 // check for empty row
1435 while (vc < end && tmpx <= x) {
1436 c = bidi.vis2log(vc);
1438 if (body_pos > 0 && c == body_pos - 1) {
1439 tmpx += fill_label_hfill +
1440 font_metrics::width(layout->labelsep, getLabelFont(pit));
1441 if (pit->isLineSeparator(body_pos - 1))
1442 tmpx -= singleWidth(pit, body_pos - 1);
1445 if (hfillExpansion(*pit, row, c)) {
1446 tmpx += singleWidth(pit, c);
1450 tmpx += fill_label_hfill;
1451 } else if (pit->isSeparator(c)) {
1452 tmpx += singleWidth(pit, c);
1454 tmpx += fill_separator;
1456 tmpx += singleWidth(pit, c);
1461 if ((tmpx + last_tmpx) / 2 > x) {
1466 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1469 // This (rtl_support test) is not needed, but gives
1470 // some speedup if rtl_support == false
1471 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1473 // If lastrow is false, we don't need to compute
1474 // the value of rtl.
1475 bool const rtl = (lastrow)
1476 ? pit->isRightToLeftPar(bv()->buffer()->params())
1479 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1480 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1482 else if (vc == row.pos()) {
1483 c = bidi.vis2log(vc);
1484 if (bidi.level(c) % 2 == 1)
1487 c = bidi.vis2log(vc - 1);
1488 bool const rtl = (bidi.level(c) % 2 == 1);
1489 if (left_side == rtl) {
1491 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1495 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1496 if (bidi.level(end -1) % 2 == 0)
1497 tmpx -= singleWidth(pit, end - 1);
1499 tmpx += singleWidth(pit, end - 1);
1509 void LyXText::setCursorFromCoordinates(int x, int y)
1511 LyXCursor old_cursor = cursor;
1512 setCursorFromCoordinates(cursor, x, y);
1514 deleteEmptyParagraphMechanism(old_cursor);
1517 // x,y are coordinates relative to this LyXText
1518 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1520 // Get the row first.
1521 ParagraphList::iterator pit;
1522 Row const & row = *getRowNearY(y, pit);
1523 y = pit->y + row.y_offset();
1526 pos_type const column = getColumnNearX(pit, row, x, bound);
1527 cur.par(parOffset(pit));
1528 cur.pos(row.pos() + column);
1530 cur.y(y + row.baseline());
1532 cur.boundary(bound);
1536 bool LyXText::checkAndActivateInset(bool front)
1538 if (cursor.pos() == cursorPar()->size())
1540 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1541 if (!isHighlyEditableInset(inset))
1543 inset->edit(bv(), front);
1548 DispatchResult LyXText::moveRight()
1550 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1551 return moveLeftIntern(false, true, false);
1553 return moveRightIntern(true, true, false);
1557 DispatchResult LyXText::moveLeft()
1559 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1560 return moveRightIntern(true, true, false);
1562 return moveLeftIntern(false, true, false);
1566 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1568 ParagraphList::iterator c_par = cursorPar();
1569 if (boost::next(c_par) == ownerParagraphs().end()
1570 && cursor.pos() >= c_par->size())
1571 return DispatchResult(false, FINISHED_RIGHT);
1572 if (activate_inset && checkAndActivateInset(front))
1573 return DispatchResult(true, true);
1577 return DispatchResult(true);
1581 DispatchResult LyXText::moveLeftIntern(bool front,
1582 bool activate_inset, bool selecting)
1584 if (cursor.par() == 0 && cursor.pos() <= 0)
1585 return DispatchResult(false, FINISHED);
1589 if (activate_inset && checkAndActivateInset(front))
1590 return DispatchResult(true, true);
1591 return DispatchResult(true);
1595 DispatchResult LyXText::moveUp()
1597 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1598 return DispatchResult(false, FINISHED_UP);
1601 return DispatchResult(true);
1605 DispatchResult LyXText::moveDown()
1607 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1608 return DispatchResult(false, FINISHED_DOWN);
1611 return DispatchResult(true);
1615 bool LyXText::cursorLeft(bool internal)
1617 if (cursor.pos() > 0) {
1618 bool boundary = cursor.boundary();
1619 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1620 if (!internal && !boundary &&
1621 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1622 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1626 if (cursor.par() != 0) {
1627 // steps into the paragraph above
1628 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1636 bool LyXText::cursorRight(bool internal)
1638 if (!internal && cursor.boundary()) {
1639 setCursor(cursor.par(), cursor.pos(), true, false);
1643 if (cursor.pos() != cursorPar()->size()) {
1644 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1645 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1647 setCursor(cursor.par(), cursor.pos(), true, true);
1651 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1652 setCursor(cursor.par() + 1, 0);
1660 void LyXText::cursorUp(bool selecting)
1662 Row const & row = *cursorRow();
1663 int x = bv()->x_target() - xo_;
1664 int y = cursor.y() - row.baseline() - 1;
1665 setCursorFromCoordinates(x, y);
1668 int y_abs = y + yo_ - bv()->top_y();
1669 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1670 if (inset_hit && isHighlyEditableInset(inset_hit))
1671 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1676 void LyXText::cursorDown(bool selecting)
1678 Row const & row = *cursorRow();
1679 int x = bv()->x_target() - xo_;
1680 int y = cursor.y() - row.baseline() + row.height() + 1;
1681 setCursorFromCoordinates(x, y);
1684 int y_abs = y + yo_ - bv()->top_y();
1685 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1686 if (inset_hit && isHighlyEditableInset(inset_hit))
1687 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1692 void LyXText::cursorUpParagraph()
1694 ParagraphList::iterator cpit = cursorPar();
1695 if (cursor.pos() > 0)
1697 else if (cpit != ownerParagraphs().begin())
1698 setCursor(boost::prior(cpit), 0);
1702 void LyXText::cursorDownParagraph()
1704 ParagraphList::iterator pit = cursorPar();
1705 ParagraphList::iterator next_pit = boost::next(pit);
1707 if (next_pit != ownerParagraphs().end())
1708 setCursor(next_pit, 0);
1710 setCursor(pit, pit->size());
1714 // fix the cursor `cur' after a characters has been deleted at `where'
1715 // position. Called by deleteEmptyParagraphMechanism
1716 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1718 // if cursor is not in the paragraph where the delete occured,
1720 if (cur.par() != where.par())
1723 // if cursor position is after the place where the delete occured,
1725 if (cur.pos() > where.pos())
1726 cur.pos(cur.pos()-1);
1728 // check also if we don't want to set the cursor on a spot behind the
1729 // pagragraph because we erased the last character.
1730 if (cur.pos() > getPar(cur)->size())
1731 cur.pos(getPar(cur)->size());
1733 // recompute row et al. for this cursor
1734 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1738 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1740 // Would be wrong to delete anything if we have a selection.
1741 if (selection.set())
1744 // Don't do anything if the cursor is invalid
1745 if (old_cursor.par() == -1)
1748 // We allow all kinds of "mumbo-jumbo" when freespacing.
1749 ParagraphList::iterator const old_pit = getPar(old_cursor);
1750 if (old_pit->isFreeSpacing())
1753 /* Ok I'll put some comments here about what is missing.
1754 I have fixed BackSpace (and thus Delete) to not delete
1755 double-spaces automagically. I have also changed Cut,
1756 Copy and Paste to hopefully do some sensible things.
1757 There are still some small problems that can lead to
1758 double spaces stored in the document file or space at
1759 the beginning of paragraphs. This happens if you have
1760 the cursor between to spaces and then save. Or if you
1761 cut and paste and the selection have a space at the
1762 beginning and then save right after the paste. I am
1763 sure none of these are very hard to fix, but I will
1764 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1765 that I can get some feedback. (Lgb)
1768 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1769 // delete the LineSeparator.
1772 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1773 // delete the LineSeparator.
1776 // If the pos around the old_cursor were spaces, delete one of them.
1777 if (old_cursor.par() != cursor.par()
1778 || old_cursor.pos() != cursor.pos()) {
1780 // Only if the cursor has really moved
1781 if (old_cursor.pos() > 0
1782 && old_cursor.pos() < old_pit->size()
1783 && old_pit->isLineSeparator(old_cursor.pos())
1784 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1785 bool erased = old_pit->erase(old_cursor.pos() - 1);
1786 redoParagraph(old_pit);
1790 #ifdef WITH_WARNINGS
1791 #warning This will not work anymore when we have multiple views of the same buffer
1792 // In this case, we will have to correct also the cursors held by
1793 // other bufferviews. It will probably be easier to do that in a more
1794 // automated way in LyXCursor code. (JMarc 26/09/2001)
1796 // correct all cursors held by the LyXText
1797 fixCursorAfterDelete(cursor, old_cursor);
1798 fixCursorAfterDelete(selection.cursor, old_cursor);
1799 fixCursorAfterDelete(selection.start, old_cursor);
1800 fixCursorAfterDelete(selection.end, old_cursor);
1805 // don't delete anything if this is the ONLY paragraph!
1806 if (ownerParagraphs().size() == 1)
1809 // Do not delete empty paragraphs with keepempty set.
1810 if (old_pit->allowEmpty())
1813 // only do our magic if we changed paragraph
1814 if (old_cursor.par() == cursor.par())
1817 // record if we have deleted a paragraph
1818 // we can't possibly have deleted a paragraph before this point
1819 bool deleted = false;
1821 if (old_pit->empty()
1822 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1823 // ok, we will delete something
1824 LyXCursor tmpcursor;
1828 bool selection_position_was_oldcursor_position =
1829 selection.cursor.par() == old_cursor.par()
1830 && selection.cursor.pos() == old_cursor.pos();
1833 cursor = old_cursor; // that undo can restore the right cursor position
1835 ParagraphList::iterator endpit = boost::next(old_pit);
1836 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1839 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1843 ParagraphList::iterator tmppit = cursorPar();
1845 ownerParagraphs().erase(old_pit);
1846 // update cursor par offset
1847 cursor.par(parOffset(tmppit));
1851 setCursorIntern(cursor.par(), cursor.pos());
1853 if (selection_position_was_oldcursor_position) {
1854 // correct selection
1855 selection.cursor = cursor;
1862 if (old_pit->stripLeadingSpaces()) {
1863 redoParagraph(old_pit);
1865 setCursorIntern(cursor.par(), cursor.pos());
1866 selection.cursor = cursor;
1872 ParagraphList & LyXText::ownerParagraphs() const
1874 return *paragraphs_;
1878 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1880 recordUndo(Undo::ATOMIC, this, first, last);
1884 void LyXText::recUndo(lyx::paroffset_type par) const
1886 recordUndo(Undo::ATOMIC, this, par, par);
1890 bool LyXText::isInInset() const
1892 // Sub-level has non-null bv owner and non-null inset owner.
1893 return inset_owner != 0;
1897 int defaultRowHeight()
1899 LyXFont const font(LyXFont::ALL_SANE);
1900 return int(font_metrics::maxHeight(font) * 1.2);