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"
30 #include "CutAndPaste.h"
32 #include "errorlist.h"
34 #include "FloatList.h"
35 #include "funcrequest.h"
40 #include "lyxrow_funcs.h"
41 #include "paragraph.h"
42 #include "paragraph_funcs.h"
43 #include "ParagraphParameters.h"
47 #include "frontends/font_metrics.h"
48 #include "frontends/LyXView.h"
50 #include "insets/insetbibitem.h"
51 #include "insets/insetenv.h"
52 #include "insets/insetfloat.h"
53 #include "insets/insetwrap.h"
55 #include "support/lstrings.h"
56 #include "support/textutils.h"
57 #include "support/tostr.h"
58 #include "support/std_sstream.h"
60 #include <boost/tuple/tuple.hpp>
63 using lyx::paroffset_type;
64 using lyx::support::bformat;
67 using std::ostringstream;
71 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
72 ParagraphList & paragraphs)
73 : height(0), width(0), anchor_y_(0),
74 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
75 in_inset_(ininset), paragraphs_(¶graphs)
80 void LyXText::init(BufferView * bview)
84 ParagraphList::iterator const beg = ownerParagraphs().begin();
85 ParagraphList::iterator const end = ownerParagraphs().end();
86 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->beginningOfBody();
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(ParagraphList::iterator pit,
183 pos_type pos, LyXFont const & fnt,
186 BufferParams const & params = bv()->buffer()->params();
187 LyXFont font = getFont(pit, pos);
188 font.update(fnt, params.language, toggleall);
189 // Let the insets convert their font
190 if (pit->isInset(pos)) {
191 InsetOld * inset = pit->getInset(pos);
192 if (isEditableInset(inset)) {
193 static_cast<UpdatableInset *>(inset)
194 ->setFont(bv(), fnt, toggleall, true);
198 // Plug through to version below:
199 setCharFont(pit, pos, font);
203 void LyXText::setCharFont(
204 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
207 LyXLayout_ptr const & layout = pit->layout();
209 // Get concrete layout font to reduce against
212 if (pos < pit->beginningOfBody())
213 layoutfont = layout->labelfont;
215 layoutfont = layout->font;
217 // Realize against environment font information
218 if (pit->getDepth()) {
219 ParagraphList::iterator tp = pit;
220 while (!layoutfont.resolved() &&
221 tp != ownerParagraphs().end() &&
223 tp = outerHook(tp, ownerParagraphs());
224 if (tp != ownerParagraphs().end())
225 layoutfont.realize(tp->layout()->font);
229 layoutfont.realize(defaultfont_);
231 // Now, reduce font against full layout font
232 font.reduce(layoutfont);
234 pit->setFont(pos, font);
238 InsetOld * LyXText::getInset() const
240 ParagraphList::iterator pit = cursorPar();
241 pos_type const pos = cursor.pos();
243 if (pos < pit->size() && pit->isInset(pos)) {
244 return pit->getInset(pos);
250 void LyXText::toggleInset()
252 InsetOld * inset = getInset();
253 // is there an editable inset at cursor position?
254 if (!isEditableInset(inset)) {
255 // No, try to see if we are inside a collapsable inset
256 if (inset_owner && inset_owner->owner()
257 && inset_owner->owner()->isOpen()) {
258 bv()->unlockInset(inset_owner->owner());
259 inset_owner->owner()->close(bv());
260 bv()->getLyXText()->cursorRight(bv());
264 //bv()->owner()->message(inset->editMessage());
266 // do we want to keep this?? (JMarc)
267 if (!isHighlyEditableInset(inset))
268 recUndo(cursor.par());
275 bv()->updateInset(inset);
279 /* used in setlayout */
280 // Asger is not sure we want to do this...
281 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
284 LyXLayout_ptr const & layout = par.layout();
285 pos_type const psize = par.size();
288 for (pos_type pos = 0; pos < psize; ++pos) {
289 if (pos < par.beginningOfBody())
290 layoutfont = layout->labelfont;
292 layoutfont = layout->font;
294 LyXFont tmpfont = par.getFontSettings(params, pos);
295 tmpfont.reduce(layoutfont);
296 par.setFont(pos, tmpfont);
301 ParagraphList::iterator
302 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
303 LyXCursor & send_cur,
304 string const & layout)
306 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
307 ParagraphList::iterator undoendpit = endpit;
308 ParagraphList::iterator pars_end = ownerParagraphs().end();
310 if (endpit != pars_end && endpit->getDepth()) {
311 while (endpit != pars_end && endpit->getDepth()) {
315 } else if (endpit != pars_end) {
316 // because of parindents etc.
320 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
322 // ok we have a selection. This is always between sstart_cur
323 // and sel_end cursor
325 ParagraphList::iterator pit = getPar(sstart_cur);
326 ParagraphList::iterator epit = boost::next(getPar(send_cur));
328 BufferParams const & bufparams = bv()->buffer()->params();
329 LyXLayout_ptr const & lyxlayout =
330 bufparams.getLyXTextClass()[layout];
333 pit->applyLayout(lyxlayout);
334 makeFontEntriesLayoutSpecific(bufparams, *pit);
335 pit->params().spaceTop(lyxlayout->fill_top ?
336 VSpace(VSpace::VFILL)
337 : VSpace(VSpace::NONE));
338 pit->params().spaceBottom(lyxlayout->fill_bottom ?
339 VSpace(VSpace::VFILL)
340 : VSpace(VSpace::NONE));
341 if (lyxlayout->margintype == MARGIN_MANUAL)
342 pit->setLabelWidthString(lyxlayout->labelstring());
343 cur.par(std::distance(ownerParagraphs().begin(), pit));
345 } while (pit != epit);
351 // set layout over selection and make a total rebreak of those paragraphs
352 void LyXText::setLayout(string const & layout)
354 LyXCursor tmpcursor = cursor; // store the current cursor
356 // if there is no selection just set the layout
357 // of the current paragraph
358 if (!selection.set()) {
359 selection.start = cursor; // dummy selection
360 selection.end = cursor;
363 // special handling of new environment insets
364 BufferParams const & params = bv()->buffer()->params();
365 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
366 if (lyxlayout->is_environment) {
367 // move everything in a new environment inset
368 lyxerr << "setting layout " << layout << endl;
369 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
370 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
371 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
372 InsetOld * inset = new InsetEnvironment(params, layout);
373 if (bv()->insertInset(inset)) {
375 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
382 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
383 selection.end, layout);
384 redoParagraphs(getPar(selection.start), endpit);
386 // we have to reset the selection, because the
387 // geometry could have changed
388 setCursor(selection.start.par(), selection.start.pos(), false);
389 selection.cursor = cursor;
390 setCursor(selection.end.par(), selection.end.pos(), false);
394 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
398 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
400 ParagraphList::iterator pit = cursorPar();
401 ParagraphList::iterator end = cursorPar();
402 ParagraphList::iterator start = pit;
404 if (selection.set()) {
405 pit = getPar(selection.start);
406 end = getPar(selection.end);
410 ParagraphList::iterator pastend = boost::next(end);
413 recUndo(parOffset(start), parOffset(end));
415 bool changed = false;
417 int prev_after_depth = 0;
418 #warning parlist ... could be nicer ?
419 if (start != ownerParagraphs().begin()) {
420 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
424 int const depth = pit->params().depth();
425 if (type == bv_funcs::INC_DEPTH) {
426 if (depth < prev_after_depth
427 && pit->layout()->labeltype != LABEL_BIBLIO) {
430 pit->params().depth(depth + 1);
435 pit->params().depth(depth - 1);
438 prev_after_depth = pit->getMaxDepthAfter();
450 redoParagraphs(start, pastend);
452 // We need to actually move the text->cursor. I don't
453 // understand why ...
454 LyXCursor tmpcursor = cursor;
456 // we have to reset the visual selection because the
457 // geometry could have changed
458 if (selection.set()) {
459 setCursor(selection.start.par(), selection.start.pos());
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos());
464 // this handles the counter labels, and also fixes up
465 // depth values for follow-on (child) paragraphs
469 setCursor(tmpcursor.par(), tmpcursor.pos());
475 // set font over selection and make a total rebreak of those paragraphs
476 void LyXText::setFont(LyXFont const & font, bool toggleall)
478 // if there is no selection just set the current_font
479 if (!selection.set()) {
480 // Determine basis font
482 if (cursor.pos() < cursorPar()->beginningOfBody()) {
483 layoutfont = getLabelFont(cursorPar());
485 layoutfont = getLayoutFont(cursorPar());
487 // Update current font
488 real_current_font.update(font,
489 bv()->buffer()->params().language,
492 // Reduce to implicit settings
493 current_font = real_current_font;
494 current_font.reduce(layoutfont);
495 // And resolve it completely
496 real_current_font.realize(layoutfont);
501 LyXCursor tmpcursor = cursor; // store the current cursor
503 // ok we have a selection. This is always between sel_start_cursor
504 // and sel_end cursor
506 recUndo(selection.start.par(), selection.end.par());
508 cursor = selection.start;
509 while (cursor.par() != selection.end.par() ||
510 cursor.pos() < selection.end.pos())
512 if (cursor.pos() < cursorPar()->size()) {
513 // an open footnote should behave like a closed one
514 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
515 cursor.pos(cursor.pos() + 1);
518 cursor.par(cursor.par() + 1);
523 redoParagraph(getPar(selection.start));
525 // we have to reset the selection, because the
526 // geometry could have changed, but we keep
527 // it for user convenience
528 setCursor(selection.start.par(), selection.start.pos());
529 selection.cursor = cursor;
530 setCursor(selection.end.par(), selection.end.pos());
532 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
533 tmpcursor.boundary());
537 // important for the screen
540 // the cursor set functions have a special mechanism. When they
541 // realize, that you left an empty paragraph, they will delete it.
543 // need the selection cursor:
544 void LyXText::setSelection()
546 TextCursor::setSelection();
551 void LyXText::clearSelection()
553 TextCursor::clearSelection();
555 // reset this in the bv_owner!
556 if (bv_owner && bv_owner->text)
557 bv_owner->text->xsel_cache.set(false);
561 void LyXText::cursorHome()
563 ParagraphList::iterator cpit = cursorPar();
564 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
568 void LyXText::cursorEnd()
570 ParagraphList::iterator cpit = cursorPar();
571 setCursor(cpit, cpit->getRow(cursor.pos())->endpos() - 1);
575 void LyXText::cursorTop()
577 setCursor(ownerParagraphs().begin(), 0);
581 void LyXText::cursorBottom()
583 ParagraphList::iterator lastpit =
584 boost::prior(ownerParagraphs().end());
585 setCursor(lastpit, lastpit->size());
589 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
591 // If the mask is completely neutral, tell user
592 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
593 // Could only happen with user style
594 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
598 // Try implicit word selection
599 // If there is a change in the language the implicit word selection
601 LyXCursor resetCursor = cursor;
602 bool implicitSelection = (font.language() == ignore_language
603 && font.number() == LyXFont::IGNORE)
604 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
607 setFont(font, toggleall);
609 // Implicit selections are cleared afterwards
610 //and cursor is set to the original position.
611 if (implicitSelection) {
613 cursor = resetCursor;
614 setCursor(cursorPar(), cursor.pos());
615 selection.cursor = cursor;
620 string LyXText::getStringToIndex()
622 // Try implicit word selection
623 // If there is a change in the language the implicit word selection
625 LyXCursor const reset_cursor = cursor;
626 bool const implicitSelection =
627 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
630 if (!selection.set())
631 bv()->owner()->message(_("Nothing to index!"));
632 else if (selection.start.par() != selection.end.par())
633 bv()->owner()->message(_("Cannot index more than one paragraph!"));
635 idxstring = selectionAsString(*bv()->buffer(), false);
637 // Reset cursors to their original position.
638 cursor = reset_cursor;
639 setCursor(cursorPar(), cursor.pos());
640 selection.cursor = cursor;
642 // Clear the implicit selection.
643 if (implicitSelection)
650 // the DTP switches for paragraphs. LyX will store them in the first
651 // physical paragraph. When a paragraph is broken, the top settings rest,
652 // the bottom settings are given to the new one. So I can make sure,
653 // they do not duplicate themself and you cannnot make dirty things with
656 void LyXText::setParagraph(bool line_top, bool line_bottom,
657 bool pagebreak_top, bool pagebreak_bottom,
658 VSpace const & space_top,
659 VSpace const & space_bottom,
660 Spacing const & spacing,
662 string const & labelwidthstring,
665 LyXCursor tmpcursor = cursor;
666 if (!selection.set()) {
667 selection.start = cursor;
668 selection.end = cursor;
671 // make sure that the depth behind the selection are restored, too
672 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
673 ParagraphList::iterator undoendpit = endpit;
674 ParagraphList::iterator pars_end = ownerParagraphs().end();
676 if (endpit != pars_end && endpit->getDepth()) {
677 while (endpit != pars_end && endpit->getDepth()) {
681 } else if (endpit != pars_end) {
682 // because of parindents etc.
686 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
689 int tmppit = selection.end.par();
691 while (tmppit != selection.start.par() - 1) {
692 setCursor(tmppit, 0);
694 ParagraphList::iterator const pit = cursorPar();
695 ParagraphParameters & params = pit->params();
697 params.lineTop(line_top);
698 params.lineBottom(line_bottom);
699 params.pagebreakTop(pagebreak_top);
700 params.pagebreakBottom(pagebreak_bottom);
701 params.spaceTop(space_top);
702 params.spaceBottom(space_bottom);
703 params.spacing(spacing);
704 // does the layout allow the new alignment?
705 LyXLayout_ptr const & layout = pit->layout();
707 if (align == LYX_ALIGN_LAYOUT)
708 align = layout->align;
709 if (align & layout->alignpossible) {
710 if (align == layout->align)
711 params.align(LYX_ALIGN_LAYOUT);
715 pit->setLabelWidthString(labelwidthstring);
716 params.noindent(noindent);
720 redoParagraphs(getPar(selection.start), endpit);
723 setCursor(selection.start.par(), selection.start.pos());
724 selection.cursor = cursor;
725 setCursor(selection.end.par(), selection.end.pos());
727 setCursor(tmpcursor.par(), tmpcursor.pos());
729 bv()->updateInset(inset_owner);
735 string expandLabel(LyXTextClass const & textclass,
736 LyXLayout_ptr const & layout, bool appendix)
738 string fmt = appendix ?
739 layout->labelstring_appendix() : layout->labelstring();
741 // handle 'inherited level parts' in 'fmt',
742 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
743 size_t const i = fmt.find('@', 0);
744 if (i != string::npos) {
745 size_t const j = fmt.find('@', i + 1);
746 if (j != string::npos) {
747 string parent(fmt, i + 1, j - i - 1);
748 string label = expandLabel(textclass, textclass[parent], appendix);
749 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
753 return textclass.counters().counterLabel(fmt);
757 void incrementItemDepth(ParagraphList::iterator pit,
758 ParagraphList::iterator first_pit)
760 int const cur_labeltype = pit->layout()->labeltype;
762 if (cur_labeltype != LABEL_ENUMERATE &&
763 cur_labeltype != LABEL_ITEMIZE)
766 int const cur_depth = pit->getDepth();
768 ParagraphList::iterator prev_pit = boost::prior(pit);
770 int const prev_depth = prev_pit->getDepth();
771 int const prev_labeltype = prev_pit->layout()->labeltype;
772 if (prev_depth == 0 && cur_depth > 0) {
773 if (prev_labeltype == cur_labeltype) {
774 pit->itemdepth = prev_pit->itemdepth + 1;
777 } else if (prev_depth < cur_depth) {
778 if (prev_labeltype == cur_labeltype) {
779 pit->itemdepth = prev_pit->itemdepth + 1;
782 } else if (prev_depth == cur_depth) {
783 if (prev_labeltype == cur_labeltype) {
784 pit->itemdepth = prev_pit->itemdepth;
788 if (prev_pit == first_pit)
796 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
797 ParagraphList::iterator firstpit,
803 int const cur_depth = pit->getDepth();
804 ParagraphList::iterator prev_pit = boost::prior(pit);
806 int const prev_depth = prev_pit->getDepth();
807 int const prev_labeltype = prev_pit->layout()->labeltype;
808 if (prev_depth <= cur_depth) {
809 if (prev_labeltype != LABEL_ENUMERATE) {
810 switch (pit->itemdepth) {
812 counters.reset("enumi");
814 counters.reset("enumii");
816 counters.reset("enumiii");
818 counters.reset("enumiv");
824 if (prev_pit == firstpit)
834 // set the counter of a paragraph. This includes the labels
835 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
837 BufferParams const & bufparams = buf.params();
838 LyXTextClass const & textclass = bufparams.getLyXTextClass();
839 LyXLayout_ptr const & layout = pit->layout();
840 ParagraphList::iterator first_pit = ownerParagraphs().begin();
841 Counters & counters = textclass.counters();
846 if (pit == first_pit) {
847 pit->params().appendix(pit->params().startOfAppendix());
849 pit->params().appendix(boost::prior(pit)->params().appendix());
850 if (!pit->params().appendix() &&
851 pit->params().startOfAppendix()) {
852 pit->params().appendix(true);
853 textclass.counters().reset();
856 // Maybe we have to increment the item depth.
857 incrementItemDepth(pit, first_pit);
860 // erase what was there before
861 pit->params().labelString(string());
863 if (layout->margintype == MARGIN_MANUAL) {
864 if (pit->params().labelWidthString().empty())
865 pit->setLabelWidthString(layout->labelstring());
867 pit->setLabelWidthString(string());
870 // is it a layout that has an automatic label?
871 if (layout->labeltype == LABEL_COUNTER) {
872 BufferParams const & bufparams = buf.params();
873 LyXTextClass const & textclass = bufparams.getLyXTextClass();
874 counters.step(layout->counter);
875 string label = expandLabel(textclass, layout, pit->params().appendix());
876 pit->params().labelString(label);
877 } else if (layout->labeltype == LABEL_ITEMIZE) {
878 // At some point of time we should do something more
879 // clever here, like:
880 // pit->params().labelString(
881 // bufparams.user_defined_bullet(pit->itemdepth).getText());
882 // for now, use a simple hardcoded label
884 switch (pit->itemdepth) {
899 pit->params().labelString(itemlabel);
900 } else if (layout->labeltype == LABEL_ENUMERATE) {
901 // Maybe we have to reset the enumeration counter.
902 resetEnumCounterIfNeeded(pit, first_pit, counters);
905 // Yes I know this is a really, really! bad solution
907 string enumcounter = "enum";
909 switch (pit->itemdepth) {
921 // not a valid enumdepth...
925 counters.step(enumcounter);
927 pit->params().labelString(counters.enumLabel(enumcounter));
928 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
929 counters.step("bibitem");
930 int number = counters.value("bibitem");
931 if (pit->bibitem()) {
932 pit->bibitem()->setCounter(number);
933 pit->params().labelString(layout->labelstring());
935 // In biblio should't be following counters but...
937 string s = buf.B_(layout->labelstring());
940 if (layout->labeltype == LABEL_SENSITIVE) {
941 ParagraphList::iterator end = ownerParagraphs().end();
942 ParagraphList::iterator tmppit = pit;
945 while (tmppit != end && tmppit->inInset()
946 // the single '=' is intended below
947 && (in = tmppit->inInset()->owner()))
949 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
950 in->lyxCode() == InsetOld::WRAP_CODE) {
954 Paragraph const * owner = &ownerPar(buf, in);
956 for ( ; tmppit != end; ++tmppit)
957 if (&*tmppit == owner)
965 if (in->lyxCode() == InsetOld::FLOAT_CODE)
966 type = static_cast<InsetFloat*>(in)->params().type;
967 else if (in->lyxCode() == InsetOld::WRAP_CODE)
968 type = static_cast<InsetWrap*>(in)->params().type;
972 Floating const & fl = textclass.floats().getType(type);
974 counters.step(fl.type());
976 // Doesn't work... yet.
977 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
979 // par->SetLayout(0);
980 // s = layout->labelstring;
981 s = _("Senseless: ");
984 pit->params().labelString(s);
990 // Updates all counters. Paragraphs with changed label string will be rebroken
991 void LyXText::updateCounters()
994 bv()->buffer()->params().getLyXTextClass().counters().reset();
996 ParagraphList::iterator beg = ownerParagraphs().begin();
997 ParagraphList::iterator end = ownerParagraphs().end();
998 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
999 string const oldLabel = pit->params().labelString();
1001 size_t maxdepth = 0;
1003 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1005 if (pit->params().depth() > maxdepth)
1006 pit->params().depth(maxdepth);
1008 // setCounter can potentially change the labelString.
1009 setCounter(*bv()->buffer(), pit);
1011 string const & newLabel = pit->params().labelString();
1013 if (oldLabel != newLabel)
1019 void LyXText::insertInset(InsetOld * inset)
1021 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1023 recUndo(cursor.par());
1025 cursorPar()->insertInset(cursor.pos(), inset);
1026 // Just to rebreak and refresh correctly.
1027 // The character will not be inserted a second time
1028 insertChar(Paragraph::META_INSET);
1029 // If we enter a highly editable inset the cursor should be before
1030 // the inset. After an Undo LyX tries to call inset->edit(...)
1031 // and fails if the cursor is behind the inset and getInset
1032 // does not return the inset!
1033 if (isHighlyEditableInset(inset))
1039 void LyXText::cutSelection(bool doclear, bool realcut)
1041 // Stuff what we got on the clipboard. Even if there is no selection.
1043 // There is a problem with having the stuffing here in that the
1044 // larger the selection the slower LyX will get. This can be
1045 // solved by running the line below only when the selection has
1046 // finished. The solution used currently just works, to make it
1047 // faster we need to be more clever and probably also have more
1048 // calls to stuffClipboard. (Lgb)
1049 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1051 // This doesn't make sense, if there is no selection
1052 if (!selection.set())
1055 // OK, we have a selection. This is always between selection.start
1056 // and selection.end
1058 // make sure that the depth behind the selection are restored, too
1059 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1060 ParagraphList::iterator undoendpit = endpit;
1061 ParagraphList::iterator pars_end = ownerParagraphs().end();
1063 if (endpit != pars_end && endpit->getDepth()) {
1064 while (endpit != pars_end && endpit->getDepth()) {
1066 undoendpit = endpit;
1068 } else if (endpit != pars_end) {
1069 // because of parindents etc.
1073 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1075 endpit = getPar(selection.end.par());
1076 int endpos = selection.end.pos();
1078 BufferParams const & bufparams = bv()->buffer()->params();
1079 boost::tie(endpit, endpos) = realcut ?
1080 CutAndPaste::cutSelection(bufparams,
1082 getPar(selection.start.par()), endpit,
1083 selection.start.pos(), endpos,
1084 bufparams.textclass,
1086 : CutAndPaste::eraseSelection(bufparams,
1088 getPar(selection.start.par()), endpit,
1089 selection.start.pos(), endpos,
1091 // sometimes necessary
1093 getPar(selection.start.par())->stripLeadingSpaces();
1095 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1096 // cutSelection can invalidate the cursor so we need to set
1098 // we prefer the end for when tracking changes
1100 cursor.par(parOffset(endpit));
1102 // need a valid cursor. (Lgb)
1105 setCursor(cursorPar(), cursor.pos());
1106 selection.cursor = cursor;
1111 void LyXText::copySelection()
1113 // stuff the selection onto the X clipboard, from an explicit copy request
1114 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1116 // this doesnt make sense, if there is no selection
1117 if (!selection.set())
1120 // ok we have a selection. This is always between selection.start
1121 // and sel_end cursor
1123 // copy behind a space if there is one
1124 while (getPar(selection.start)->size() > selection.start.pos()
1125 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1126 && (selection.start.par() != selection.end.par()
1127 || selection.start.pos() < selection.end.pos()))
1128 selection.start.pos(selection.start.pos() + 1);
1130 CutAndPaste::copySelection(getPar(selection.start.par()),
1131 getPar(selection.end.par()),
1132 selection.start.pos(), selection.end.pos(),
1133 bv()->buffer()->params().textclass);
1137 void LyXText::pasteSelection(size_t sel_index)
1139 // this does not make sense, if there is nothing to paste
1140 if (!CutAndPaste::checkPastePossible())
1143 recUndo(cursor.par());
1145 ParagraphList::iterator endpit;
1150 boost::tie(ppp, endpit) =
1151 CutAndPaste::pasteSelection(*bv()->buffer(),
1153 cursorPar(), cursor.pos(),
1154 bv()->buffer()->params().textclass,
1156 bufferErrors(*bv()->buffer(), el);
1157 bv()->showErrorList(_("Paste"));
1159 redoParagraphs(cursorPar(), endpit);
1161 setCursor(cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1165 setCursor(ppp.first, ppp.second);
1171 void LyXText::setSelectionRange(lyx::pos_type length)
1176 selection.cursor = cursor;
1183 // simple replacing. The font of the first selected character is used
1184 void LyXText::replaceSelectionWithString(string const & str)
1186 recUndo(cursor.par());
1189 if (!selection.set()) { // create a dummy selection
1190 selection.end = cursor;
1191 selection.start = cursor;
1194 // Get font setting before we cut
1195 pos_type pos = selection.end.pos();
1196 LyXFont const font = getPar(selection.start)
1197 ->getFontSettings(bv()->buffer()->params(),
1198 selection.start.pos());
1200 // Insert the new string
1201 string::const_iterator cit = str.begin();
1202 string::const_iterator end = str.end();
1203 for (; cit != end; ++cit) {
1204 getPar(selection.end)->insertChar(pos, (*cit), font);
1208 // Cut the selection
1209 cutSelection(true, false);
1215 // needed to insert the selection
1216 void LyXText::insertStringAsLines(string const & str)
1218 ParagraphList::iterator pit = cursorPar();
1219 pos_type pos = cursor.pos();
1220 ParagraphList::iterator endpit = boost::next(cursorPar());
1222 recUndo(cursor.par());
1224 // only to be sure, should not be neccessary
1227 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1229 redoParagraphs(cursorPar(), endpit);
1230 setCursor(cursorPar(), cursor.pos());
1231 selection.cursor = cursor;
1232 setCursor(pit, pos);
1237 // turns double-CR to single CR, others where converted into one
1238 // blank. Then InsertStringAsLines is called
1239 void LyXText::insertStringAsParagraphs(string const & str)
1241 string linestr(str);
1242 bool newline_inserted = false;
1243 string::size_type const siz = linestr.length();
1245 for (string::size_type i = 0; i < siz; ++i) {
1246 if (linestr[i] == '\n') {
1247 if (newline_inserted) {
1248 // we know that \r will be ignored by
1249 // InsertStringA. Of course, it is a dirty
1250 // trick, but it works...
1251 linestr[i - 1] = '\r';
1255 newline_inserted = true;
1257 } else if (IsPrintable(linestr[i])) {
1258 newline_inserted = false;
1261 insertStringAsLines(linestr);
1265 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1267 setCursor(parOffset(pit), pos);
1271 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1273 LyXCursor old_cursor = cursor;
1274 setCursorIntern(par, pos, setfont, boundary);
1275 return deleteEmptyParagraphMechanism(old_cursor);
1279 void LyXText::redoCursor()
1281 #warning maybe the same for selections?
1282 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1286 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1287 pos_type pos, bool boundary)
1289 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1293 cur.boundary(boundary);
1295 // no rows, no fun...
1296 if (ownerParagraphs().begin()->rows.empty())
1299 // get the cursor y position in text
1301 ParagraphList::iterator pit = getPar(par);
1302 Row const & row = *pit->getRow(pos);
1303 int y = pit->y + row.y_offset();
1305 // y is now the beginning of the cursor row
1306 y += row.baseline();
1307 // y is now the cursor baseline
1310 pos_type last = lastPos(*pit, row);
1312 // None of these should happen, but we're scaredy-cats
1313 if (pos > pit->size()) {
1314 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1317 } else if (pos > last + 1) {
1318 lyxerr << "dont like 2 please report" << endl;
1319 // This shouldn't happen.
1322 } else if (pos < row.pos()) {
1323 lyxerr << "dont like 3 please report" << endl;
1328 // now get the cursors x position
1329 float x = getCursorX(pit, row, pos, last, boundary);
1335 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1336 pos_type pos, pos_type last, bool boundary) const
1338 pos_type cursor_vpos = 0;
1340 double fill_separator = row.fill_separator();
1341 double fill_hfill = row.fill_hfill();
1342 double fill_label_hfill = row.fill_label_hfill();
1343 pos_type const row_pos = row.pos();
1346 cursor_vpos = row_pos;
1347 else if (pos > last && !boundary)
1348 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1349 ? row_pos : last + 1;
1350 else if (pos > row_pos && (pos > last || boundary))
1351 // Place cursor after char at (logical) position pos - 1
1352 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1353 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1355 // Place cursor before char at (logical) position pos
1356 cursor_vpos = (bidi.level(pos) % 2 == 0)
1357 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1359 pos_type body_pos = pit->beginningOfBody();
1361 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1364 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1365 pos_type pos = bidi.vis2log(vpos);
1366 if (body_pos > 0 && pos == body_pos - 1) {
1367 x += fill_label_hfill +
1368 font_metrics::width(
1369 pit->layout()->labelsep, getLabelFont(pit));
1370 if (pit->isLineSeparator(body_pos - 1))
1371 x -= singleWidth(pit, body_pos - 1);
1374 if (hfillExpansion(*pit, row, pos)) {
1375 x += singleWidth(pit, pos);
1376 if (pos >= body_pos)
1379 x += fill_label_hfill;
1380 } else if (pit->isSeparator(pos)) {
1381 x += singleWidth(pit, pos);
1382 if (pos >= body_pos)
1383 x += fill_separator;
1385 x += singleWidth(pit, pos);
1391 void LyXText::setCursorIntern(paroffset_type par,
1392 pos_type pos, bool setfont, bool boundary)
1394 setCursor(cursor, par, pos, boundary);
1400 void LyXText::setCurrentFont()
1402 pos_type pos = cursor.pos();
1403 ParagraphList::iterator pit = cursorPar();
1405 if (cursor.boundary() && pos > 0)
1409 if (pos == pit->size())
1411 else // potentional bug... BUG (Lgb)
1412 if (pit->isSeparator(pos)) {
1413 if (pos > pit->getRow(pos)->pos() &&
1414 bidi.level(pos) % 2 ==
1415 bidi.level(pos - 1) % 2)
1417 else if (pos + 1 < pit->size())
1422 BufferParams const & bufparams = bv()->buffer()->params();
1423 current_font = pit->getFontSettings(bufparams, pos);
1424 real_current_font = getFont(pit, pos);
1426 if (cursor.pos() == pit->size() &&
1427 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1428 !cursor.boundary()) {
1429 Language const * lang =
1430 pit->getParLanguage(bufparams);
1431 current_font.setLanguage(lang);
1432 current_font.setNumber(LyXFont::OFF);
1433 real_current_font.setLanguage(lang);
1434 real_current_font.setNumber(LyXFont::OFF);
1439 // returns the column near the specified x-coordinate of the row
1440 // x is set to the real beginning of this column
1441 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1442 Row const & row, int & x, bool & boundary) const
1444 double tmpx = row.x();
1445 double fill_separator = row.fill_separator();
1446 double fill_hfill = row.fill_hfill();
1447 double fill_label_hfill = row.fill_label_hfill();
1449 pos_type vc = row.pos();
1450 pos_type last = lastPos(*pit, row);
1452 LyXLayout_ptr const & layout = pit->layout();
1454 bool left_side = false;
1456 pos_type body_pos = pit->beginningOfBody();
1457 double last_tmpx = tmpx;
1460 (body_pos - 1 > last ||
1461 !pit->isLineSeparator(body_pos - 1)))
1464 // check for empty row
1470 while (vc <= last && tmpx <= x) {
1471 c = bidi.vis2log(vc);
1473 if (body_pos > 0 && c == body_pos - 1) {
1474 tmpx += fill_label_hfill +
1475 font_metrics::width(layout->labelsep, getLabelFont(pit));
1476 if (pit->isLineSeparator(body_pos - 1))
1477 tmpx -= singleWidth(pit, body_pos - 1);
1480 if (hfillExpansion(*pit, row, c)) {
1481 tmpx += singleWidth(pit, c);
1485 tmpx += fill_label_hfill;
1486 } else if (pit->isSeparator(c)) {
1487 tmpx += singleWidth(pit, c);
1489 tmpx += fill_separator;
1491 tmpx += singleWidth(pit, c);
1496 if ((tmpx + last_tmpx) / 2 > x) {
1501 if (vc > last + 1) // This shouldn't happen.
1505 // This (rtl_support test) is not needed, but gives
1506 // some speedup if rtl_support == false
1507 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1509 // If lastrow is false, we don't need to compute
1510 // the value of rtl.
1511 bool const rtl = (lastrow)
1512 ? pit->isRightToLeftPar(bv()->buffer()->params())
1515 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1516 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1518 else if (vc == row.pos()) {
1519 c = bidi.vis2log(vc);
1520 if (bidi.level(c) % 2 == 1)
1523 c = bidi.vis2log(vc - 1);
1524 bool const rtl = (bidi.level(c) % 2 == 1);
1525 if (left_side == rtl) {
1527 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1531 if (row.pos() <= last && c > last && pit->isNewline(last)) {
1532 if (bidi.level(last) % 2 == 0)
1533 tmpx -= singleWidth(pit, last);
1535 tmpx += singleWidth(pit, last);
1545 void LyXText::setCursorFromCoordinates(int x, int y)
1547 LyXCursor old_cursor = cursor;
1548 setCursorFromCoordinates(cursor, x, y);
1550 deleteEmptyParagraphMechanism(old_cursor);
1554 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1556 // Get the row first.
1557 ParagraphList::iterator pit;
1558 Row const & row = *getRowNearY(y, pit);
1559 y = pit->y + row.y_offset();
1562 pos_type const column = getColumnNearX(pit, row, x, bound);
1563 cur.par(parOffset(pit));
1564 cur.pos(row.pos() + column);
1566 cur.y(y + row.baseline());
1568 cur.boundary(bound);
1572 void LyXText::cursorLeft(bool internal)
1574 if (cursor.pos() > 0) {
1575 bool boundary = cursor.boundary();
1576 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1577 if (!internal && !boundary &&
1578 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1579 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1580 } else if (cursor.par() != 0) {
1581 // steps into the paragraph above
1582 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1587 void LyXText::cursorRight(bool internal)
1589 bool const at_end = (cursor.pos() == cursorPar()->size());
1590 bool const at_newline = !at_end &&
1591 cursorPar()->isNewline(cursor.pos());
1593 if (!internal && cursor.boundary() && !at_newline)
1594 setCursor(cursor.par(), cursor.pos(), true, false);
1596 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1597 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1599 setCursor(cursor.par(), cursor.pos(), true, true);
1600 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1601 setCursor(cursor.par() + 1, 0);
1605 void LyXText::cursorUp(bool selecting)
1607 ParagraphList::iterator cpit = cursorPar();
1608 Row const & crow = *cpit->getRow(cursor.pos());
1610 int x = cursor.x_fix();
1611 int y = cursor.y() - crow.baseline() - 1;
1612 setCursorFromCoordinates(x, y);
1614 int topy = bv_owner->top_y();
1615 int y1 = cursor.y() - topy;
1618 InsetOld * inset_hit = checkInsetHit(x, y1);
1619 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1620 inset_hit->dispatch(
1621 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1625 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1626 crow.baseline() << endl;
1627 setCursorFromCoordinates(cursor.x_fix(),
1628 cursor.y() - crow.baseline() - 1);
1633 void LyXText::cursorDown(bool selecting)
1635 ParagraphList::iterator cpit = cursorPar();
1636 Row const & crow = *cpit->getRow(cursor.pos());
1638 int x = cursor.x_fix();
1639 int y = cursor.y() - crow.baseline() + crow.height() + 1;
1640 setCursorFromCoordinates(x, y);
1642 int topy = bv_owner->top_y();
1643 int y1 = cursor.y() - topy;
1646 InsetOld * inset_hit = checkInsetHit(x, y1);
1647 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1648 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1649 inset_hit->dispatch(cmd);
1653 setCursorFromCoordinates(cursor.x_fix(),
1654 cursor.y() - crow.baseline() + crow.height() + 1);
1659 void LyXText::cursorUpParagraph()
1661 ParagraphList::iterator cpit = cursorPar();
1662 if (cursor.pos() > 0)
1664 else if (cpit != ownerParagraphs().begin())
1665 setCursor(boost::prior(cpit), 0);
1669 void LyXText::cursorDownParagraph()
1671 ParagraphList::iterator pit = cursorPar();
1672 ParagraphList::iterator next_pit = boost::next(pit);
1674 if (next_pit != ownerParagraphs().end())
1675 setCursor(next_pit, 0);
1677 setCursor(pit, pit->size());
1681 // fix the cursor `cur' after a characters has been deleted at `where'
1682 // position. Called by deleteEmptyParagraphMechanism
1683 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1685 // if cursor is not in the paragraph where the delete occured,
1687 if (cur.par() != where.par())
1690 // if cursor position is after the place where the delete occured,
1692 if (cur.pos() > where.pos())
1693 cur.pos(cur.pos()-1);
1695 // check also if we don't want to set the cursor on a spot behind the
1696 // pagragraph because we erased the last character.
1697 if (cur.pos() > getPar(cur)->size())
1698 cur.pos(getPar(cur)->size());
1700 // recompute row et al. for this cursor
1701 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1705 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1707 // Would be wrong to delete anything if we have a selection.
1708 if (selection.set())
1711 // Don't do anything if the cursor is invalid
1712 if (old_cursor.par() == -1)
1715 // We allow all kinds of "mumbo-jumbo" when freespacing.
1716 ParagraphList::iterator const old_pit = getPar(old_cursor);
1717 if (old_pit->isFreeSpacing())
1720 /* Ok I'll put some comments here about what is missing.
1721 I have fixed BackSpace (and thus Delete) to not delete
1722 double-spaces automagically. I have also changed Cut,
1723 Copy and Paste to hopefully do some sensible things.
1724 There are still some small problems that can lead to
1725 double spaces stored in the document file or space at
1726 the beginning of paragraphs. This happens if you have
1727 the cursor between to spaces and then save. Or if you
1728 cut and paste and the selection have a space at the
1729 beginning and then save right after the paste. I am
1730 sure none of these are very hard to fix, but I will
1731 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1732 that I can get some feedback. (Lgb)
1735 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1736 // delete the LineSeparator.
1739 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1740 // delete the LineSeparator.
1743 // If the pos around the old_cursor were spaces, delete one of them.
1744 if (old_cursor.par() != cursor.par()
1745 || old_cursor.pos() != cursor.pos()) {
1747 // Only if the cursor has really moved
1748 if (old_cursor.pos() > 0
1749 && old_cursor.pos() < old_pit->size()
1750 && old_pit->isLineSeparator(old_cursor.pos())
1751 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1752 bool erased = old_pit->erase(old_cursor.pos() - 1);
1753 redoParagraph(old_pit);
1757 #ifdef WITH_WARNINGS
1758 #warning This will not work anymore when we have multiple views of the same buffer
1759 // In this case, we will have to correct also the cursors held by
1760 // other bufferviews. It will probably be easier to do that in a more
1761 // automated way in LyXCursor code. (JMarc 26/09/2001)
1763 // correct all cursors held by the LyXText
1764 fixCursorAfterDelete(cursor, old_cursor);
1765 fixCursorAfterDelete(selection.cursor, old_cursor);
1766 fixCursorAfterDelete(selection.start, old_cursor);
1767 fixCursorAfterDelete(selection.end, old_cursor);
1772 // don't delete anything if this is the ONLY paragraph!
1773 if (ownerParagraphs().size() == 1)
1776 // Do not delete empty paragraphs with keepempty set.
1777 if (old_pit->allowEmpty())
1780 // only do our magic if we changed paragraph
1781 if (old_cursor.par() == cursor.par())
1784 // record if we have deleted a paragraph
1785 // we can't possibly have deleted a paragraph before this point
1786 bool deleted = false;
1788 if (old_pit->empty() ||
1789 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1790 // ok, we will delete something
1791 LyXCursor tmpcursor;
1795 bool selection_position_was_oldcursor_position =
1796 selection.cursor.par() == old_cursor.par()
1797 && selection.cursor.pos() == old_cursor.pos();
1800 cursor = old_cursor; // that undo can restore the right cursor position
1802 ParagraphList::iterator endpit = boost::next(old_pit);
1803 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1806 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1810 ownerParagraphs().erase(old_pit);
1814 setCursorIntern(cursor.par(), cursor.pos());
1816 if (selection_position_was_oldcursor_position) {
1817 // correct selection
1818 selection.cursor = cursor;
1822 if (old_pit->stripLeadingSpaces()) {
1823 redoParagraph(old_pit);
1825 setCursorIntern(cursor.par(), cursor.pos());
1826 selection.cursor = cursor;
1833 ParagraphList & LyXText::ownerParagraphs() const
1835 return *paragraphs_;
1839 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1841 recordUndo(Undo::ATOMIC, this, first, last);
1845 void LyXText::recUndo(lyx::paroffset_type par) const
1847 recordUndo(Undo::ATOMIC, this, par, par);
1851 bool LyXText::isInInset() const
1853 // Sub-level has non-null bv owner and non-null inset owner.
1854 return inset_owner != 0;
1858 int defaultRowHeight()
1860 LyXFont const font(LyXFont::ALL_SANE);
1861 return int(font_metrics::maxAscent(font)
1862 + font_metrics::maxDescent(font) * 1.5);