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 "dispatchresult.h"
33 #include "errorlist.h"
35 #include "FloatList.h"
36 #include "funcrequest.h"
41 #include "lyxrow_funcs.h"
42 #include "paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
48 #include "frontends/font_metrics.h"
49 #include "frontends/LyXView.h"
51 #include "insets/insetbibitem.h"
52 #include "insets/insetenv.h"
53 #include "insets/insetfloat.h"
54 #include "insets/insetwrap.h"
56 #include "support/lstrings.h"
57 #include "support/textutils.h"
58 #include "support/tostr.h"
59 #include "support/std_sstream.h"
61 #include <boost/tuple/tuple.hpp>
64 using lyx::paroffset_type;
65 using lyx::support::bformat;
68 using std::ostringstream;
72 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
73 ParagraphList & paragraphs)
74 : height(0), width(0),
75 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
76 in_inset_(ininset), paragraphs_(¶graphs),
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)
95 current_font = getFont(beg, 0);
97 redoParagraphs(beg, end);
98 setCursorIntern(0, 0);
99 selection.cursor = cursor;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as Paragraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
112 BOOST_ASSERT(pos >= 0);
114 LyXLayout_ptr const & layout = pit->layout();
116 BufferParams const & params = bv()->buffer()->params();
117 pos_type const body_pos = pit->beginningOfBody();
119 // We specialize the 95% common case:
120 if (!pit->getDepth()) {
121 LyXFont f = pit->getFontSettings(params, pos);
123 pit->inInset()->getDrawFont(f);
124 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
125 return f.realize(layout->reslabelfont);
127 return f.realize(layout->resfont);
130 // The uncommon case need not be optimized as much
133 layoutfont = layout->labelfont;
135 layoutfont = layout->font;
137 LyXFont font = pit->getFontSettings(params, pos);
138 font.realize(layoutfont);
141 pit->inInset()->getDrawFont(font);
143 // Realize with the fonts of lesser depth.
144 //font.realize(outerFont(pit, ownerParagraphs()));
145 font.realize(defaultfont_);
151 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
153 LyXLayout_ptr const & layout = pit->layout();
155 if (!pit->getDepth())
156 return layout->resfont;
158 LyXFont font = layout->font;
159 // Realize with the fonts of lesser depth.
160 //font.realize(outerFont(pit, ownerParagraphs()));
161 font.realize(defaultfont_);
167 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
169 LyXLayout_ptr const & layout = pit->layout();
171 if (!pit->getDepth())
172 return layout->reslabelfont;
174 LyXFont font = layout->labelfont;
175 // Realize with the fonts of lesser depth.
176 font.realize(outerFont(pit, ownerParagraphs()));
177 font.realize(defaultfont_);
183 void LyXText::setCharFont(ParagraphList::iterator pit,
184 pos_type pos, LyXFont const & fnt,
187 BufferParams const & params = bv()->buffer()->params();
188 LyXFont font = getFont(pit, pos);
189 font.update(fnt, params.language, toggleall);
190 // Let the insets convert their font
191 if (pit->isInset(pos)) {
192 InsetOld * inset = pit->getInset(pos);
193 if (isEditableInset(inset)) {
194 static_cast<UpdatableInset *>(inset)
195 ->setFont(bv(), fnt, toggleall, true);
199 // Plug through to version below:
200 setCharFont(pit, pos, font);
204 void LyXText::setCharFont(
205 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
208 LyXLayout_ptr const & layout = pit->layout();
210 // Get concrete layout font to reduce against
213 if (pos < pit->beginningOfBody())
214 layoutfont = layout->labelfont;
216 layoutfont = layout->font;
218 // Realize against environment font information
219 if (pit->getDepth()) {
220 ParagraphList::iterator tp = pit;
221 while (!layoutfont.resolved() &&
222 tp != ownerParagraphs().end() &&
224 tp = outerHook(tp, ownerParagraphs());
225 if (tp != ownerParagraphs().end())
226 layoutfont.realize(tp->layout()->font);
230 layoutfont.realize(defaultfont_);
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 pit->setFont(pos, font);
239 InsetOld * LyXText::getInset() const
241 ParagraphList::iterator pit = cursorPar();
242 pos_type const pos = cursor.pos();
244 if (pos < pit->size() && pit->isInset(pos)) {
245 return pit->getInset(pos);
251 void LyXText::toggleInset()
253 InsetOld * inset = getInset();
254 // is there an editable inset at cursor position?
255 if (!isEditableInset(inset)) {
256 // No, try to see if we are inside a collapsable inset
257 if (inset_owner && inset_owner->owner()
258 && inset_owner->owner()->isOpen()) {
259 bv()->unlockInset(inset_owner->owner());
260 inset_owner->owner()->close(bv());
261 bv()->getLyXText()->cursorRight(bv());
265 //bv()->owner()->message(inset->editMessage());
267 // do we want to keep this?? (JMarc)
268 if (!isHighlyEditableInset(inset))
269 recUndo(cursor.par());
276 bv()->updateInset(inset);
280 /* used in setlayout */
281 // Asger is not sure we want to do this...
282 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
285 LyXLayout_ptr const & layout = par.layout();
286 pos_type const psize = par.size();
289 for (pos_type pos = 0; pos < psize; ++pos) {
290 if (pos < par.beginningOfBody())
291 layoutfont = layout->labelfont;
293 layoutfont = layout->font;
295 LyXFont tmpfont = par.getFontSettings(params, pos);
296 tmpfont.reduce(layoutfont);
297 par.setFont(pos, tmpfont);
302 ParagraphList::iterator
303 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
304 LyXCursor & send_cur,
305 string const & layout)
307 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
308 ParagraphList::iterator undoendpit = endpit;
309 ParagraphList::iterator pars_end = ownerParagraphs().end();
311 if (endpit != pars_end && endpit->getDepth()) {
312 while (endpit != pars_end && endpit->getDepth()) {
316 } else if (endpit != pars_end) {
317 // because of parindents etc.
321 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
323 // ok we have a selection. This is always between sstart_cur
324 // and sel_end cursor
326 ParagraphList::iterator pit = getPar(sstart_cur);
327 ParagraphList::iterator epit = boost::next(getPar(send_cur));
329 BufferParams const & bufparams = bv()->buffer()->params();
330 LyXLayout_ptr const & lyxlayout =
331 bufparams.getLyXTextClass()[layout];
334 pit->applyLayout(lyxlayout);
335 makeFontEntriesLayoutSpecific(bufparams, *pit);
336 pit->params().spaceTop(lyxlayout->fill_top ?
337 VSpace(VSpace::VFILL)
338 : VSpace(VSpace::NONE));
339 pit->params().spaceBottom(lyxlayout->fill_bottom ?
340 VSpace(VSpace::VFILL)
341 : VSpace(VSpace::NONE));
342 if (lyxlayout->margintype == MARGIN_MANUAL)
343 pit->setLabelWidthString(lyxlayout->labelstring());
344 cur.par(std::distance(ownerParagraphs().begin(), pit));
346 } while (pit != epit);
352 // set layout over selection and make a total rebreak of those paragraphs
353 void LyXText::setLayout(string const & layout)
355 LyXCursor tmpcursor = cursor; // store the current cursor
357 // if there is no selection just set the layout
358 // of the current paragraph
359 if (!selection.set()) {
360 selection.start = cursor; // dummy selection
361 selection.end = cursor;
364 // special handling of new environment insets
365 BufferParams const & params = bv()->buffer()->params();
366 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
367 if (lyxlayout->is_environment) {
368 // move everything in a new environment inset
369 lyxerr << "setting layout " << layout << endl;
370 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
371 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
372 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
373 InsetOld * inset = new InsetEnvironment(params, layout);
374 if (bv()->insertInset(inset)) {
376 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
383 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
384 selection.end, layout);
385 redoParagraphs(getPar(selection.start), endpit);
387 // we have to reset the selection, because the
388 // geometry could have changed
389 setCursor(selection.start.par(), selection.start.pos(), false);
390 selection.cursor = cursor;
391 setCursor(selection.end.par(), selection.end.pos(), false);
395 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
399 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
401 ParagraphList::iterator pit = cursorPar();
402 ParagraphList::iterator end = cursorPar();
403 ParagraphList::iterator start = pit;
405 if (selection.set()) {
406 pit = getPar(selection.start);
407 end = getPar(selection.end);
411 ParagraphList::iterator pastend = boost::next(end);
414 recUndo(parOffset(start), parOffset(end));
416 bool changed = false;
418 int prev_after_depth = 0;
419 #warning parlist ... could be nicer ?
420 if (start != ownerParagraphs().begin()) {
421 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
425 int const depth = pit->params().depth();
426 if (type == bv_funcs::INC_DEPTH) {
427 if (depth < prev_after_depth
428 && pit->layout()->labeltype != LABEL_BIBLIO) {
431 pit->params().depth(depth + 1);
436 pit->params().depth(depth - 1);
439 prev_after_depth = pit->getMaxDepthAfter();
451 redoParagraphs(start, pastend);
453 // We need to actually move the text->cursor. I don't
454 // understand why ...
455 LyXCursor tmpcursor = cursor;
457 // we have to reset the visual selection because the
458 // geometry could have changed
459 if (selection.set()) {
460 setCursor(selection.start.par(), selection.start.pos());
461 selection.cursor = cursor;
462 setCursor(selection.end.par(), selection.end.pos());
465 // this handles the counter labels, and also fixes up
466 // depth values for follow-on (child) paragraphs
470 setCursor(tmpcursor.par(), tmpcursor.pos());
476 // set font over selection and make a total rebreak of those paragraphs
477 void LyXText::setFont(LyXFont const & font, bool toggleall)
479 // if there is no selection just set the current_font
480 if (!selection.set()) {
481 // Determine basis font
483 if (cursor.pos() < cursorPar()->beginningOfBody()) {
484 layoutfont = getLabelFont(cursorPar());
486 layoutfont = getLayoutFont(cursorPar());
488 // Update current font
489 real_current_font.update(font,
490 bv()->buffer()->params().language,
493 // Reduce to implicit settings
494 current_font = real_current_font;
495 current_font.reduce(layoutfont);
496 // And resolve it completely
497 real_current_font.realize(layoutfont);
502 LyXCursor tmpcursor = cursor; // store the current cursor
504 // ok we have a selection. This is always between sel_start_cursor
505 // and sel_end cursor
507 recUndo(selection.start.par(), selection.end.par());
509 cursor = selection.start;
510 while (cursor.par() != selection.end.par() ||
511 cursor.pos() < selection.end.pos())
513 if (cursor.pos() < cursorPar()->size()) {
514 // an open footnote should behave like a closed one
515 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
516 cursor.pos(cursor.pos() + 1);
519 cursor.par(cursor.par() + 1);
524 redoParagraph(getPar(selection.start));
526 // we have to reset the selection, because the
527 // geometry could have changed, but we keep
528 // it for user convenience
529 setCursor(selection.start.par(), selection.start.pos());
530 selection.cursor = cursor;
531 setCursor(selection.end.par(), selection.end.pos());
533 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
534 tmpcursor.boundary());
538 // important for the screen
541 // the cursor set functions have a special mechanism. When they
542 // realize, that you left an empty paragraph, they will delete it.
544 // need the selection cursor:
545 void LyXText::setSelection()
547 TextCursor::setSelection();
552 void LyXText::clearSelection()
554 TextCursor::clearSelection();
556 // reset this in the bv_owner!
557 if (bv_owner && bv_owner->text)
558 bv_owner->text->xsel_cache.set(false);
562 void LyXText::cursorHome()
564 ParagraphList::iterator cpit = cursorPar();
565 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
569 void LyXText::cursorEnd()
571 ParagraphList::iterator cpit = cursorPar();
572 pos_type end = cpit->getRow(cursor.pos())->endpos();
573 /* if not on the last row of the par, put the cursor before
575 setCursor(cpit, end == cpit->size() ? end : end - 1);
579 void LyXText::cursorTop()
581 setCursor(ownerParagraphs().begin(), 0);
585 void LyXText::cursorBottom()
587 ParagraphList::iterator lastpit =
588 boost::prior(ownerParagraphs().end());
589 setCursor(lastpit, lastpit->size());
593 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
595 // If the mask is completely neutral, tell user
596 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
597 // Could only happen with user style
598 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
602 // Try implicit word selection
603 // If there is a change in the language the implicit word selection
605 LyXCursor resetCursor = cursor;
606 bool implicitSelection =
607 font.language() == ignore_language
608 && font.number() == LyXFont::IGNORE
609 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
612 setFont(font, toggleall);
614 // Implicit selections are cleared afterwards
615 //and cursor is set to the original position.
616 if (implicitSelection) {
618 cursor = resetCursor;
619 setCursor(cursorPar(), cursor.pos());
620 selection.cursor = cursor;
625 string LyXText::getStringToIndex()
627 // Try implicit word selection
628 // If there is a change in the language the implicit word selection
630 LyXCursor const reset_cursor = cursor;
631 bool const implicitSelection =
632 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
635 if (!selection.set())
636 bv()->owner()->message(_("Nothing to index!"));
637 else if (selection.start.par() != selection.end.par())
638 bv()->owner()->message(_("Cannot index more than one paragraph!"));
640 idxstring = selectionAsString(*bv()->buffer(), false);
642 // Reset cursors to their original position.
643 cursor = reset_cursor;
644 setCursor(cursorPar(), cursor.pos());
645 selection.cursor = cursor;
647 // Clear the implicit selection.
648 if (implicitSelection)
655 // the DTP switches for paragraphs. LyX will store them in the first
656 // physical paragraph. When a paragraph is broken, the top settings rest,
657 // the bottom settings are given to the new one. So I can make sure,
658 // they do not duplicate themself and you cannnot make dirty things with
661 void LyXText::setParagraph(
662 VSpace const & space_top,
663 VSpace const & space_bottom,
664 Spacing const & spacing,
666 string const & labelwidthstring,
669 LyXCursor tmpcursor = cursor;
670 if (!selection.set()) {
671 selection.start = cursor;
672 selection.end = cursor;
675 // make sure that the depth behind the selection are restored, too
676 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
677 ParagraphList::iterator undoendpit = endpit;
678 ParagraphList::iterator pars_end = ownerParagraphs().end();
680 if (endpit != pars_end && endpit->getDepth()) {
681 while (endpit != pars_end && endpit->getDepth()) {
685 } else if (endpit != pars_end) {
686 // because of parindents etc.
690 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
693 int tmppit = selection.end.par();
695 while (tmppit != selection.start.par() - 1) {
696 setCursor(tmppit, 0);
698 ParagraphList::iterator const pit = cursorPar();
699 ParagraphParameters & params = pit->params();
700 params.spaceTop(space_top);
701 params.spaceBottom(space_bottom);
702 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 const end = row.endpos();
1312 // None of these should happen, but we're scaredy-cats
1314 lyxerr << "dont like -1" << endl;
1317 BOOST_ASSERT(false);
1318 } else if (pos > pit->size()) {
1319 lyxerr << "dont like 1, pos: " << pos
1320 << " size: " << pit->size()
1321 << " row.pos():" << row.pos()
1322 << " paroffset: " << par << endl;
1325 BOOST_ASSERT(false);
1326 } else if (pos > end) {
1327 lyxerr << "dont like 2 please report" << endl;
1328 // This shouldn't happen.
1331 BOOST_ASSERT(false);
1332 } else if (pos < row.pos()) {
1333 lyxerr << "dont like 3 please report pos:" << pos
1334 << " size: " << pit->size()
1335 << " row.pos():" << row.pos()
1336 << " paroffset: " << par << endl;
1339 BOOST_ASSERT(false);
1341 // now get the cursors x position
1342 cur.x(int(getCursorX(pit, row, pos, boundary)));
1343 bv()->x_target(cur.x());
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 = bv()->x_target();
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(bv()->x_target(),
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 = bv()->x_target();
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(bv()->x_target(),
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);