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),
74 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
75 in_inset_(ininset), paragraphs_(¶graphs),
81 void LyXText::init(BufferView * bview)
85 ParagraphList::iterator const beg = ownerParagraphs().begin();
86 ParagraphList::iterator const end = ownerParagraphs().end();
87 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 pos_type end = cpit->getRow(cursor.pos())->endpos();
572 /* if not on the last row of the par, put the cursor before
574 setCursor(cpit, end == cpit->size() ? end : end - 1);
578 void LyXText::cursorTop()
580 setCursor(ownerParagraphs().begin(), 0);
584 void LyXText::cursorBottom()
586 ParagraphList::iterator lastpit =
587 boost::prior(ownerParagraphs().end());
588 setCursor(lastpit, lastpit->size());
592 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
594 // If the mask is completely neutral, tell user
595 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
596 // Could only happen with user style
597 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
601 // Try implicit word selection
602 // If there is a change in the language the implicit word selection
604 LyXCursor resetCursor = cursor;
605 bool implicitSelection =
606 font.language() == ignore_language
607 && font.number() == LyXFont::IGNORE
608 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
611 setFont(font, toggleall);
613 // Implicit selections are cleared afterwards
614 //and cursor is set to the original position.
615 if (implicitSelection) {
617 cursor = resetCursor;
618 setCursor(cursorPar(), cursor.pos());
619 selection.cursor = cursor;
624 string LyXText::getStringToIndex()
626 // Try implicit word selection
627 // If there is a change in the language the implicit word selection
629 LyXCursor const reset_cursor = cursor;
630 bool const implicitSelection =
631 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
634 if (!selection.set())
635 bv()->owner()->message(_("Nothing to index!"));
636 else if (selection.start.par() != selection.end.par())
637 bv()->owner()->message(_("Cannot index more than one paragraph!"));
639 idxstring = selectionAsString(*bv()->buffer(), false);
641 // Reset cursors to their original position.
642 cursor = reset_cursor;
643 setCursor(cursorPar(), cursor.pos());
644 selection.cursor = cursor;
646 // Clear the implicit selection.
647 if (implicitSelection)
654 // the DTP switches for paragraphs. LyX will store them in the first
655 // physical paragraph. When a paragraph is broken, the top settings rest,
656 // the bottom settings are given to the new one. So I can make sure,
657 // they do not duplicate themself and you cannnot make dirty things with
660 void LyXText::setParagraph(
661 VSpace const & space_top,
662 VSpace const & space_bottom,
663 Spacing const & spacing,
665 string const & labelwidthstring,
668 LyXCursor tmpcursor = cursor;
669 if (!selection.set()) {
670 selection.start = cursor;
671 selection.end = cursor;
674 // make sure that the depth behind the selection are restored, too
675 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
676 ParagraphList::iterator undoendpit = endpit;
677 ParagraphList::iterator pars_end = ownerParagraphs().end();
679 if (endpit != pars_end && endpit->getDepth()) {
680 while (endpit != pars_end && endpit->getDepth()) {
684 } else if (endpit != pars_end) {
685 // because of parindents etc.
689 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
692 int tmppit = selection.end.par();
694 while (tmppit != selection.start.par() - 1) {
695 setCursor(tmppit, 0);
697 ParagraphList::iterator const pit = cursorPar();
698 ParagraphParameters & params = pit->params();
699 params.spaceTop(space_top);
700 params.spaceBottom(space_bottom);
701 params.spacing(spacing);
703 // does the layout allow the new alignment?
704 LyXLayout_ptr const & layout = pit->layout();
706 if (align == LYX_ALIGN_LAYOUT)
707 align = layout->align;
708 if (align & layout->alignpossible) {
709 if (align == layout->align)
710 params.align(LYX_ALIGN_LAYOUT);
714 pit->setLabelWidthString(labelwidthstring);
715 params.noindent(noindent);
719 redoParagraphs(getPar(selection.start), endpit);
722 setCursor(selection.start.par(), selection.start.pos());
723 selection.cursor = cursor;
724 setCursor(selection.end.par(), selection.end.pos());
726 setCursor(tmpcursor.par(), tmpcursor.pos());
728 bv()->updateInset(inset_owner);
734 string expandLabel(LyXTextClass const & textclass,
735 LyXLayout_ptr const & layout, bool appendix)
737 string fmt = appendix ?
738 layout->labelstring_appendix() : layout->labelstring();
740 // handle 'inherited level parts' in 'fmt',
741 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
742 size_t const i = fmt.find('@', 0);
743 if (i != string::npos) {
744 size_t const j = fmt.find('@', i + 1);
745 if (j != string::npos) {
746 string parent(fmt, i + 1, j - i - 1);
747 string label = expandLabel(textclass, textclass[parent], appendix);
748 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
752 return textclass.counters().counterLabel(fmt);
756 void incrementItemDepth(ParagraphList::iterator pit,
757 ParagraphList::iterator first_pit)
759 int const cur_labeltype = pit->layout()->labeltype;
761 if (cur_labeltype != LABEL_ENUMERATE &&
762 cur_labeltype != LABEL_ITEMIZE)
765 int const cur_depth = pit->getDepth();
767 ParagraphList::iterator prev_pit = boost::prior(pit);
769 int const prev_depth = prev_pit->getDepth();
770 int const prev_labeltype = prev_pit->layout()->labeltype;
771 if (prev_depth == 0 && cur_depth > 0) {
772 if (prev_labeltype == cur_labeltype) {
773 pit->itemdepth = prev_pit->itemdepth + 1;
776 } else if (prev_depth < cur_depth) {
777 if (prev_labeltype == cur_labeltype) {
778 pit->itemdepth = prev_pit->itemdepth + 1;
781 } else if (prev_depth == cur_depth) {
782 if (prev_labeltype == cur_labeltype) {
783 pit->itemdepth = prev_pit->itemdepth;
787 if (prev_pit == first_pit)
795 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
796 ParagraphList::iterator firstpit,
802 int const cur_depth = pit->getDepth();
803 ParagraphList::iterator prev_pit = boost::prior(pit);
805 int const prev_depth = prev_pit->getDepth();
806 int const prev_labeltype = prev_pit->layout()->labeltype;
807 if (prev_depth <= cur_depth) {
808 if (prev_labeltype != LABEL_ENUMERATE) {
809 switch (pit->itemdepth) {
811 counters.reset("enumi");
813 counters.reset("enumii");
815 counters.reset("enumiii");
817 counters.reset("enumiv");
823 if (prev_pit == firstpit)
833 // set the counter of a paragraph. This includes the labels
834 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
836 BufferParams const & bufparams = buf.params();
837 LyXTextClass const & textclass = bufparams.getLyXTextClass();
838 LyXLayout_ptr const & layout = pit->layout();
839 ParagraphList::iterator first_pit = ownerParagraphs().begin();
840 Counters & counters = textclass.counters();
845 if (pit == first_pit) {
846 pit->params().appendix(pit->params().startOfAppendix());
848 pit->params().appendix(boost::prior(pit)->params().appendix());
849 if (!pit->params().appendix() &&
850 pit->params().startOfAppendix()) {
851 pit->params().appendix(true);
852 textclass.counters().reset();
855 // Maybe we have to increment the item depth.
856 incrementItemDepth(pit, first_pit);
859 // erase what was there before
860 pit->params().labelString(string());
862 if (layout->margintype == MARGIN_MANUAL) {
863 if (pit->params().labelWidthString().empty())
864 pit->setLabelWidthString(layout->labelstring());
866 pit->setLabelWidthString(string());
869 // is it a layout that has an automatic label?
870 if (layout->labeltype == LABEL_COUNTER) {
871 BufferParams const & bufparams = buf.params();
872 LyXTextClass const & textclass = bufparams.getLyXTextClass();
873 counters.step(layout->counter);
874 string label = expandLabel(textclass, layout, pit->params().appendix());
875 pit->params().labelString(label);
876 } else if (layout->labeltype == LABEL_ITEMIZE) {
877 // At some point of time we should do something more
878 // clever here, like:
879 // pit->params().labelString(
880 // bufparams.user_defined_bullet(pit->itemdepth).getText());
881 // for now, use a simple hardcoded label
883 switch (pit->itemdepth) {
898 pit->params().labelString(itemlabel);
899 } else if (layout->labeltype == LABEL_ENUMERATE) {
900 // Maybe we have to reset the enumeration counter.
901 resetEnumCounterIfNeeded(pit, first_pit, counters);
904 // Yes I know this is a really, really! bad solution
906 string enumcounter = "enum";
908 switch (pit->itemdepth) {
920 // not a valid enumdepth...
924 counters.step(enumcounter);
926 pit->params().labelString(counters.enumLabel(enumcounter));
927 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
928 counters.step("bibitem");
929 int number = counters.value("bibitem");
930 if (pit->bibitem()) {
931 pit->bibitem()->setCounter(number);
932 pit->params().labelString(layout->labelstring());
934 // In biblio should't be following counters but...
936 string s = buf.B_(layout->labelstring());
939 if (layout->labeltype == LABEL_SENSITIVE) {
940 ParagraphList::iterator end = ownerParagraphs().end();
941 ParagraphList::iterator tmppit = pit;
944 while (tmppit != end && tmppit->inInset()
945 // the single '=' is intended below
946 && (in = tmppit->inInset()->owner()))
948 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
949 in->lyxCode() == InsetOld::WRAP_CODE) {
953 Paragraph const * owner = &ownerPar(buf, in);
955 for ( ; tmppit != end; ++tmppit)
956 if (&*tmppit == owner)
964 if (in->lyxCode() == InsetOld::FLOAT_CODE)
965 type = static_cast<InsetFloat*>(in)->params().type;
966 else if (in->lyxCode() == InsetOld::WRAP_CODE)
967 type = static_cast<InsetWrap*>(in)->params().type;
971 Floating const & fl = textclass.floats().getType(type);
973 counters.step(fl.type());
975 // Doesn't work... yet.
976 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
978 // par->SetLayout(0);
979 // s = layout->labelstring;
980 s = _("Senseless: ");
983 pit->params().labelString(s);
989 // Updates all counters. Paragraphs with changed label string will be rebroken
990 void LyXText::updateCounters()
993 bv()->buffer()->params().getLyXTextClass().counters().reset();
995 ParagraphList::iterator beg = ownerParagraphs().begin();
996 ParagraphList::iterator end = ownerParagraphs().end();
997 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
998 string const oldLabel = pit->params().labelString();
1000 size_t maxdepth = 0;
1002 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1004 if (pit->params().depth() > maxdepth)
1005 pit->params().depth(maxdepth);
1007 // setCounter can potentially change the labelString.
1008 setCounter(*bv()->buffer(), pit);
1010 string const & newLabel = pit->params().labelString();
1012 if (oldLabel != newLabel)
1018 void LyXText::insertInset(InsetOld * inset)
1020 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1022 recUndo(cursor.par());
1024 cursorPar()->insertInset(cursor.pos(), inset);
1025 // Just to rebreak and refresh correctly.
1026 // The character will not be inserted a second time
1027 insertChar(Paragraph::META_INSET);
1028 // If we enter a highly editable inset the cursor should be before
1029 // the inset. After an Undo LyX tries to call inset->edit(...)
1030 // and fails if the cursor is behind the inset and getInset
1031 // does not return the inset!
1032 if (isHighlyEditableInset(inset))
1038 void LyXText::cutSelection(bool doclear, bool realcut)
1040 // Stuff what we got on the clipboard. Even if there is no selection.
1042 // There is a problem with having the stuffing here in that the
1043 // larger the selection the slower LyX will get. This can be
1044 // solved by running the line below only when the selection has
1045 // finished. The solution used currently just works, to make it
1046 // faster we need to be more clever and probably also have more
1047 // calls to stuffClipboard. (Lgb)
1048 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1050 // This doesn't 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 selection.end
1057 // make sure that the depth behind the selection are restored, too
1058 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1059 ParagraphList::iterator undoendpit = endpit;
1060 ParagraphList::iterator pars_end = ownerParagraphs().end();
1062 if (endpit != pars_end && endpit->getDepth()) {
1063 while (endpit != pars_end && endpit->getDepth()) {
1065 undoendpit = endpit;
1067 } else if (endpit != pars_end) {
1068 // because of parindents etc.
1072 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1074 endpit = getPar(selection.end.par());
1075 int endpos = selection.end.pos();
1077 BufferParams const & bufparams = bv()->buffer()->params();
1078 boost::tie(endpit, endpos) = realcut ?
1079 CutAndPaste::cutSelection(bufparams,
1081 getPar(selection.start.par()), endpit,
1082 selection.start.pos(), endpos,
1083 bufparams.textclass,
1085 : CutAndPaste::eraseSelection(bufparams,
1087 getPar(selection.start.par()), endpit,
1088 selection.start.pos(), endpos,
1090 // sometimes necessary
1092 getPar(selection.start.par())->stripLeadingSpaces();
1094 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1095 // cutSelection can invalidate the cursor so we need to set
1097 // we prefer the end for when tracking changes
1099 cursor.par(parOffset(endpit));
1101 // need a valid cursor. (Lgb)
1104 setCursor(cursorPar(), cursor.pos());
1105 selection.cursor = cursor;
1110 void LyXText::copySelection()
1112 // stuff the selection onto the X clipboard, from an explicit copy request
1113 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1115 // this doesnt make sense, if there is no selection
1116 if (!selection.set())
1119 // ok we have a selection. This is always between selection.start
1120 // and sel_end cursor
1122 // copy behind a space if there is one
1123 while (getPar(selection.start)->size() > selection.start.pos()
1124 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1125 && (selection.start.par() != selection.end.par()
1126 || selection.start.pos() < selection.end.pos()))
1127 selection.start.pos(selection.start.pos() + 1);
1129 CutAndPaste::copySelection(getPar(selection.start.par()),
1130 getPar(selection.end.par()),
1131 selection.start.pos(), selection.end.pos(),
1132 bv()->buffer()->params().textclass);
1136 void LyXText::pasteSelection(size_t sel_index)
1138 // this does not make sense, if there is nothing to paste
1139 if (!CutAndPaste::checkPastePossible())
1142 recUndo(cursor.par());
1144 ParagraphList::iterator endpit;
1149 boost::tie(ppp, endpit) =
1150 CutAndPaste::pasteSelection(*bv()->buffer(),
1152 cursorPar(), cursor.pos(),
1153 bv()->buffer()->params().textclass,
1155 bufferErrors(*bv()->buffer(), el);
1156 bv()->showErrorList(_("Paste"));
1158 redoParagraphs(cursorPar(), endpit);
1160 setCursor(cursor.par(), cursor.pos());
1163 selection.cursor = cursor;
1164 setCursor(ppp.first, ppp.second);
1170 void LyXText::setSelectionRange(lyx::pos_type length)
1175 selection.cursor = cursor;
1182 // simple replacing. The font of the first selected character is used
1183 void LyXText::replaceSelectionWithString(string const & str)
1185 recUndo(cursor.par());
1188 if (!selection.set()) { // create a dummy selection
1189 selection.end = cursor;
1190 selection.start = cursor;
1193 // Get font setting before we cut
1194 pos_type pos = selection.end.pos();
1195 LyXFont const font = getPar(selection.start)
1196 ->getFontSettings(bv()->buffer()->params(),
1197 selection.start.pos());
1199 // Insert the new string
1200 string::const_iterator cit = str.begin();
1201 string::const_iterator end = str.end();
1202 for (; cit != end; ++cit) {
1203 getPar(selection.end)->insertChar(pos, (*cit), font);
1207 // Cut the selection
1208 cutSelection(true, false);
1214 // needed to insert the selection
1215 void LyXText::insertStringAsLines(string const & str)
1217 ParagraphList::iterator pit = cursorPar();
1218 pos_type pos = cursor.pos();
1219 ParagraphList::iterator endpit = boost::next(cursorPar());
1221 recUndo(cursor.par());
1223 // only to be sure, should not be neccessary
1226 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1228 redoParagraphs(cursorPar(), endpit);
1229 setCursor(cursorPar(), cursor.pos());
1230 selection.cursor = cursor;
1231 setCursor(pit, pos);
1236 // turns double-CR to single CR, others where converted into one
1237 // blank. Then InsertStringAsLines is called
1238 void LyXText::insertStringAsParagraphs(string const & str)
1240 string linestr(str);
1241 bool newline_inserted = false;
1242 string::size_type const siz = linestr.length();
1244 for (string::size_type i = 0; i < siz; ++i) {
1245 if (linestr[i] == '\n') {
1246 if (newline_inserted) {
1247 // we know that \r will be ignored by
1248 // InsertStringA. Of course, it is a dirty
1249 // trick, but it works...
1250 linestr[i - 1] = '\r';
1254 newline_inserted = true;
1256 } else if (IsPrintable(linestr[i])) {
1257 newline_inserted = false;
1260 insertStringAsLines(linestr);
1264 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1266 setCursor(parOffset(pit), pos);
1270 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1272 LyXCursor old_cursor = cursor;
1273 setCursorIntern(par, pos, setfont, boundary);
1274 return deleteEmptyParagraphMechanism(old_cursor);
1278 void LyXText::redoCursor()
1280 #warning maybe the same for selections?
1281 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1285 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1286 pos_type pos, bool boundary)
1288 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1292 cur.boundary(boundary);
1294 // no rows, no fun...
1295 if (ownerParagraphs().begin()->rows.empty())
1298 // get the cursor y position in text
1300 ParagraphList::iterator pit = getPar(par);
1301 Row const & row = *pit->getRow(pos);
1302 int y = pit->y + row.y_offset();
1304 // y is now the beginning of the cursor row
1305 y += row.baseline();
1306 // y is now the cursor baseline
1309 pos_type const end = row.endpos();
1311 // None of these should happen, but we're scaredy-cats
1313 lyxerr << "dont like -1" << endl;
1316 BOOST_ASSERT(false);
1317 } else if (pos > pit->size()) {
1318 lyxerr << "dont like 1, pos: " << pos
1319 << " size: " << pit->size()
1320 << " row.pos():" << row.pos()
1321 << " paroffset: " << par << endl;
1324 BOOST_ASSERT(false);
1325 } else if (pos > end) {
1326 lyxerr << "dont like 2 please report" << endl;
1327 // This shouldn't happen.
1330 BOOST_ASSERT(false);
1331 } else if (pos < row.pos()) {
1332 lyxerr << "dont like 3 please report pos:" << pos
1333 << " size: " << pit->size()
1334 << " row.pos():" << row.pos()
1335 << " paroffset: " << par << endl;
1338 BOOST_ASSERT(false);
1340 // now get the cursors x position
1341 float x = getCursorX(pit, row, pos, boundary);
1347 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1348 pos_type pos, bool boundary) const
1350 pos_type cursor_vpos = 0;
1352 double fill_separator = row.fill_separator();
1353 double fill_hfill = row.fill_hfill();
1354 double fill_label_hfill = row.fill_label_hfill();
1355 pos_type const row_pos = row.pos();
1356 pos_type const end = row.endpos();
1359 cursor_vpos = row_pos;
1360 else if (pos >= end && !boundary)
1361 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1363 else if (pos > row_pos && (pos >= end || boundary))
1364 // Place cursor after char at (logical) position pos - 1
1365 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1366 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1368 // Place cursor before char at (logical) position pos
1369 cursor_vpos = (bidi.level(pos) % 2 == 0)
1370 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1372 pos_type body_pos = pit->beginningOfBody();
1374 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1377 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1378 pos_type pos = bidi.vis2log(vpos);
1379 if (body_pos > 0 && pos == body_pos - 1) {
1380 x += fill_label_hfill
1381 + font_metrics::width(pit->layout()->labelsep,
1383 if (pit->isLineSeparator(body_pos - 1))
1384 x -= singleWidth(pit, body_pos - 1);
1387 if (hfillExpansion(*pit, row, pos)) {
1388 x += singleWidth(pit, pos);
1389 if (pos >= body_pos)
1392 x += fill_label_hfill;
1393 } else if (pit->isSeparator(pos)) {
1394 x += singleWidth(pit, pos);
1395 if (pos >= body_pos)
1396 x += fill_separator;
1398 x += singleWidth(pit, pos);
1404 void LyXText::setCursorIntern(paroffset_type par,
1405 pos_type pos, bool setfont, bool boundary)
1407 setCursor(cursor, par, pos, boundary);
1413 void LyXText::setCurrentFont()
1415 pos_type pos = cursor.pos();
1416 ParagraphList::iterator pit = cursorPar();
1418 if (cursor.boundary() && pos > 0)
1422 if (pos == pit->size())
1424 else // potentional bug... BUG (Lgb)
1425 if (pit->isSeparator(pos)) {
1426 if (pos > pit->getRow(pos)->pos() &&
1427 bidi.level(pos) % 2 ==
1428 bidi.level(pos - 1) % 2)
1430 else if (pos + 1 < pit->size())
1435 BufferParams const & bufparams = bv()->buffer()->params();
1436 current_font = pit->getFontSettings(bufparams, pos);
1437 real_current_font = getFont(pit, pos);
1439 if (cursor.pos() == pit->size() &&
1440 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1441 !cursor.boundary()) {
1442 Language const * lang =
1443 pit->getParLanguage(bufparams);
1444 current_font.setLanguage(lang);
1445 current_font.setNumber(LyXFont::OFF);
1446 real_current_font.setLanguage(lang);
1447 real_current_font.setNumber(LyXFont::OFF);
1452 // returns the column near the specified x-coordinate of the row
1453 // x is set to the real beginning of this column
1454 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1455 Row const & row, int & x, bool & boundary) const
1457 double tmpx = row.x();
1458 double fill_separator = row.fill_separator();
1459 double fill_hfill = row.fill_hfill();
1460 double fill_label_hfill = row.fill_label_hfill();
1462 pos_type vc = row.pos();
1463 pos_type end = row.endpos();
1465 LyXLayout_ptr const & layout = pit->layout();
1467 bool left_side = false;
1469 pos_type body_pos = pit->beginningOfBody();
1470 double last_tmpx = tmpx;
1474 !pit->isLineSeparator(body_pos - 1)))
1477 // check for empty row
1483 while (vc < end && tmpx <= x) {
1484 c = bidi.vis2log(vc);
1486 if (body_pos > 0 && c == body_pos - 1) {
1487 tmpx += fill_label_hfill +
1488 font_metrics::width(layout->labelsep, getLabelFont(pit));
1489 if (pit->isLineSeparator(body_pos - 1))
1490 tmpx -= singleWidth(pit, body_pos - 1);
1493 if (hfillExpansion(*pit, row, c)) {
1494 tmpx += singleWidth(pit, c);
1498 tmpx += fill_label_hfill;
1499 } else if (pit->isSeparator(c)) {
1500 tmpx += singleWidth(pit, c);
1502 tmpx += fill_separator;
1504 tmpx += singleWidth(pit, c);
1509 if ((tmpx + last_tmpx) / 2 > x) {
1514 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1517 // This (rtl_support test) is not needed, but gives
1518 // some speedup if rtl_support == false
1519 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1521 // If lastrow is false, we don't need to compute
1522 // the value of rtl.
1523 bool const rtl = (lastrow)
1524 ? pit->isRightToLeftPar(bv()->buffer()->params())
1527 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1528 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1530 else if (vc == row.pos()) {
1531 c = bidi.vis2log(vc);
1532 if (bidi.level(c) % 2 == 1)
1535 c = bidi.vis2log(vc - 1);
1536 bool const rtl = (bidi.level(c) % 2 == 1);
1537 if (left_side == rtl) {
1539 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1543 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1544 if (bidi.level(end -1) % 2 == 0)
1545 tmpx -= singleWidth(pit, end - 1);
1547 tmpx += singleWidth(pit, end - 1);
1557 void LyXText::setCursorFromCoordinates(int x, int y)
1559 LyXCursor old_cursor = cursor;
1560 setCursorFromCoordinates(cursor, x, y);
1562 deleteEmptyParagraphMechanism(old_cursor);
1566 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1568 // Get the row first.
1569 ParagraphList::iterator pit;
1570 Row const & row = *getRowNearY(y, pit);
1571 y = pit->y + row.y_offset();
1574 pos_type const column = getColumnNearX(pit, row, x, bound);
1575 cur.par(parOffset(pit));
1576 cur.pos(row.pos() + column);
1578 cur.y(y + row.baseline());
1580 cur.boundary(bound);
1584 void LyXText::cursorLeft(bool internal)
1586 if (cursor.pos() > 0) {
1587 bool boundary = cursor.boundary();
1588 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1589 if (!internal && !boundary &&
1590 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1591 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1592 } else if (cursor.par() != 0) {
1593 // steps into the paragraph above
1594 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1599 void LyXText::cursorRight(bool internal)
1601 bool const at_end = (cursor.pos() == cursorPar()->size());
1602 bool const at_newline = !at_end &&
1603 cursorPar()->isNewline(cursor.pos());
1605 if (!internal && cursor.boundary() && !at_newline)
1606 setCursor(cursor.par(), cursor.pos(), true, false);
1608 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1609 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1611 setCursor(cursor.par(), cursor.pos(), true, true);
1612 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1613 setCursor(cursor.par() + 1, 0);
1617 void LyXText::cursorUp(bool selecting)
1619 ParagraphList::iterator cpit = cursorPar();
1620 Row const & crow = *cpit->getRow(cursor.pos());
1622 int x = cursor.x_fix();
1623 int y = cursor.y() - crow.baseline() - 1;
1624 setCursorFromCoordinates(x, y);
1626 int topy = bv_owner->top_y();
1627 int y1 = cursor.y() - topy;
1630 InsetOld * inset_hit = checkInsetHit(x, y1);
1631 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1632 inset_hit->dispatch(
1633 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1637 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1638 crow.baseline() << endl;
1639 setCursorFromCoordinates(cursor.x_fix(),
1640 cursor.y() - crow.baseline() - 1);
1645 void LyXText::cursorDown(bool selecting)
1647 ParagraphList::iterator cpit = cursorPar();
1648 Row const & crow = *cpit->getRow(cursor.pos());
1650 int x = cursor.x_fix();
1651 int y = cursor.y() - crow.baseline() + crow.height() + 1;
1652 setCursorFromCoordinates(x, y);
1654 int topy = bv_owner->top_y();
1655 int y1 = cursor.y() - topy;
1658 InsetOld * inset_hit = checkInsetHit(x, y1);
1659 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1660 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1661 inset_hit->dispatch(cmd);
1665 setCursorFromCoordinates(cursor.x_fix(),
1666 cursor.y() - crow.baseline() + crow.height() + 1);
1671 void LyXText::cursorUpParagraph()
1673 ParagraphList::iterator cpit = cursorPar();
1674 if (cursor.pos() > 0)
1676 else if (cpit != ownerParagraphs().begin())
1677 setCursor(boost::prior(cpit), 0);
1681 void LyXText::cursorDownParagraph()
1683 ParagraphList::iterator pit = cursorPar();
1684 ParagraphList::iterator next_pit = boost::next(pit);
1686 if (next_pit != ownerParagraphs().end())
1687 setCursor(next_pit, 0);
1689 setCursor(pit, pit->size());
1693 // fix the cursor `cur' after a characters has been deleted at `where'
1694 // position. Called by deleteEmptyParagraphMechanism
1695 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1697 // if cursor is not in the paragraph where the delete occured,
1699 if (cur.par() != where.par())
1702 // if cursor position is after the place where the delete occured,
1704 if (cur.pos() > where.pos())
1705 cur.pos(cur.pos()-1);
1707 // check also if we don't want to set the cursor on a spot behind the
1708 // pagragraph because we erased the last character.
1709 if (cur.pos() > getPar(cur)->size())
1710 cur.pos(getPar(cur)->size());
1712 // recompute row et al. for this cursor
1713 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1717 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1719 // Would be wrong to delete anything if we have a selection.
1720 if (selection.set())
1723 // Don't do anything if the cursor is invalid
1724 if (old_cursor.par() == -1)
1727 // We allow all kinds of "mumbo-jumbo" when freespacing.
1728 ParagraphList::iterator const old_pit = getPar(old_cursor);
1729 if (old_pit->isFreeSpacing())
1732 /* Ok I'll put some comments here about what is missing.
1733 I have fixed BackSpace (and thus Delete) to not delete
1734 double-spaces automagically. I have also changed Cut,
1735 Copy and Paste to hopefully do some sensible things.
1736 There are still some small problems that can lead to
1737 double spaces stored in the document file or space at
1738 the beginning of paragraphs. This happens if you have
1739 the cursor between to spaces and then save. Or if you
1740 cut and paste and the selection have a space at the
1741 beginning and then save right after the paste. I am
1742 sure none of these are very hard to fix, but I will
1743 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1744 that I can get some feedback. (Lgb)
1747 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1748 // delete the LineSeparator.
1751 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1752 // delete the LineSeparator.
1755 // If the pos around the old_cursor were spaces, delete one of them.
1756 if (old_cursor.par() != cursor.par()
1757 || old_cursor.pos() != cursor.pos()) {
1759 // Only if the cursor has really moved
1760 if (old_cursor.pos() > 0
1761 && old_cursor.pos() < old_pit->size()
1762 && old_pit->isLineSeparator(old_cursor.pos())
1763 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1764 bool erased = old_pit->erase(old_cursor.pos() - 1);
1765 redoParagraph(old_pit);
1769 #ifdef WITH_WARNINGS
1770 #warning This will not work anymore when we have multiple views of the same buffer
1771 // In this case, we will have to correct also the cursors held by
1772 // other bufferviews. It will probably be easier to do that in a more
1773 // automated way in LyXCursor code. (JMarc 26/09/2001)
1775 // correct all cursors held by the LyXText
1776 fixCursorAfterDelete(cursor, old_cursor);
1777 fixCursorAfterDelete(selection.cursor, old_cursor);
1778 fixCursorAfterDelete(selection.start, old_cursor);
1779 fixCursorAfterDelete(selection.end, old_cursor);
1784 // don't delete anything if this is the ONLY paragraph!
1785 if (ownerParagraphs().size() == 1)
1788 // Do not delete empty paragraphs with keepempty set.
1789 if (old_pit->allowEmpty())
1792 // only do our magic if we changed paragraph
1793 if (old_cursor.par() == cursor.par())
1796 // record if we have deleted a paragraph
1797 // we can't possibly have deleted a paragraph before this point
1798 bool deleted = false;
1800 if (old_pit->empty()
1801 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1802 // ok, we will delete something
1803 LyXCursor tmpcursor;
1807 bool selection_position_was_oldcursor_position =
1808 selection.cursor.par() == old_cursor.par()
1809 && selection.cursor.pos() == old_cursor.pos();
1812 cursor = old_cursor; // that undo can restore the right cursor position
1814 ParagraphList::iterator endpit = boost::next(old_pit);
1815 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1818 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1822 ownerParagraphs().erase(old_pit);
1826 setCursorIntern(cursor.par(), cursor.pos());
1828 if (selection_position_was_oldcursor_position) {
1829 // correct selection
1830 selection.cursor = cursor;
1837 if (old_pit->stripLeadingSpaces()) {
1838 redoParagraph(old_pit);
1840 setCursorIntern(cursor.par(), cursor.pos());
1841 selection.cursor = cursor;
1847 ParagraphList & LyXText::ownerParagraphs() const
1849 return *paragraphs_;
1853 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1855 recordUndo(Undo::ATOMIC, this, first, last);
1859 void LyXText::recUndo(lyx::paroffset_type par) const
1861 recordUndo(Undo::ATOMIC, this, par, par);
1865 bool LyXText::isInInset() const
1867 // Sub-level has non-null bv owner and non-null inset owner.
1868 return inset_owner != 0;
1872 int defaultRowHeight()
1874 LyXFont const font(LyXFont::ALL_SANE);
1875 return int(font_metrics::maxHeight(font) * 1.2);