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
1315 << " size: " << pit->size()
1316 << " row.pos():" << row.pos()
1317 << " paroffset: " << par << endl;
1320 BOOST_ASSERT(false);
1321 } else if (pos > last + 1) {
1322 lyxerr << "dont like 2 please report" << endl;
1323 // This shouldn't happen.
1326 BOOST_ASSERT(false);
1327 } else if (pos < row.pos()) {
1328 lyxerr << "dont like 3 please report pos:" << pos
1329 << " size: " << pit->size()
1330 << " row.pos():" << row.pos()
1331 << " paroffset: " << par << endl;
1334 BOOST_ASSERT(false);
1337 // now get the cursors x position
1338 float x = getCursorX(pit, row, pos, last, boundary);
1344 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1345 pos_type pos, pos_type last, bool boundary) const
1347 pos_type cursor_vpos = 0;
1349 double fill_separator = row.fill_separator();
1350 double fill_hfill = row.fill_hfill();
1351 double fill_label_hfill = row.fill_label_hfill();
1352 pos_type const row_pos = row.pos();
1355 cursor_vpos = row_pos;
1356 else if (pos > last && !boundary)
1357 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1358 ? row_pos : last + 1;
1359 else if (pos > row_pos && (pos > last || boundary))
1360 // Place cursor after char at (logical) position pos - 1
1361 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1362 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1364 // Place cursor before char at (logical) position pos
1365 cursor_vpos = (bidi.level(pos) % 2 == 0)
1366 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1368 pos_type body_pos = pit->beginningOfBody();
1370 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1373 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1374 pos_type pos = bidi.vis2log(vpos);
1375 if (body_pos > 0 && pos == body_pos - 1) {
1376 x += fill_label_hfill +
1377 font_metrics::width(
1378 pit->layout()->labelsep, getLabelFont(pit));
1379 if (pit->isLineSeparator(body_pos - 1))
1380 x -= singleWidth(pit, body_pos - 1);
1383 if (hfillExpansion(*pit, row, pos)) {
1384 x += singleWidth(pit, pos);
1385 if (pos >= body_pos)
1388 x += fill_label_hfill;
1389 } else if (pit->isSeparator(pos)) {
1390 x += singleWidth(pit, pos);
1391 if (pos >= body_pos)
1392 x += fill_separator;
1394 x += singleWidth(pit, pos);
1400 void LyXText::setCursorIntern(paroffset_type par,
1401 pos_type pos, bool setfont, bool boundary)
1403 setCursor(cursor, par, pos, boundary);
1409 void LyXText::setCurrentFont()
1411 pos_type pos = cursor.pos();
1412 ParagraphList::iterator pit = cursorPar();
1414 if (cursor.boundary() && pos > 0)
1418 if (pos == pit->size())
1420 else // potentional bug... BUG (Lgb)
1421 if (pit->isSeparator(pos)) {
1422 if (pos > pit->getRow(pos)->pos() &&
1423 bidi.level(pos) % 2 ==
1424 bidi.level(pos - 1) % 2)
1426 else if (pos + 1 < pit->size())
1431 BufferParams const & bufparams = bv()->buffer()->params();
1432 current_font = pit->getFontSettings(bufparams, pos);
1433 real_current_font = getFont(pit, pos);
1435 if (cursor.pos() == pit->size() &&
1436 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1437 !cursor.boundary()) {
1438 Language const * lang =
1439 pit->getParLanguage(bufparams);
1440 current_font.setLanguage(lang);
1441 current_font.setNumber(LyXFont::OFF);
1442 real_current_font.setLanguage(lang);
1443 real_current_font.setNumber(LyXFont::OFF);
1448 // returns the column near the specified x-coordinate of the row
1449 // x is set to the real beginning of this column
1450 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1451 Row const & row, int & x, bool & boundary) const
1453 double tmpx = row.x();
1454 double fill_separator = row.fill_separator();
1455 double fill_hfill = row.fill_hfill();
1456 double fill_label_hfill = row.fill_label_hfill();
1458 pos_type vc = row.pos();
1459 pos_type last = lastPos(*pit, row);
1461 LyXLayout_ptr const & layout = pit->layout();
1463 bool left_side = false;
1465 pos_type body_pos = pit->beginningOfBody();
1466 double last_tmpx = tmpx;
1469 (body_pos - 1 > last ||
1470 !pit->isLineSeparator(body_pos - 1)))
1473 // check for empty row
1479 while (vc <= last && tmpx <= x) {
1480 c = bidi.vis2log(vc);
1482 if (body_pos > 0 && c == body_pos - 1) {
1483 tmpx += fill_label_hfill +
1484 font_metrics::width(layout->labelsep, getLabelFont(pit));
1485 if (pit->isLineSeparator(body_pos - 1))
1486 tmpx -= singleWidth(pit, body_pos - 1);
1489 if (hfillExpansion(*pit, row, c)) {
1490 tmpx += singleWidth(pit, c);
1494 tmpx += fill_label_hfill;
1495 } else if (pit->isSeparator(c)) {
1496 tmpx += singleWidth(pit, c);
1498 tmpx += fill_separator;
1500 tmpx += singleWidth(pit, c);
1505 if ((tmpx + last_tmpx) / 2 > x) {
1510 if (vc > last + 1) // This shouldn't happen.
1514 // This (rtl_support test) is not needed, but gives
1515 // some speedup if rtl_support == false
1516 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1518 // If lastrow is false, we don't need to compute
1519 // the value of rtl.
1520 bool const rtl = (lastrow)
1521 ? pit->isRightToLeftPar(bv()->buffer()->params())
1524 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1525 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1527 else if (vc == row.pos()) {
1528 c = bidi.vis2log(vc);
1529 if (bidi.level(c) % 2 == 1)
1532 c = bidi.vis2log(vc - 1);
1533 bool const rtl = (bidi.level(c) % 2 == 1);
1534 if (left_side == rtl) {
1536 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1540 if (row.pos() <= last && c > last && pit->isNewline(last)) {
1541 if (bidi.level(last) % 2 == 0)
1542 tmpx -= singleWidth(pit, last);
1544 tmpx += singleWidth(pit, last);
1554 void LyXText::setCursorFromCoordinates(int x, int y)
1556 LyXCursor old_cursor = cursor;
1557 setCursorFromCoordinates(cursor, x, y);
1559 deleteEmptyParagraphMechanism(old_cursor);
1563 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1565 // Get the row first.
1566 ParagraphList::iterator pit;
1567 Row const & row = *getRowNearY(y, pit);
1568 y = pit->y + row.y_offset();
1571 pos_type const column = getColumnNearX(pit, row, x, bound);
1572 cur.par(parOffset(pit));
1573 cur.pos(row.pos() + column);
1575 cur.y(y + row.baseline());
1577 cur.boundary(bound);
1581 void LyXText::cursorLeft(bool internal)
1583 if (cursor.pos() > 0) {
1584 bool boundary = cursor.boundary();
1585 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1586 if (!internal && !boundary &&
1587 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1588 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1589 } else if (cursor.par() != 0) {
1590 // steps into the paragraph above
1591 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1596 void LyXText::cursorRight(bool internal)
1598 bool const at_end = (cursor.pos() == cursorPar()->size());
1599 bool const at_newline = !at_end &&
1600 cursorPar()->isNewline(cursor.pos());
1602 if (!internal && cursor.boundary() && !at_newline)
1603 setCursor(cursor.par(), cursor.pos(), true, false);
1605 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1606 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1608 setCursor(cursor.par(), cursor.pos(), true, true);
1609 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1610 setCursor(cursor.par() + 1, 0);
1614 void LyXText::cursorUp(bool selecting)
1616 ParagraphList::iterator cpit = cursorPar();
1617 Row const & crow = *cpit->getRow(cursor.pos());
1619 int x = cursor.x_fix();
1620 int y = cursor.y() - crow.baseline() - 1;
1621 setCursorFromCoordinates(x, y);
1623 int topy = bv_owner->top_y();
1624 int y1 = cursor.y() - topy;
1627 InsetOld * inset_hit = checkInsetHit(x, y1);
1628 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1629 inset_hit->dispatch(
1630 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1634 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1635 crow.baseline() << endl;
1636 setCursorFromCoordinates(cursor.x_fix(),
1637 cursor.y() - crow.baseline() - 1);
1642 void LyXText::cursorDown(bool selecting)
1644 ParagraphList::iterator cpit = cursorPar();
1645 Row const & crow = *cpit->getRow(cursor.pos());
1647 int x = cursor.x_fix();
1648 int y = cursor.y() - crow.baseline() + crow.height() + 1;
1649 setCursorFromCoordinates(x, y);
1651 int topy = bv_owner->top_y();
1652 int y1 = cursor.y() - topy;
1655 InsetOld * inset_hit = checkInsetHit(x, y1);
1656 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1657 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1658 inset_hit->dispatch(cmd);
1662 setCursorFromCoordinates(cursor.x_fix(),
1663 cursor.y() - crow.baseline() + crow.height() + 1);
1668 void LyXText::cursorUpParagraph()
1670 ParagraphList::iterator cpit = cursorPar();
1671 if (cursor.pos() > 0)
1673 else if (cpit != ownerParagraphs().begin())
1674 setCursor(boost::prior(cpit), 0);
1678 void LyXText::cursorDownParagraph()
1680 ParagraphList::iterator pit = cursorPar();
1681 ParagraphList::iterator next_pit = boost::next(pit);
1683 if (next_pit != ownerParagraphs().end())
1684 setCursor(next_pit, 0);
1686 setCursor(pit, pit->size());
1690 // fix the cursor `cur' after a characters has been deleted at `where'
1691 // position. Called by deleteEmptyParagraphMechanism
1692 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1694 // if cursor is not in the paragraph where the delete occured,
1696 if (cur.par() != where.par())
1699 // if cursor position is after the place where the delete occured,
1701 if (cur.pos() > where.pos())
1702 cur.pos(cur.pos()-1);
1704 // check also if we don't want to set the cursor on a spot behind the
1705 // pagragraph because we erased the last character.
1706 if (cur.pos() > getPar(cur)->size())
1707 cur.pos(getPar(cur)->size());
1709 // recompute row et al. for this cursor
1710 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1714 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1716 // Would be wrong to delete anything if we have a selection.
1717 if (selection.set())
1720 // Don't do anything if the cursor is invalid
1721 if (old_cursor.par() == -1)
1724 // We allow all kinds of "mumbo-jumbo" when freespacing.
1725 ParagraphList::iterator const old_pit = getPar(old_cursor);
1726 if (old_pit->isFreeSpacing())
1729 /* Ok I'll put some comments here about what is missing.
1730 I have fixed BackSpace (and thus Delete) to not delete
1731 double-spaces automagically. I have also changed Cut,
1732 Copy and Paste to hopefully do some sensible things.
1733 There are still some small problems that can lead to
1734 double spaces stored in the document file or space at
1735 the beginning of paragraphs. This happens if you have
1736 the cursor between to spaces and then save. Or if you
1737 cut and paste and the selection have a space at the
1738 beginning and then save right after the paste. I am
1739 sure none of these are very hard to fix, but I will
1740 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1741 that I can get some feedback. (Lgb)
1744 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1745 // delete the LineSeparator.
1748 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1749 // delete the LineSeparator.
1752 // If the pos around the old_cursor were spaces, delete one of them.
1753 if (old_cursor.par() != cursor.par()
1754 || old_cursor.pos() != cursor.pos()) {
1756 // Only if the cursor has really moved
1757 if (old_cursor.pos() > 0
1758 && old_cursor.pos() < old_pit->size()
1759 && old_pit->isLineSeparator(old_cursor.pos())
1760 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1761 bool erased = old_pit->erase(old_cursor.pos() - 1);
1762 redoParagraph(old_pit);
1766 #ifdef WITH_WARNINGS
1767 #warning This will not work anymore when we have multiple views of the same buffer
1768 // In this case, we will have to correct also the cursors held by
1769 // other bufferviews. It will probably be easier to do that in a more
1770 // automated way in LyXCursor code. (JMarc 26/09/2001)
1772 // correct all cursors held by the LyXText
1773 fixCursorAfterDelete(cursor, old_cursor);
1774 fixCursorAfterDelete(selection.cursor, old_cursor);
1775 fixCursorAfterDelete(selection.start, old_cursor);
1776 fixCursorAfterDelete(selection.end, old_cursor);
1781 // don't delete anything if this is the ONLY paragraph!
1782 if (ownerParagraphs().size() == 1)
1785 // Do not delete empty paragraphs with keepempty set.
1786 if (old_pit->allowEmpty())
1789 // only do our magic if we changed paragraph
1790 if (old_cursor.par() == cursor.par())
1793 // record if we have deleted a paragraph
1794 // we can't possibly have deleted a paragraph before this point
1795 bool deleted = false;
1797 if (old_pit->empty() ||
1798 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1799 // ok, we will delete something
1800 LyXCursor tmpcursor;
1804 bool selection_position_was_oldcursor_position =
1805 selection.cursor.par() == old_cursor.par()
1806 && selection.cursor.pos() == old_cursor.pos();
1809 cursor = old_cursor; // that undo can restore the right cursor position
1811 ParagraphList::iterator endpit = boost::next(old_pit);
1812 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1815 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1819 ownerParagraphs().erase(old_pit);
1823 setCursorIntern(cursor.par(), cursor.pos());
1825 if (selection_position_was_oldcursor_position) {
1826 // correct selection
1827 selection.cursor = cursor;
1831 if (old_pit->stripLeadingSpaces()) {
1832 redoParagraph(old_pit);
1834 setCursorIntern(cursor.par(), cursor.pos());
1835 selection.cursor = cursor;
1842 ParagraphList & LyXText::ownerParagraphs() const
1844 return *paragraphs_;
1848 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1850 recordUndo(Undo::ATOMIC, this, first, last);
1854 void LyXText::recUndo(lyx::paroffset_type par) const
1856 recordUndo(Undo::ATOMIC, this, par, par);
1860 bool LyXText::isInInset() const
1862 // Sub-level has non-null bv owner and non-null inset owner.
1863 return inset_owner != 0;
1867 int defaultRowHeight()
1869 LyXFont const font(LyXFont::ALL_SANE);
1870 return int(font_metrics::maxAscent(font)
1871 + font_metrics::maxDescent(font) * 1.5);