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 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 float x = getCursorX(pit, row, pos, boundary);
1348 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1349 pos_type pos, bool boundary) const
1351 pos_type cursor_vpos = 0;
1353 double fill_separator = row.fill_separator();
1354 double fill_hfill = row.fill_hfill();
1355 double fill_label_hfill = row.fill_label_hfill();
1356 pos_type const row_pos = row.pos();
1357 pos_type const end = row.endpos();
1360 cursor_vpos = row_pos;
1361 else if (pos >= end && !boundary)
1362 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1364 else if (pos > row_pos && (pos >= end || boundary))
1365 // Place cursor after char at (logical) position pos - 1
1366 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1367 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1369 // Place cursor before char at (logical) position pos
1370 cursor_vpos = (bidi.level(pos) % 2 == 0)
1371 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1373 pos_type body_pos = pit->beginningOfBody();
1375 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1378 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1379 pos_type pos = bidi.vis2log(vpos);
1380 if (body_pos > 0 && pos == body_pos - 1) {
1381 x += fill_label_hfill
1382 + font_metrics::width(pit->layout()->labelsep,
1384 if (pit->isLineSeparator(body_pos - 1))
1385 x -= singleWidth(pit, body_pos - 1);
1388 if (hfillExpansion(*pit, row, pos)) {
1389 x += singleWidth(pit, pos);
1390 if (pos >= body_pos)
1393 x += fill_label_hfill;
1394 } else if (pit->isSeparator(pos)) {
1395 x += singleWidth(pit, pos);
1396 if (pos >= body_pos)
1397 x += fill_separator;
1399 x += singleWidth(pit, pos);
1405 void LyXText::setCursorIntern(paroffset_type par,
1406 pos_type pos, bool setfont, bool boundary)
1408 setCursor(cursor, par, pos, boundary);
1414 void LyXText::setCurrentFont()
1416 pos_type pos = cursor.pos();
1417 ParagraphList::iterator pit = cursorPar();
1419 if (cursor.boundary() && pos > 0)
1423 if (pos == pit->size())
1425 else // potentional bug... BUG (Lgb)
1426 if (pit->isSeparator(pos)) {
1427 if (pos > pit->getRow(pos)->pos() &&
1428 bidi.level(pos) % 2 ==
1429 bidi.level(pos - 1) % 2)
1431 else if (pos + 1 < pit->size())
1436 BufferParams const & bufparams = bv()->buffer()->params();
1437 current_font = pit->getFontSettings(bufparams, pos);
1438 real_current_font = getFont(pit, pos);
1440 if (cursor.pos() == pit->size() &&
1441 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1442 !cursor.boundary()) {
1443 Language const * lang =
1444 pit->getParLanguage(bufparams);
1445 current_font.setLanguage(lang);
1446 current_font.setNumber(LyXFont::OFF);
1447 real_current_font.setLanguage(lang);
1448 real_current_font.setNumber(LyXFont::OFF);
1453 // returns the column near the specified x-coordinate of the row
1454 // x is set to the real beginning of this column
1455 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1456 Row const & row, int & x, bool & boundary) const
1458 double tmpx = row.x();
1459 double fill_separator = row.fill_separator();
1460 double fill_hfill = row.fill_hfill();
1461 double fill_label_hfill = row.fill_label_hfill();
1463 pos_type vc = row.pos();
1464 pos_type end = row.endpos();
1466 LyXLayout_ptr const & layout = pit->layout();
1468 bool left_side = false;
1470 pos_type body_pos = pit->beginningOfBody();
1471 double last_tmpx = tmpx;
1475 !pit->isLineSeparator(body_pos - 1)))
1478 // check for empty row
1484 while (vc < end && tmpx <= x) {
1485 c = bidi.vis2log(vc);
1487 if (body_pos > 0 && c == body_pos - 1) {
1488 tmpx += fill_label_hfill +
1489 font_metrics::width(layout->labelsep, getLabelFont(pit));
1490 if (pit->isLineSeparator(body_pos - 1))
1491 tmpx -= singleWidth(pit, body_pos - 1);
1494 if (hfillExpansion(*pit, row, c)) {
1495 tmpx += singleWidth(pit, c);
1499 tmpx += fill_label_hfill;
1500 } else if (pit->isSeparator(c)) {
1501 tmpx += singleWidth(pit, c);
1503 tmpx += fill_separator;
1505 tmpx += singleWidth(pit, c);
1510 if ((tmpx + last_tmpx) / 2 > x) {
1515 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1518 // This (rtl_support test) is not needed, but gives
1519 // some speedup if rtl_support == false
1520 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1522 // If lastrow is false, we don't need to compute
1523 // the value of rtl.
1524 bool const rtl = (lastrow)
1525 ? pit->isRightToLeftPar(bv()->buffer()->params())
1528 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1529 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1531 else if (vc == row.pos()) {
1532 c = bidi.vis2log(vc);
1533 if (bidi.level(c) % 2 == 1)
1536 c = bidi.vis2log(vc - 1);
1537 bool const rtl = (bidi.level(c) % 2 == 1);
1538 if (left_side == rtl) {
1540 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1544 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1545 if (bidi.level(end -1) % 2 == 0)
1546 tmpx -= singleWidth(pit, end - 1);
1548 tmpx += singleWidth(pit, end - 1);
1558 void LyXText::setCursorFromCoordinates(int x, int y)
1560 LyXCursor old_cursor = cursor;
1561 setCursorFromCoordinates(cursor, x, y);
1563 deleteEmptyParagraphMechanism(old_cursor);
1567 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1569 // Get the row first.
1570 ParagraphList::iterator pit;
1571 Row const & row = *getRowNearY(y, pit);
1572 y = pit->y + row.y_offset();
1575 pos_type const column = getColumnNearX(pit, row, x, bound);
1576 cur.par(parOffset(pit));
1577 cur.pos(row.pos() + column);
1579 cur.y(y + row.baseline());
1581 cur.boundary(bound);
1585 void LyXText::cursorLeft(bool internal)
1587 if (cursor.pos() > 0) {
1588 bool boundary = cursor.boundary();
1589 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1590 if (!internal && !boundary &&
1591 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1592 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1593 } else if (cursor.par() != 0) {
1594 // steps into the paragraph above
1595 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1600 void LyXText::cursorRight(bool internal)
1602 bool const at_end = (cursor.pos() == cursorPar()->size());
1603 bool const at_newline = !at_end &&
1604 cursorPar()->isNewline(cursor.pos());
1606 if (!internal && cursor.boundary() && !at_newline)
1607 setCursor(cursor.par(), cursor.pos(), true, false);
1609 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1610 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1612 setCursor(cursor.par(), cursor.pos(), true, true);
1613 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1614 setCursor(cursor.par() + 1, 0);
1618 void LyXText::cursorUp(bool selecting)
1620 ParagraphList::iterator cpit = cursorPar();
1621 Row const & crow = *cpit->getRow(cursor.pos());
1623 int x = cursor.x_fix();
1624 int y = cursor.y() - crow.baseline() - 1;
1625 setCursorFromCoordinates(x, y);
1627 int topy = bv_owner->top_y();
1628 int y1 = cursor.y() - topy;
1631 InsetOld * inset_hit = checkInsetHit(x, y1);
1632 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1633 inset_hit->dispatch(
1634 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1638 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1639 crow.baseline() << endl;
1640 setCursorFromCoordinates(cursor.x_fix(),
1641 cursor.y() - crow.baseline() - 1);
1646 void LyXText::cursorDown(bool selecting)
1648 ParagraphList::iterator cpit = cursorPar();
1649 Row const & crow = *cpit->getRow(cursor.pos());
1651 int x = cursor.x_fix();
1652 int y = cursor.y() - crow.baseline() + crow.height() + 1;
1653 setCursorFromCoordinates(x, y);
1655 int topy = bv_owner->top_y();
1656 int y1 = cursor.y() - topy;
1659 InsetOld * inset_hit = checkInsetHit(x, y1);
1660 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1661 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1662 inset_hit->dispatch(cmd);
1666 setCursorFromCoordinates(cursor.x_fix(),
1667 cursor.y() - crow.baseline() + crow.height() + 1);
1672 void LyXText::cursorUpParagraph()
1674 ParagraphList::iterator cpit = cursorPar();
1675 if (cursor.pos() > 0)
1677 else if (cpit != ownerParagraphs().begin())
1678 setCursor(boost::prior(cpit), 0);
1682 void LyXText::cursorDownParagraph()
1684 ParagraphList::iterator pit = cursorPar();
1685 ParagraphList::iterator next_pit = boost::next(pit);
1687 if (next_pit != ownerParagraphs().end())
1688 setCursor(next_pit, 0);
1690 setCursor(pit, pit->size());
1694 // fix the cursor `cur' after a characters has been deleted at `where'
1695 // position. Called by deleteEmptyParagraphMechanism
1696 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1698 // if cursor is not in the paragraph where the delete occured,
1700 if (cur.par() != where.par())
1703 // if cursor position is after the place where the delete occured,
1705 if (cur.pos() > where.pos())
1706 cur.pos(cur.pos()-1);
1708 // check also if we don't want to set the cursor on a spot behind the
1709 // pagragraph because we erased the last character.
1710 if (cur.pos() > getPar(cur)->size())
1711 cur.pos(getPar(cur)->size());
1713 // recompute row et al. for this cursor
1714 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1718 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1720 // Would be wrong to delete anything if we have a selection.
1721 if (selection.set())
1724 // Don't do anything if the cursor is invalid
1725 if (old_cursor.par() == -1)
1728 // We allow all kinds of "mumbo-jumbo" when freespacing.
1729 ParagraphList::iterator const old_pit = getPar(old_cursor);
1730 if (old_pit->isFreeSpacing())
1733 /* Ok I'll put some comments here about what is missing.
1734 I have fixed BackSpace (and thus Delete) to not delete
1735 double-spaces automagically. I have also changed Cut,
1736 Copy and Paste to hopefully do some sensible things.
1737 There are still some small problems that can lead to
1738 double spaces stored in the document file or space at
1739 the beginning of paragraphs. This happens if you have
1740 the cursor between to spaces and then save. Or if you
1741 cut and paste and the selection have a space at the
1742 beginning and then save right after the paste. I am
1743 sure none of these are very hard to fix, but I will
1744 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1745 that I can get some feedback. (Lgb)
1748 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1749 // delete the LineSeparator.
1752 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1753 // delete the LineSeparator.
1756 // If the pos around the old_cursor were spaces, delete one of them.
1757 if (old_cursor.par() != cursor.par()
1758 || old_cursor.pos() != cursor.pos()) {
1760 // Only if the cursor has really moved
1761 if (old_cursor.pos() > 0
1762 && old_cursor.pos() < old_pit->size()
1763 && old_pit->isLineSeparator(old_cursor.pos())
1764 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1765 bool erased = old_pit->erase(old_cursor.pos() - 1);
1766 redoParagraph(old_pit);
1770 #ifdef WITH_WARNINGS
1771 #warning This will not work anymore when we have multiple views of the same buffer
1772 // In this case, we will have to correct also the cursors held by
1773 // other bufferviews. It will probably be easier to do that in a more
1774 // automated way in LyXCursor code. (JMarc 26/09/2001)
1776 // correct all cursors held by the LyXText
1777 fixCursorAfterDelete(cursor, old_cursor);
1778 fixCursorAfterDelete(selection.cursor, old_cursor);
1779 fixCursorAfterDelete(selection.start, old_cursor);
1780 fixCursorAfterDelete(selection.end, old_cursor);
1785 // don't delete anything if this is the ONLY paragraph!
1786 if (ownerParagraphs().size() == 1)
1789 // Do not delete empty paragraphs with keepempty set.
1790 if (old_pit->allowEmpty())
1793 // only do our magic if we changed paragraph
1794 if (old_cursor.par() == cursor.par())
1797 // record if we have deleted a paragraph
1798 // we can't possibly have deleted a paragraph before this point
1799 bool deleted = false;
1801 if (old_pit->empty() ||
1802 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1803 // ok, we will delete something
1804 LyXCursor tmpcursor;
1808 bool selection_position_was_oldcursor_position =
1809 selection.cursor.par() == old_cursor.par()
1810 && selection.cursor.pos() == old_cursor.pos();
1813 cursor = old_cursor; // that undo can restore the right cursor position
1815 ParagraphList::iterator endpit = boost::next(old_pit);
1816 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1819 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1823 ownerParagraphs().erase(old_pit);
1827 setCursorIntern(cursor.par(), cursor.pos());
1829 if (selection_position_was_oldcursor_position) {
1830 // correct selection
1831 selection.cursor = cursor;
1835 if (old_pit->stripLeadingSpaces()) {
1836 redoParagraph(old_pit);
1838 setCursorIntern(cursor.par(), cursor.pos());
1839 selection.cursor = cursor;
1846 ParagraphList & LyXText::ownerParagraphs() const
1848 return *paragraphs_;
1852 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1854 recordUndo(Undo::ATOMIC, this, first, last);
1858 void LyXText::recUndo(lyx::paroffset_type par) const
1860 recordUndo(Undo::ATOMIC, this, par, par);
1864 bool LyXText::isInInset() const
1866 // Sub-level has non-null bv owner and non-null inset owner.
1867 return inset_owner != 0;
1871 int defaultRowHeight()
1873 LyXFont const font(LyXFont::ALL_SANE);
1874 return int(font_metrics::maxAscent(font)
1875 + font_metrics::maxDescent(font) * 1.5);