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 if (lyxlayout->margintype == MARGIN_MANUAL)
314 pit->setLabelWidthString(lyxlayout->labelstring());
315 cur.par(std::distance(ownerParagraphs().begin(), pit));
317 } while (pit != epit);
323 // set layout over selection and make a total rebreak of those paragraphs
324 void LyXText::setLayout(string const & layout)
328 // special handling of new environment insets
329 BufferParams const & params = bv()->buffer()->params();
330 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
331 if (lyxlayout->is_environment) {
332 // move everything in a new environment inset
333 lyxerr << "setting layout " << layout << endl;
334 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
335 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
336 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
337 InsetOld * inset = new InsetEnvironment(params, layout);
338 if (bv()->insertInset(inset)) {
340 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
346 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
347 selection.end, layout);
348 redoParagraphs(getPar(selection.start), endpit);
357 void getSelectionSpan(LyXText & text,
358 ParagraphList::iterator & beg,
359 ParagraphList::iterator & end)
361 if (!text.selection.set()) {
362 beg = text.cursorPar();
363 end = boost::next(beg);
365 beg = text.getPar(text.selection.start);
366 end = boost::next(text.getPar(text.selection.end));
371 bool changeDepthAllowed(bv_funcs::DEPTH_CHANGE type,
372 Paragraph const & par,
375 if (par.layout()->labeltype == LABEL_BIBLIO)
377 int const depth = par.params().depth();
378 if (type == bv_funcs::INC_DEPTH && depth < max_depth)
380 if (type == bv_funcs::DEC_DEPTH && depth > 0)
389 bool LyXText::changeDepthAllowed(bv_funcs::DEPTH_CHANGE type)
391 ParagraphList::iterator beg, end;
392 getSelectionSpan(*this, beg, end);
394 if (beg != ownerParagraphs().begin())
395 max_depth = boost::prior(beg)->getMaxDepthAfter();
397 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
398 if (::changeDepthAllowed(type, *pit, max_depth))
400 max_depth = pit->getMaxDepthAfter();
406 void LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type)
408 ParagraphList::iterator beg, end;
409 getSelectionSpan(*this, beg, end);
411 recUndo(parOffset(beg), parOffset(end) - 1);
414 if (beg != ownerParagraphs().begin())
415 max_depth = boost::prior(beg)->getMaxDepthAfter();
417 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
418 if (::changeDepthAllowed(type, *pit, max_depth)) {
419 int const depth = pit->params().depth();
420 if (type == bv_funcs::INC_DEPTH)
421 pit->params().depth(depth + 1);
423 pit->params().depth(depth - 1);
425 max_depth = pit->getMaxDepthAfter();
427 // this handles the counter labels, and also fixes up
428 // depth values for follow-on (child) paragraphs
434 // set font over selection and make a total rebreak of those paragraphs
435 void LyXText::setFont(LyXFont const & font, bool toggleall)
437 // if there is no selection just set the current_font
438 if (!selection.set()) {
439 // Determine basis font
441 if (cursor.pos() < cursorPar()->beginOfBody()) {
442 layoutfont = getLabelFont(cursorPar());
444 layoutfont = getLayoutFont(cursorPar());
446 // Update current font
447 real_current_font.update(font,
448 bv()->buffer()->params().language,
451 // Reduce to implicit settings
452 current_font = real_current_font;
453 current_font.reduce(layoutfont);
454 // And resolve it completely
455 real_current_font.realize(layoutfont);
460 // ok we have a selection.
461 recUndo(selection.start.par(), selection.end.par());
464 ParagraphList::iterator beg = getPar(selection.start.par());
465 ParagraphList::iterator end = getPar(selection.end.par());
467 PosIterator pos(&ownerParagraphs(), beg, selection.start.pos());
468 PosIterator posend(&ownerParagraphs(), end, selection.end.pos());
470 BufferParams const & params = bv()->buffer()->params();
472 for (; pos != posend; ++pos) {
473 LyXFont f = getFont(pos.pit(), pos.pos());
474 f.update(font, params.language, toggleall);
475 setCharFont(pos.pit(), pos.pos(), f);
480 redoParagraphs(beg, ++end);
485 // important for the screen
488 // the cursor set functions have a special mechanism. When they
489 // realize, that you left an empty paragraph, they will delete it.
491 // need the selection cursor:
492 void LyXText::setSelection()
494 TextCursor::setSelection();
498 void LyXText::clearSelection()
500 TextCursor::clearSelection();
502 // reset this in the bv()!
503 if (bv() && bv()->text())
504 bv()->text()->xsel_cache.set(false);
508 void LyXText::cursorHome()
510 ParagraphList::iterator cpit = cursorPar();
511 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
515 void LyXText::cursorEnd()
517 ParagraphList::iterator cpit = cursorPar();
518 pos_type end = cpit->getRow(cursor.pos())->endpos();
519 // if not on the last row of the par, put the cursor before
521 setCursor(cpit, end == cpit->size() ? end : end - 1);
525 void LyXText::cursorTop()
527 setCursor(ownerParagraphs().begin(), 0);
531 void LyXText::cursorBottom()
533 ParagraphList::iterator lastpit =
534 boost::prior(ownerParagraphs().end());
535 setCursor(lastpit, lastpit->size());
539 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
541 // If the mask is completely neutral, tell user
542 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
543 // Could only happen with user style
544 bv()->owner()->message(_("No font change defined. "
545 "Use Character under the Layout menu to define font change."));
549 // Try implicit word selection
550 // If there is a change in the language the implicit word selection
552 LyXCursor resetCursor = cursor;
553 bool implicitSelection =
554 font.language() == ignore_language
555 && font.number() == LyXFont::IGNORE
556 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
559 setFont(font, toggleall);
561 // Implicit selections are cleared afterwards
562 //and cursor is set to the original position.
563 if (implicitSelection) {
565 cursor = resetCursor;
566 setCursor(cursorPar(), cursor.pos());
567 selection.cursor = cursor;
572 string LyXText::getStringToIndex()
574 // Try implicit word selection
575 // If there is a change in the language the implicit word selection
577 LyXCursor const reset_cursor = cursor;
578 bool const implicitSelection =
579 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
582 if (!selection.set())
583 bv()->owner()->message(_("Nothing to index!"));
584 else if (selection.start.par() != selection.end.par())
585 bv()->owner()->message(_("Cannot index more than one paragraph!"));
587 idxstring = selectionAsString(*bv()->buffer(), false);
589 // Reset cursors to their original position.
590 cursor = reset_cursor;
591 setCursor(cursorPar(), cursor.pos());
592 selection.cursor = cursor;
594 // Clear the implicit selection.
595 if (implicitSelection)
602 // the DTP switches for paragraphs. LyX will store them in the first
603 // physical paragraph. When a paragraph is broken, the top settings rest,
604 // the bottom settings are given to the new one. So I can make sure,
605 // they do not duplicate themself and you cannot play dirty tricks with
608 void LyXText::setParagraph(
609 Spacing const & spacing,
611 string const & labelwidthstring,
615 // make sure that the depth behind the selection are restored, too
616 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
617 ParagraphList::iterator undoendpit = endpit;
618 ParagraphList::iterator pars_end = ownerParagraphs().end();
620 if (endpit != pars_end && endpit->getDepth()) {
621 while (endpit != pars_end && endpit->getDepth()) {
625 } else if (endpit != pars_end) {
626 // because of parindents etc.
630 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
632 int tmppit = selection.end.par();
634 while (tmppit != selection.start.par() - 1) {
635 setCursor(tmppit, 0);
637 ParagraphList::iterator const pit = cursorPar();
638 ParagraphParameters & params = pit->params();
639 params.spacing(spacing);
641 // does the layout allow the new alignment?
642 LyXLayout_ptr const & layout = pit->layout();
644 if (align == LYX_ALIGN_LAYOUT)
645 align = layout->align;
646 if (align & layout->alignpossible) {
647 if (align == layout->align)
648 params.align(LYX_ALIGN_LAYOUT);
652 pit->setLabelWidthString(labelwidthstring);
653 params.noindent(noindent);
657 redoParagraphs(getPar(selection.start), endpit);
662 string expandLabel(LyXTextClass const & textclass,
663 LyXLayout_ptr const & layout, bool appendix)
665 string fmt = appendix ?
666 layout->labelstring_appendix() : layout->labelstring();
668 // handle 'inherited level parts' in 'fmt',
669 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
670 size_t const i = fmt.find('@', 0);
671 if (i != string::npos) {
672 size_t const j = fmt.find('@', i + 1);
673 if (j != string::npos) {
674 string parent(fmt, i + 1, j - i - 1);
675 string label = expandLabel(textclass, textclass[parent], appendix);
676 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
680 return textclass.counters().counterLabel(fmt);
686 void incrementItemDepth(ParagraphList::iterator pit,
687 ParagraphList::iterator first_pit)
689 int const cur_labeltype = pit->layout()->labeltype;
691 if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
694 int const cur_depth = pit->getDepth();
696 ParagraphList::iterator prev_pit = boost::prior(pit);
698 int const prev_depth = prev_pit->getDepth();
699 int const prev_labeltype = prev_pit->layout()->labeltype;
700 if (prev_depth == 0 && cur_depth > 0) {
701 if (prev_labeltype == cur_labeltype) {
702 pit->itemdepth = prev_pit->itemdepth + 1;
705 } else if (prev_depth < cur_depth) {
706 if (prev_labeltype == cur_labeltype) {
707 pit->itemdepth = prev_pit->itemdepth + 1;
710 } else if (prev_depth == cur_depth) {
711 if (prev_labeltype == cur_labeltype) {
712 pit->itemdepth = prev_pit->itemdepth;
716 if (prev_pit == first_pit)
724 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
725 ParagraphList::iterator firstpit,
731 int const cur_depth = pit->getDepth();
732 ParagraphList::iterator prev_pit = boost::prior(pit);
734 int const prev_depth = prev_pit->getDepth();
735 int const prev_labeltype = prev_pit->layout()->labeltype;
736 if (prev_depth <= cur_depth) {
737 if (prev_labeltype != LABEL_ENUMERATE) {
738 switch (pit->itemdepth) {
740 counters.reset("enumi");
742 counters.reset("enumii");
744 counters.reset("enumiii");
746 counters.reset("enumiv");
752 if (prev_pit == firstpit)
762 // set the counter of a paragraph. This includes the labels
763 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
765 BufferParams const & bufparams = buf.params();
766 LyXTextClass const & textclass = bufparams.getLyXTextClass();
767 LyXLayout_ptr const & layout = pit->layout();
768 ParagraphList::iterator first_pit = ownerParagraphs().begin();
769 Counters & counters = textclass.counters();
774 if (pit == first_pit) {
775 pit->params().appendix(pit->params().startOfAppendix());
777 pit->params().appendix(boost::prior(pit)->params().appendix());
778 if (!pit->params().appendix() &&
779 pit->params().startOfAppendix()) {
780 pit->params().appendix(true);
781 textclass.counters().reset();
784 // Maybe we have to increment the item depth.
785 incrementItemDepth(pit, first_pit);
788 // erase what was there before
789 pit->params().labelString(string());
791 if (layout->margintype == MARGIN_MANUAL) {
792 if (pit->params().labelWidthString().empty())
793 pit->setLabelWidthString(layout->labelstring());
795 pit->setLabelWidthString(string());
798 // is it a layout that has an automatic label?
799 if (layout->labeltype == LABEL_COUNTER) {
800 BufferParams const & bufparams = buf.params();
801 LyXTextClass const & textclass = bufparams.getLyXTextClass();
802 counters.step(layout->counter);
803 string label = expandLabel(textclass, layout, pit->params().appendix());
804 pit->params().labelString(label);
805 } else if (layout->labeltype == LABEL_ITEMIZE) {
806 // At some point of time we should do something more
807 // clever here, like:
808 // pit->params().labelString(
809 // bufparams.user_defined_bullet(pit->itemdepth).getText());
810 // for now, use a simple hardcoded label
812 switch (pit->itemdepth) {
827 pit->params().labelString(itemlabel);
828 } else if (layout->labeltype == LABEL_ENUMERATE) {
829 // Maybe we have to reset the enumeration counter.
830 resetEnumCounterIfNeeded(pit, first_pit, counters);
833 // Yes I know this is a really, really! bad solution
835 string enumcounter = "enum";
837 switch (pit->itemdepth) {
849 // not a valid enumdepth...
853 counters.step(enumcounter);
855 pit->params().labelString(counters.enumLabel(enumcounter));
856 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
857 counters.step("bibitem");
858 int number = counters.value("bibitem");
859 if (pit->bibitem()) {
860 pit->bibitem()->setCounter(number);
861 pit->params().labelString(layout->labelstring());
863 // In biblio should't be following counters but...
865 string s = buf.B_(layout->labelstring());
868 if (layout->labeltype == LABEL_SENSITIVE) {
869 ParagraphList::iterator end = ownerParagraphs().end();
870 ParagraphList::iterator tmppit = pit;
873 while (tmppit != end && tmppit->inInset()
874 // the single '=' is intended below
875 && (in = tmppit->inInset()->owner()))
877 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
878 in->lyxCode() == InsetOld::WRAP_CODE) {
882 Paragraph const * owner = &ownerPar(buf, in);
884 for ( ; tmppit != end; ++tmppit)
885 if (&*tmppit == owner)
893 if (in->lyxCode() == InsetOld::FLOAT_CODE)
894 type = static_cast<InsetFloat*>(in)->params().type;
895 else if (in->lyxCode() == InsetOld::WRAP_CODE)
896 type = static_cast<InsetWrap*>(in)->params().type;
900 Floating const & fl = textclass.floats().getType(type);
902 counters.step(fl.type());
904 // Doesn't work... yet.
905 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
907 // par->SetLayout(0);
908 // s = layout->labelstring;
909 s = _("Senseless: ");
912 pit->params().labelString(s);
918 // Updates all counters.
919 void LyXText::updateCounters()
922 bv()->buffer()->params().getLyXTextClass().counters().reset();
924 bool update_pos = false;
926 ParagraphList::iterator beg = ownerParagraphs().begin();
927 ParagraphList::iterator end = ownerParagraphs().end();
928 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
929 string const oldLabel = pit->params().labelString();
932 maxdepth = boost::prior(pit)->getMaxDepthAfter();
934 if (pit->params().depth() > maxdepth)
935 pit->params().depth(maxdepth);
937 // setCounter can potentially change the labelString.
938 setCounter(*bv()->buffer(), pit);
939 string const & newLabel = pit->params().labelString();
940 if (oldLabel != newLabel) {
941 redoParagraphInternal(pit);
947 updateParPositions();
951 void LyXText::insertInset(InsetOld * inset)
953 if (!cursorPar()->insetAllowed(inset->lyxCode()))
956 recUndo(cursor.par());
958 cursorPar()->insertInset(cursor.pos(), inset);
959 // Just to rebreak and refresh correctly.
960 // The character will not be inserted a second time
961 insertChar(Paragraph::META_INSET);
962 // If we enter a highly editable inset the cursor should be before
963 // the inset. After an undo LyX tries to call inset->edit(...)
964 // and fails if the cursor is behind the inset and getInset
965 // does not return the inset!
966 if (isHighlyEditableInset(inset))
973 void LyXText::cutSelection(bool doclear, bool realcut)
975 // Stuff what we got on the clipboard. Even if there is no selection.
977 // There is a problem with having the stuffing here in that the
978 // larger the selection the slower LyX will get. This can be
979 // solved by running the line below only when the selection has
980 // finished. The solution used currently just works, to make it
981 // faster we need to be more clever and probably also have more
982 // calls to stuffClipboard. (Lgb)
983 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
985 // This doesn't make sense, if there is no selection
986 if (!selection.set())
989 // OK, we have a selection. This is always between selection.start
992 // make sure that the depth behind the selection are restored, too
993 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
994 ParagraphList::iterator undoendpit = endpit;
995 ParagraphList::iterator pars_end = ownerParagraphs().end();
997 if (endpit != pars_end && endpit->getDepth()) {
998 while (endpit != pars_end && endpit->getDepth()) {
1000 undoendpit = endpit;
1002 } else if (endpit != pars_end) {
1003 // because of parindents etc.
1007 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1009 endpit = getPar(selection.end.par());
1010 int endpos = selection.end.pos();
1012 BufferParams const & bufparams = bv()->buffer()->params();
1013 boost::tie(endpit, endpos) = realcut ?
1014 CutAndPaste::cutSelection(bufparams,
1016 getPar(selection.start.par()), endpit,
1017 selection.start.pos(), endpos,
1018 bufparams.textclass,
1020 : CutAndPaste::eraseSelection(bufparams,
1022 getPar(selection.start.par()), endpit,
1023 selection.start.pos(), endpos,
1025 // sometimes necessary
1027 getPar(selection.start.par())->stripLeadingSpaces();
1029 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1030 // cutSelection can invalidate the cursor so we need to set
1032 // we prefer the end for when tracking changes
1034 cursor.par(parOffset(endpit));
1036 // need a valid cursor. (Lgb)
1039 setCursor(cursorPar(), cursor.pos());
1040 selection.cursor = cursor;
1045 void LyXText::copySelection()
1047 // stuff the selection onto the X clipboard, from an explicit copy request
1048 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1050 // this doesnt make sense, if there is no selection
1051 if (!selection.set())
1054 // ok we have a selection. This is always between selection.start
1055 // and sel_end cursor
1057 // copy behind a space if there is one
1058 while (getPar(selection.start)->size() > selection.start.pos()
1059 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1060 && (selection.start.par() != selection.end.par()
1061 || selection.start.pos() < selection.end.pos()))
1062 selection.start.pos(selection.start.pos() + 1);
1064 CutAndPaste::copySelection(getPar(selection.start.par()),
1065 getPar(selection.end.par()),
1066 selection.start.pos(), selection.end.pos(),
1067 bv()->buffer()->params().textclass);
1071 void LyXText::pasteSelection(size_t sel_index)
1073 // this does not make sense, if there is nothing to paste
1074 if (!CutAndPaste::checkPastePossible())
1077 recUndo(cursor.par());
1079 ParagraphList::iterator endpit;
1084 boost::tie(ppp, endpit) =
1085 CutAndPaste::pasteSelection(*bv()->buffer(),
1087 cursorPar(), cursor.pos(),
1088 bv()->buffer()->params().textclass,
1090 bufferErrors(*bv()->buffer(), el);
1091 bv()->showErrorList(_("Paste"));
1093 redoParagraphs(cursorPar(), endpit);
1095 setCursor(cursor.par(), cursor.pos());
1098 selection.cursor = cursor;
1099 setCursor(ppp.first, ppp.second);
1105 void LyXText::setSelectionRange(lyx::pos_type length)
1110 selection.cursor = cursor;
1117 // simple replacing. The font of the first selected character is used
1118 void LyXText::replaceSelectionWithString(string const & str)
1120 recUndo(cursor.par());
1123 if (!selection.set()) { // create a dummy selection
1124 selection.end = cursor;
1125 selection.start = cursor;
1128 // Get font setting before we cut
1129 pos_type pos = selection.end.pos();
1130 LyXFont const font = getPar(selection.start)
1131 ->getFontSettings(bv()->buffer()->params(),
1132 selection.start.pos());
1134 // Insert the new string
1135 string::const_iterator cit = str.begin();
1136 string::const_iterator end = str.end();
1137 for (; cit != end; ++cit) {
1138 getPar(selection.end)->insertChar(pos, (*cit), font);
1142 // Cut the selection
1143 cutSelection(true, false);
1149 // needed to insert the selection
1150 void LyXText::insertStringAsLines(string const & str)
1152 ParagraphList::iterator pit = cursorPar();
1153 pos_type pos = cursor.pos();
1154 ParagraphList::iterator endpit = boost::next(cursorPar());
1156 recUndo(cursor.par());
1158 // only to be sure, should not be neccessary
1161 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1163 redoParagraphs(cursorPar(), endpit);
1164 setCursor(cursorPar(), cursor.pos());
1165 selection.cursor = cursor;
1166 setCursor(pit, pos);
1171 // turns double-CR to single CR, others where converted into one
1172 // blank. Then InsertStringAsLines is called
1173 void LyXText::insertStringAsParagraphs(string const & str)
1175 string linestr(str);
1176 bool newline_inserted = false;
1177 string::size_type const siz = linestr.length();
1179 for (string::size_type i = 0; i < siz; ++i) {
1180 if (linestr[i] == '\n') {
1181 if (newline_inserted) {
1182 // we know that \r will be ignored by
1183 // InsertStringA. Of course, it is a dirty
1184 // trick, but it works...
1185 linestr[i - 1] = '\r';
1189 newline_inserted = true;
1191 } else if (IsPrintable(linestr[i])) {
1192 newline_inserted = false;
1195 insertStringAsLines(linestr);
1199 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1201 setCursor(parOffset(pit), pos);
1205 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1207 LyXCursor old_cursor = cursor;
1208 setCursorIntern(par, pos, setfont, boundary);
1209 return deleteEmptyParagraphMechanism(old_cursor);
1213 void LyXText::redoCursor()
1215 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1217 if (!selection.set())
1220 LyXCursor tmpcursor = cursor;
1221 setCursor(selection.cursor.par(), selection.cursor.pos());
1222 selection.cursor = cursor;
1223 setCursor(tmpcursor.par(), tmpcursor.pos());
1228 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1229 pos_type pos, bool boundary)
1231 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1235 cur.boundary(boundary);
1237 // no rows, no fun...
1238 if (ownerParagraphs().begin()->rows.empty())
1241 // get the cursor y position in text
1243 ParagraphList::iterator pit = getPar(par);
1244 Row const & row = *pit->getRow(pos);
1246 int y = pit->y + row.y_offset();
1248 // y is now the beginning of the cursor row
1249 y += row.baseline();
1250 // y is now the cursor baseline
1253 pos_type const end = row.endpos();
1255 // None of these should happen, but we're scaredy-cats
1257 lyxerr << "dont like -1" << endl;
1260 BOOST_ASSERT(false);
1261 } else if (pos > pit->size()) {
1262 lyxerr << "dont like 1, pos: " << pos
1263 << " size: " << pit->size()
1264 << " row.pos():" << row.pos()
1265 << " paroffset: " << par << endl;
1268 BOOST_ASSERT(false);
1269 } else if (pos > end) {
1270 lyxerr << "dont like 2 please report" << endl;
1271 // This shouldn't happen.
1274 BOOST_ASSERT(false);
1275 } else if (pos < row.pos()) {
1276 lyxerr << "dont like 3 please report pos:" << pos
1277 << " size: " << pit->size()
1278 << " row.pos():" << row.pos()
1279 << " paroffset: " << par << endl;
1282 BOOST_ASSERT(false);
1284 // now get the cursors x position
1285 cur.x(int(getCursorX(pit, row, pos, boundary)));
1289 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1290 pos_type pos, bool boundary) const
1292 pos_type cursor_vpos = 0;
1294 double fill_separator = row.fill_separator();
1295 double fill_hfill = row.fill_hfill();
1296 double fill_label_hfill = row.fill_label_hfill();
1297 pos_type const row_pos = row.pos();
1298 pos_type const end = row.endpos();
1301 cursor_vpos = row_pos;
1302 else if (pos >= end && !boundary)
1303 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1305 else if (pos > row_pos && (pos >= end || boundary))
1306 // Place cursor after char at (logical) position pos - 1
1307 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1308 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1310 // Place cursor before char at (logical) position pos
1311 cursor_vpos = (bidi.level(pos) % 2 == 0)
1312 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1314 pos_type body_pos = pit->beginOfBody();
1316 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1319 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1320 pos_type pos = bidi.vis2log(vpos);
1321 if (body_pos > 0 && pos == body_pos - 1) {
1322 x += fill_label_hfill
1323 + font_metrics::width(pit->layout()->labelsep,
1325 if (pit->isLineSeparator(body_pos - 1))
1326 x -= singleWidth(pit, body_pos - 1);
1329 if (hfillExpansion(*pit, row, pos)) {
1330 x += singleWidth(pit, pos);
1331 if (pos >= body_pos)
1334 x += fill_label_hfill;
1335 } else if (pit->isSeparator(pos)) {
1336 x += singleWidth(pit, pos);
1337 if (pos >= body_pos)
1338 x += fill_separator;
1340 x += singleWidth(pit, pos);
1346 void LyXText::setCursorIntern(paroffset_type par,
1347 pos_type pos, bool setfont, bool boundary)
1349 setCursor(cursor, par, pos, boundary);
1350 bv()->x_target(cursor.x() + xo_);
1356 void LyXText::setCurrentFont()
1358 pos_type pos = cursor.pos();
1359 ParagraphList::iterator pit = cursorPar();
1361 if (cursor.boundary() && pos > 0)
1365 if (pos == pit->size())
1367 else // potentional bug... BUG (Lgb)
1368 if (pit->isSeparator(pos)) {
1369 if (pos > pit->getRow(pos)->pos() &&
1370 bidi.level(pos) % 2 ==
1371 bidi.level(pos - 1) % 2)
1373 else if (pos + 1 < pit->size())
1378 BufferParams const & bufparams = bv()->buffer()->params();
1379 current_font = pit->getFontSettings(bufparams, pos);
1380 real_current_font = getFont(pit, pos);
1382 if (cursor.pos() == pit->size() &&
1383 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1384 !cursor.boundary()) {
1385 Language const * lang =
1386 pit->getParLanguage(bufparams);
1387 current_font.setLanguage(lang);
1388 current_font.setNumber(LyXFont::OFF);
1389 real_current_font.setLanguage(lang);
1390 real_current_font.setNumber(LyXFont::OFF);
1395 // returns the column near the specified x-coordinate of the row
1396 // x is set to the real beginning of this column
1397 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1398 Row const & row, int & x, bool & boundary) const
1400 double tmpx = row.x();
1401 double fill_separator = row.fill_separator();
1402 double fill_hfill = row.fill_hfill();
1403 double fill_label_hfill = row.fill_label_hfill();
1405 pos_type vc = row.pos();
1406 pos_type end = row.endpos();
1408 LyXLayout_ptr const & layout = pit->layout();
1410 bool left_side = false;
1412 pos_type body_pos = pit->beginOfBody();
1413 double last_tmpx = tmpx;
1417 !pit->isLineSeparator(body_pos - 1)))
1420 // check for empty row
1426 while (vc < end && tmpx <= x) {
1427 c = bidi.vis2log(vc);
1429 if (body_pos > 0 && c == body_pos - 1) {
1430 tmpx += fill_label_hfill +
1431 font_metrics::width(layout->labelsep, getLabelFont(pit));
1432 if (pit->isLineSeparator(body_pos - 1))
1433 tmpx -= singleWidth(pit, body_pos - 1);
1436 if (hfillExpansion(*pit, row, c)) {
1437 tmpx += singleWidth(pit, c);
1441 tmpx += fill_label_hfill;
1442 } else if (pit->isSeparator(c)) {
1443 tmpx += singleWidth(pit, c);
1445 tmpx += fill_separator;
1447 tmpx += singleWidth(pit, c);
1452 if ((tmpx + last_tmpx) / 2 > x) {
1457 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1460 // This (rtl_support test) is not needed, but gives
1461 // some speedup if rtl_support == false
1462 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1464 // If lastrow is false, we don't need to compute
1465 // the value of rtl.
1466 bool const rtl = (lastrow)
1467 ? pit->isRightToLeftPar(bv()->buffer()->params())
1470 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1471 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1473 else if (vc == row.pos()) {
1474 c = bidi.vis2log(vc);
1475 if (bidi.level(c) % 2 == 1)
1478 c = bidi.vis2log(vc - 1);
1479 bool const rtl = (bidi.level(c) % 2 == 1);
1480 if (left_side == rtl) {
1482 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1486 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1487 if (bidi.level(end -1) % 2 == 0)
1488 tmpx -= singleWidth(pit, end - 1);
1490 tmpx += singleWidth(pit, end - 1);
1500 void LyXText::setCursorFromCoordinates(int x, int y)
1502 LyXCursor old_cursor = cursor;
1503 setCursorFromCoordinates(cursor, x, y);
1505 deleteEmptyParagraphMechanism(old_cursor);
1508 // x,y are coordinates relative to this LyXText
1509 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1511 // Get the row first.
1512 ParagraphList::iterator pit;
1513 Row const & row = *getRowNearY(y, pit);
1514 y = pit->y + row.y_offset();
1517 pos_type const column = getColumnNearX(pit, row, x, bound);
1518 cur.par(parOffset(pit));
1519 cur.pos(row.pos() + column);
1521 cur.y(y + row.baseline());
1523 cur.boundary(bound);
1527 bool LyXText::checkAndActivateInset(bool front)
1529 if (cursor.pos() == cursorPar()->size())
1531 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1532 if (!isHighlyEditableInset(inset))
1534 inset->edit(bv(), front);
1539 DispatchResult LyXText::moveRight()
1541 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1542 return moveLeftIntern(false, true, false);
1544 return moveRightIntern(true, true, false);
1548 DispatchResult LyXText::moveLeft()
1550 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1551 return moveRightIntern(true, true, false);
1553 return moveLeftIntern(false, true, false);
1557 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1559 ParagraphList::iterator c_par = cursorPar();
1560 if (boost::next(c_par) == ownerParagraphs().end()
1561 && cursor.pos() >= c_par->size())
1562 return DispatchResult(false, FINISHED_RIGHT);
1563 if (activate_inset && checkAndActivateInset(front))
1564 return DispatchResult(true, true);
1568 return DispatchResult(true);
1572 DispatchResult LyXText::moveLeftIntern(bool front,
1573 bool activate_inset, bool selecting)
1575 if (cursor.par() == 0 && cursor.pos() <= 0)
1576 return DispatchResult(false, FINISHED);
1580 if (activate_inset && checkAndActivateInset(front))
1581 return DispatchResult(true, true);
1582 return DispatchResult(true);
1586 DispatchResult LyXText::moveUp()
1588 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1589 return DispatchResult(false, FINISHED_UP);
1592 return DispatchResult(true);
1596 DispatchResult LyXText::moveDown()
1598 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1599 return DispatchResult(false, FINISHED_DOWN);
1602 return DispatchResult(true);
1606 bool LyXText::cursorLeft(bool internal)
1608 if (cursor.pos() > 0) {
1609 bool boundary = cursor.boundary();
1610 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1611 if (!internal && !boundary &&
1612 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1613 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1617 if (cursor.par() != 0) {
1618 // steps into the paragraph above
1619 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1627 bool LyXText::cursorRight(bool internal)
1629 if (!internal && cursor.boundary()) {
1630 setCursor(cursor.par(), cursor.pos(), true, false);
1634 if (cursor.pos() != cursorPar()->size()) {
1635 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1636 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1638 setCursor(cursor.par(), cursor.pos(), true, true);
1642 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1643 setCursor(cursor.par() + 1, 0);
1651 void LyXText::cursorUp(bool selecting)
1653 Row const & row = *cursorRow();
1654 int x = bv()->x_target() - xo_;
1655 int y = cursor.y() - row.baseline() - 1;
1656 setCursorFromCoordinates(x, y);
1659 int y_abs = y + yo_ - bv()->top_y();
1660 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1661 if (inset_hit && isHighlyEditableInset(inset_hit))
1662 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1667 void LyXText::cursorDown(bool selecting)
1669 Row const & row = *cursorRow();
1670 int x = bv()->x_target() - xo_;
1671 int y = cursor.y() - row.baseline() + row.height() + 1;
1672 setCursorFromCoordinates(x, y);
1675 int y_abs = y + yo_ - bv()->top_y();
1676 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1677 if (inset_hit && isHighlyEditableInset(inset_hit))
1678 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1683 void LyXText::cursorUpParagraph()
1685 ParagraphList::iterator cpit = cursorPar();
1686 if (cursor.pos() > 0)
1688 else if (cpit != ownerParagraphs().begin())
1689 setCursor(boost::prior(cpit), 0);
1693 void LyXText::cursorDownParagraph()
1695 ParagraphList::iterator pit = cursorPar();
1696 ParagraphList::iterator next_pit = boost::next(pit);
1698 if (next_pit != ownerParagraphs().end())
1699 setCursor(next_pit, 0);
1701 setCursor(pit, pit->size());
1705 // fix the cursor `cur' after a characters has been deleted at `where'
1706 // position. Called by deleteEmptyParagraphMechanism
1707 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1709 // if cursor is not in the paragraph where the delete occured,
1711 if (cur.par() != where.par())
1714 // if cursor position is after the place where the delete occured,
1716 if (cur.pos() > where.pos())
1717 cur.pos(cur.pos()-1);
1719 // check also if we don't want to set the cursor on a spot behind the
1720 // pagragraph because we erased the last character.
1721 if (cur.pos() > getPar(cur)->size())
1722 cur.pos(getPar(cur)->size());
1724 // recompute row et al. for this cursor
1725 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1729 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1731 // Would be wrong to delete anything if we have a selection.
1732 if (selection.set())
1735 // Don't do anything if the cursor is invalid
1736 if (old_cursor.par() == -1)
1739 // We allow all kinds of "mumbo-jumbo" when freespacing.
1740 ParagraphList::iterator const old_pit = getPar(old_cursor);
1741 if (old_pit->isFreeSpacing())
1744 /* Ok I'll put some comments here about what is missing.
1745 I have fixed BackSpace (and thus Delete) to not delete
1746 double-spaces automagically. I have also changed Cut,
1747 Copy and Paste to hopefully do some sensible things.
1748 There are still some small problems that can lead to
1749 double spaces stored in the document file or space at
1750 the beginning of paragraphs. This happens if you have
1751 the cursor between to spaces and then save. Or if you
1752 cut and paste and the selection have a space at the
1753 beginning and then save right after the paste. I am
1754 sure none of these are very hard to fix, but I will
1755 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1756 that I can get some feedback. (Lgb)
1759 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1760 // delete the LineSeparator.
1763 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1764 // delete the LineSeparator.
1767 // If the pos around the old_cursor were spaces, delete one of them.
1768 if (old_cursor.par() != cursor.par()
1769 || old_cursor.pos() != cursor.pos()) {
1771 // Only if the cursor has really moved
1772 if (old_cursor.pos() > 0
1773 && old_cursor.pos() < old_pit->size()
1774 && old_pit->isLineSeparator(old_cursor.pos())
1775 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1776 bool erased = old_pit->erase(old_cursor.pos() - 1);
1777 redoParagraph(old_pit);
1781 #ifdef WITH_WARNINGS
1782 #warning This will not work anymore when we have multiple views of the same buffer
1783 // In this case, we will have to correct also the cursors held by
1784 // other bufferviews. It will probably be easier to do that in a more
1785 // automated way in LyXCursor code. (JMarc 26/09/2001)
1787 // correct all cursors held by the LyXText
1788 fixCursorAfterDelete(cursor, old_cursor);
1789 fixCursorAfterDelete(selection.cursor, old_cursor);
1790 fixCursorAfterDelete(selection.start, old_cursor);
1791 fixCursorAfterDelete(selection.end, old_cursor);
1796 // don't delete anything if this is the ONLY paragraph!
1797 if (ownerParagraphs().size() == 1)
1800 // Do not delete empty paragraphs with keepempty set.
1801 if (old_pit->allowEmpty())
1804 // only do our magic if we changed paragraph
1805 if (old_cursor.par() == cursor.par())
1808 // record if we have deleted a paragraph
1809 // we can't possibly have deleted a paragraph before this point
1810 bool deleted = false;
1812 if (old_pit->empty()
1813 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1814 // ok, we will delete something
1815 LyXCursor tmpcursor;
1819 bool selection_position_was_oldcursor_position =
1820 selection.cursor.par() == old_cursor.par()
1821 && selection.cursor.pos() == old_cursor.pos();
1824 cursor = old_cursor; // that undo can restore the right cursor position
1826 ParagraphList::iterator endpit = boost::next(old_pit);
1827 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1830 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1834 ParagraphList::iterator tmppit = cursorPar();
1836 ownerParagraphs().erase(old_pit);
1837 // update cursor par offset
1838 cursor.par(parOffset(tmppit));
1842 setCursorIntern(cursor.par(), cursor.pos());
1844 if (selection_position_was_oldcursor_position) {
1845 // correct selection
1846 selection.cursor = cursor;
1853 if (old_pit->stripLeadingSpaces()) {
1854 redoParagraph(old_pit);
1856 setCursorIntern(cursor.par(), cursor.pos());
1857 selection.cursor = cursor;
1863 ParagraphList & LyXText::ownerParagraphs() const
1865 return *paragraphs_;
1869 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1871 recordUndo(Undo::ATOMIC, this, first, last);
1875 void LyXText::recUndo(lyx::paroffset_type par) const
1877 recordUndo(Undo::ATOMIC, this, par, par);
1881 bool LyXText::isInInset() const
1883 // Sub-level has non-null bv owner and non-null inset owner.
1884 return inset_owner != 0;
1888 int defaultRowHeight()
1890 LyXFont const font(LyXFont::ALL_SANE);
1891 return int(font_metrics::maxHeight(font) * 1.2);