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();
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 if (layout->labeltype == LABEL_MANUAL
120 && pos < pit->beginningOfBody()) {
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->reslabelfont);
127 LyXFont f = pit->getFontSettings(params, pos);
129 pit->inInset()->getDrawFont(f);
130 return f.realize(layout->resfont);
134 // The uncommon case need not be optimized as much
138 if (pos < pit->beginningOfBody()) {
140 layoutfont = layout->labelfont;
143 layoutfont = layout->font;
146 LyXFont tmpfont = pit->getFontSettings(params, pos);
147 tmpfont.realize(layoutfont);
150 pit->inInset()->getDrawFont(tmpfont);
152 // Realize with the fonts of lesser depth.
153 tmpfont.realize(outerFont(pit, ownerParagraphs()));
154 tmpfont.realize(defaultfont_);
160 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
162 LyXLayout_ptr const & layout = pit->layout();
164 if (!pit->getDepth())
165 return layout->resfont;
167 LyXFont font = layout->font;
168 // Realize with the fonts of lesser depth.
169 font.realize(outerFont(pit, ownerParagraphs()));
170 font.realize(defaultfont_);
176 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
178 LyXLayout_ptr const & layout = pit->layout();
180 if (!pit->getDepth())
181 return layout->reslabelfont;
183 LyXFont font = layout->labelfont;
184 // Realize with the fonts of lesser depth.
185 font.realize(outerFont(pit, ownerParagraphs()));
186 font.realize(defaultfont_);
192 void LyXText::setCharFont(ParagraphList::iterator pit,
193 pos_type pos, LyXFont const & fnt,
196 BufferParams const & params = bv()->buffer()->params();
197 LyXFont font = getFont(pit, pos);
198 font.update(fnt, params.language, toggleall);
199 // Let the insets convert their font
200 if (pit->isInset(pos)) {
201 InsetOld * inset = pit->getInset(pos);
202 if (isEditableInset(inset)) {
203 static_cast<UpdatableInset *>(inset)
204 ->setFont(bv(), fnt, toggleall, true);
208 // Plug through to version below:
209 setCharFont(pit, pos, font);
213 void LyXText::setCharFont(
214 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
217 LyXLayout_ptr const & layout = pit->layout();
219 // Get concrete layout font to reduce against
222 if (pos < pit->beginningOfBody())
223 layoutfont = layout->labelfont;
225 layoutfont = layout->font;
227 // Realize against environment font information
228 if (pit->getDepth()) {
229 ParagraphList::iterator tp = pit;
230 while (!layoutfont.resolved() &&
231 tp != ownerParagraphs().end() &&
233 tp = outerHook(tp, ownerParagraphs());
234 if (tp != ownerParagraphs().end())
235 layoutfont.realize(tp->layout()->font);
239 layoutfont.realize(defaultfont_);
241 // Now, reduce font against full layout font
242 font.reduce(layoutfont);
244 pit->setFont(pos, font);
248 InsetOld * LyXText::getInset() const
250 ParagraphList::iterator pit = cursorPar();
251 pos_type const pos = cursor.pos();
253 if (pos < pit->size() && pit->isInset(pos)) {
254 return pit->getInset(pos);
260 void LyXText::toggleInset()
262 InsetOld * inset = getInset();
263 // is there an editable inset at cursor position?
264 if (!isEditableInset(inset)) {
265 // No, try to see if we are inside a collapsable inset
266 if (inset_owner && inset_owner->owner()
267 && inset_owner->owner()->isOpen()) {
268 bv()->unlockInset(inset_owner->owner());
269 inset_owner->owner()->close(bv());
270 bv()->getLyXText()->cursorRight(bv());
274 //bv()->owner()->message(inset->editMessage());
276 // do we want to keep this?? (JMarc)
277 if (!isHighlyEditableInset(inset))
278 recUndo(cursor.par());
285 bv()->updateInset(inset);
289 /* used in setlayout */
290 // Asger is not sure we want to do this...
291 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
294 LyXLayout_ptr const & layout = par.layout();
295 pos_type const psize = par.size();
298 for (pos_type pos = 0; pos < psize; ++pos) {
299 if (pos < par.beginningOfBody())
300 layoutfont = layout->labelfont;
302 layoutfont = layout->font;
304 LyXFont tmpfont = par.getFontSettings(params, pos);
305 tmpfont.reduce(layoutfont);
306 par.setFont(pos, tmpfont);
311 ParagraphList::iterator
312 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
313 LyXCursor & send_cur,
314 string const & layout)
316 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
317 ParagraphList::iterator undoendpit = endpit;
318 ParagraphList::iterator pars_end = ownerParagraphs().end();
320 if (endpit != pars_end && endpit->getDepth()) {
321 while (endpit != pars_end && endpit->getDepth()) {
325 } else if (endpit != pars_end) {
326 // because of parindents etc.
330 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
332 // ok we have a selection. This is always between sstart_cur
333 // and sel_end cursor
335 ParagraphList::iterator pit = getPar(sstart_cur);
336 ParagraphList::iterator epit = boost::next(getPar(send_cur));
338 BufferParams const & bufparams = bv()->buffer()->params();
339 LyXLayout_ptr const & lyxlayout =
340 bufparams.getLyXTextClass()[layout];
343 pit->applyLayout(lyxlayout);
344 makeFontEntriesLayoutSpecific(bufparams, *pit);
345 pit->params().spaceTop(lyxlayout->fill_top ?
346 VSpace(VSpace::VFILL)
347 : VSpace(VSpace::NONE));
348 pit->params().spaceBottom(lyxlayout->fill_bottom ?
349 VSpace(VSpace::VFILL)
350 : VSpace(VSpace::NONE));
351 if (lyxlayout->margintype == MARGIN_MANUAL)
352 pit->setLabelWidthString(lyxlayout->labelstring());
353 cur.par(std::distance(ownerParagraphs().begin(), pit));
355 } while (pit != epit);
361 // set layout over selection and make a total rebreak of those paragraphs
362 void LyXText::setLayout(string const & layout)
364 LyXCursor tmpcursor = cursor; // store the current cursor
366 // if there is no selection just set the layout
367 // of the current paragraph
368 if (!selection.set()) {
369 selection.start = cursor; // dummy selection
370 selection.end = cursor;
373 // special handling of new environment insets
374 BufferParams const & params = bv()->buffer()->params();
375 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
376 if (lyxlayout->is_environment) {
377 // move everything in a new environment inset
378 lyxerr << "setting layout " << layout << endl;
379 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
380 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
381 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
382 InsetOld * inset = new InsetEnvironment(params, layout);
383 if (bv()->insertInset(inset)) {
385 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
392 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
393 selection.end, layout);
394 redoParagraphs(getPar(selection.start), endpit);
396 // we have to reset the selection, because the
397 // geometry could have changed
398 setCursor(selection.start.par(), selection.start.pos(), false);
399 selection.cursor = cursor;
400 setCursor(selection.end.par(), selection.end.pos(), false);
404 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
408 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
410 ParagraphList::iterator pit = cursorPar();
411 ParagraphList::iterator end = cursorPar();
412 ParagraphList::iterator start = pit;
414 if (selection.set()) {
415 pit = getPar(selection.start);
416 end = getPar(selection.end);
420 ParagraphList::iterator pastend = boost::next(end);
423 recUndo(parOffset(start), parOffset(end));
425 bool changed = false;
427 int prev_after_depth = 0;
428 #warning parlist ... could be nicer ?
429 if (start != ownerParagraphs().begin()) {
430 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
434 int const depth = pit->params().depth();
435 if (type == bv_funcs::INC_DEPTH) {
436 if (depth < prev_after_depth
437 && pit->layout()->labeltype != LABEL_BIBLIO) {
440 pit->params().depth(depth + 1);
445 pit->params().depth(depth - 1);
448 prev_after_depth = pit->getMaxDepthAfter();
460 redoParagraphs(start, pastend);
462 // We need to actually move the text->cursor. I don't
463 // understand why ...
464 LyXCursor tmpcursor = cursor;
466 // we have to reset the visual selection because the
467 // geometry could have changed
468 if (selection.set()) {
469 setCursor(selection.start.par(), selection.start.pos());
470 selection.cursor = cursor;
471 setCursor(selection.end.par(), selection.end.pos());
474 // this handles the counter labels, and also fixes up
475 // depth values for follow-on (child) paragraphs
479 setCursor(tmpcursor.par(), tmpcursor.pos());
485 // set font over selection and make a total rebreak of those paragraphs
486 void LyXText::setFont(LyXFont const & font, bool toggleall)
488 // if there is no selection just set the current_font
489 if (!selection.set()) {
490 // Determine basis font
492 if (cursor.pos() < cursorPar()->beginningOfBody()) {
493 layoutfont = getLabelFont(cursorPar());
495 layoutfont = getLayoutFont(cursorPar());
497 // Update current font
498 real_current_font.update(font,
499 bv()->buffer()->params().language,
502 // Reduce to implicit settings
503 current_font = real_current_font;
504 current_font.reduce(layoutfont);
505 // And resolve it completely
506 real_current_font.realize(layoutfont);
511 LyXCursor tmpcursor = cursor; // store the current cursor
513 // ok we have a selection. This is always between sel_start_cursor
514 // and sel_end cursor
516 recUndo(selection.start.par(), selection.end.par());
518 cursor = selection.start;
519 while (cursor.par() != selection.end.par() ||
520 cursor.pos() < selection.end.pos())
522 if (cursor.pos() < cursorPar()->size()) {
523 // an open footnote should behave like a closed one
524 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
525 cursor.pos(cursor.pos() + 1);
528 cursor.par(cursor.par() + 1);
533 redoParagraph(getPar(selection.start));
535 // we have to reset the selection, because the
536 // geometry could have changed, but we keep
537 // it for user convenience
538 setCursor(selection.start.par(), selection.start.pos());
539 selection.cursor = cursor;
540 setCursor(selection.end.par(), selection.end.pos());
542 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
543 tmpcursor.boundary());
547 // important for the screen
550 // the cursor set functions have a special mechanism. When they
551 // realize, that you left an empty paragraph, they will delete it.
552 // They also delete the corresponding row
554 // need the selection cursor:
555 void LyXText::setSelection()
557 TextCursor::setSelection();
562 void LyXText::clearSelection()
564 TextCursor::clearSelection();
566 // reset this in the bv_owner!
567 if (bv_owner && bv_owner->text)
568 bv_owner->text->xsel_cache.set(false);
572 void LyXText::cursorHome()
574 setCursor(cursorPar(), cursorRow()->pos());
578 void LyXText::cursorEnd()
580 setCursor(cursorPar(), cursorRow()->end() - 1);
584 void LyXText::cursorTop()
586 setCursor(ownerParagraphs().begin(), 0);
590 void LyXText::cursorBottom()
592 ParagraphList::iterator lastpit =
593 boost::prior(ownerParagraphs().end());
594 setCursor(lastpit, lastpit->size());
598 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
600 // If the mask is completely neutral, tell user
601 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
602 // Could only happen with user style
603 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
607 // Try implicit word selection
608 // If there is a change in the language the implicit word selection
610 LyXCursor resetCursor = cursor;
611 bool implicitSelection = (font.language() == ignore_language
612 && font.number() == LyXFont::IGNORE)
613 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
616 setFont(font, toggleall);
618 // Implicit selections are cleared afterwards
619 //and cursor is set to the original position.
620 if (implicitSelection) {
622 cursor = resetCursor;
623 setCursor(cursorPar(), cursor.pos());
624 selection.cursor = cursor;
629 string LyXText::getStringToIndex()
631 // Try implicit word selection
632 // If there is a change in the language the implicit word selection
634 LyXCursor const reset_cursor = cursor;
635 bool const implicitSelection =
636 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
639 if (!selection.set())
640 bv()->owner()->message(_("Nothing to index!"));
641 else if (selection.start.par() != selection.end.par())
642 bv()->owner()->message(_("Cannot index more than one paragraph!"));
644 idxstring = selectionAsString(*bv()->buffer(), false);
646 // Reset cursors to their original position.
647 cursor = reset_cursor;
648 setCursor(cursorPar(), cursor.pos());
649 selection.cursor = cursor;
651 // Clear the implicit selection.
652 if (implicitSelection)
659 // the DTP switches for paragraphs. LyX will store them in the first
660 // physical paragraph. When a paragraph is broken, the top settings rest,
661 // the bottom settings are given to the new one. So I can make sure,
662 // they do not duplicate themself and you cannnot make dirty things with
665 void LyXText::setParagraph(bool line_top, bool line_bottom,
666 bool pagebreak_top, bool pagebreak_bottom,
667 VSpace const & space_top,
668 VSpace const & space_bottom,
669 Spacing const & spacing,
671 string const & labelwidthstring,
674 LyXCursor tmpcursor = cursor;
675 if (!selection.set()) {
676 selection.start = cursor;
677 selection.end = cursor;
680 // make sure that the depth behind the selection are restored, too
681 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
682 ParagraphList::iterator undoendpit = endpit;
683 ParagraphList::iterator pars_end = ownerParagraphs().end();
685 if (endpit != pars_end && endpit->getDepth()) {
686 while (endpit != pars_end && endpit->getDepth()) {
690 } else if (endpit != pars_end) {
691 // because of parindents etc.
695 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
698 int tmppit = selection.end.par();
700 while (tmppit != selection.start.par() - 1) {
701 setCursor(tmppit, 0);
703 ParagraphList::iterator const pit = cursorPar();
704 ParagraphParameters & params = pit->params();
706 params.lineTop(line_top);
707 params.lineBottom(line_bottom);
708 params.pagebreakTop(pagebreak_top);
709 params.pagebreakBottom(pagebreak_bottom);
710 params.spaceTop(space_top);
711 params.spaceBottom(space_bottom);
712 params.spacing(spacing);
713 // does the layout allow the new alignment?
714 LyXLayout_ptr const & layout = pit->layout();
716 if (align == LYX_ALIGN_LAYOUT)
717 align = layout->align;
718 if (align & layout->alignpossible) {
719 if (align == layout->align)
720 params.align(LYX_ALIGN_LAYOUT);
724 pit->setLabelWidthString(labelwidthstring);
725 params.noindent(noindent);
729 redoParagraphs(getPar(selection.start), endpit);
732 setCursor(selection.start.par(), selection.start.pos());
733 selection.cursor = cursor;
734 setCursor(selection.end.par(), selection.end.pos());
736 setCursor(tmpcursor.par(), tmpcursor.pos());
738 bv()->updateInset(inset_owner);
744 string expandLabel(LyXTextClass const & textclass,
745 LyXLayout_ptr const & layout, bool appendix)
747 string fmt = appendix ?
748 layout->labelstring_appendix() : layout->labelstring();
750 // handle 'inherited level parts' in 'fmt',
751 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
752 size_t const i = fmt.find('@', 0);
753 if (i != string::npos) {
754 size_t const j = fmt.find('@', i + 1);
755 if (j != string::npos) {
756 string parent(fmt, i + 1, j - i - 1);
757 string label = expandLabel(textclass, textclass[parent], appendix);
758 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
762 return textclass.counters().counterLabel(fmt);
766 void incrementItemDepth(ParagraphList::iterator pit,
767 ParagraphList::iterator first_pit)
769 int const cur_labeltype = pit->layout()->labeltype;
771 if (cur_labeltype != LABEL_ENUMERATE &&
772 cur_labeltype != LABEL_ITEMIZE)
775 int const cur_depth = pit->getDepth();
777 ParagraphList::iterator prev_pit = boost::prior(pit);
779 int const prev_depth = prev_pit->getDepth();
780 int const prev_labeltype = prev_pit->layout()->labeltype;
781 if (prev_depth == 0 && cur_depth > 0) {
782 if (prev_labeltype == cur_labeltype) {
783 pit->itemdepth = prev_pit->itemdepth + 1;
786 } else if (prev_depth < cur_depth) {
787 if (prev_labeltype == cur_labeltype) {
788 pit->itemdepth = prev_pit->itemdepth + 1;
791 } else if (prev_depth == cur_depth) {
792 if (prev_labeltype == cur_labeltype) {
793 pit->itemdepth = prev_pit->itemdepth;
797 if (prev_pit == first_pit)
805 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
806 ParagraphList::iterator firstpit,
812 int const cur_depth = pit->getDepth();
813 ParagraphList::iterator prev_pit = boost::prior(pit);
815 int const prev_depth = prev_pit->getDepth();
816 int const prev_labeltype = prev_pit->layout()->labeltype;
817 if (prev_depth <= cur_depth) {
818 if (prev_labeltype != LABEL_ENUMERATE) {
819 switch (pit->itemdepth) {
821 counters.reset("enumi");
823 counters.reset("enumii");
825 counters.reset("enumiii");
827 counters.reset("enumiv");
833 if (prev_pit == firstpit)
843 // set the counter of a paragraph. This includes the labels
844 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
846 BufferParams const & bufparams = buf.params();
847 LyXTextClass const & textclass = bufparams.getLyXTextClass();
848 LyXLayout_ptr const & layout = pit->layout();
849 ParagraphList::iterator first_pit = ownerParagraphs().begin();
850 Counters & counters = textclass.counters();
855 if (pit == first_pit) {
856 pit->params().appendix(pit->params().startOfAppendix());
858 pit->params().appendix(boost::prior(pit)->params().appendix());
859 if (!pit->params().appendix() &&
860 pit->params().startOfAppendix()) {
861 pit->params().appendix(true);
862 textclass.counters().reset();
865 // Maybe we have to increment the item depth.
866 incrementItemDepth(pit, first_pit);
869 // erase what was there before
870 pit->params().labelString(string());
872 if (layout->margintype == MARGIN_MANUAL) {
873 if (pit->params().labelWidthString().empty())
874 pit->setLabelWidthString(layout->labelstring());
876 pit->setLabelWidthString(string());
879 // is it a layout that has an automatic label?
880 if (layout->labeltype == LABEL_COUNTER) {
881 BufferParams const & bufparams = buf.params();
882 LyXTextClass const & textclass = bufparams.getLyXTextClass();
883 counters.step(layout->counter);
884 string label = expandLabel(textclass, layout, pit->params().appendix());
885 pit->params().labelString(label);
886 } else if (layout->labeltype == LABEL_ITEMIZE) {
887 // At some point of time we should do something more
888 // clever here, like:
889 // pit->params().labelString(
890 // bufparams.user_defined_bullet(pit->itemdepth).getText());
891 // for now, use a simple hardcoded label
893 switch (pit->itemdepth) {
908 pit->params().labelString(itemlabel);
909 } else if (layout->labeltype == LABEL_ENUMERATE) {
910 // Maybe we have to reset the enumeration counter.
911 resetEnumCounterIfNeeded(pit, first_pit, counters);
914 // Yes I know this is a really, really! bad solution
916 string enumcounter = "enum";
918 switch (pit->itemdepth) {
930 // not a valid enumdepth...
934 counters.step(enumcounter);
936 pit->params().labelString(counters.enumLabel(enumcounter));
937 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
938 counters.step("bibitem");
939 int number = counters.value("bibitem");
940 if (pit->bibitem()) {
941 pit->bibitem()->setCounter(number);
942 pit->params().labelString(layout->labelstring());
944 // In biblio should't be following counters but...
946 string s = buf.B_(layout->labelstring());
949 if (layout->labeltype == LABEL_SENSITIVE) {
950 ParagraphList::iterator end = ownerParagraphs().end();
951 ParagraphList::iterator tmppit = pit;
954 while (tmppit != end && tmppit->inInset()
955 // the single '=' is intended below
956 && (in = tmppit->inInset()->owner()))
958 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
959 in->lyxCode() == InsetOld::WRAP_CODE) {
963 Paragraph const * owner = &ownerPar(buf, in);
965 for ( ; tmppit != end; ++tmppit)
966 if (&*tmppit == owner)
974 if (in->lyxCode() == InsetOld::FLOAT_CODE)
975 type = static_cast<InsetFloat*>(in)->params().type;
976 else if (in->lyxCode() == InsetOld::WRAP_CODE)
977 type = static_cast<InsetWrap*>(in)->params().type;
981 Floating const & fl = textclass.floats().getType(type);
983 counters.step(fl.type());
985 // Doesn't work... yet.
986 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
988 // par->SetLayout(0);
989 // s = layout->labelstring;
990 s = _("Senseless: ");
993 pit->params().labelString(s);
999 // Updates all counters. Paragraphs with changed label string will be rebroken
1000 void LyXText::updateCounters()
1003 bv()->buffer()->params().getLyXTextClass().counters().reset();
1005 ParagraphList::iterator beg = ownerParagraphs().begin();
1006 ParagraphList::iterator end = ownerParagraphs().end();
1007 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1008 string const oldLabel = pit->params().labelString();
1010 size_t maxdepth = 0;
1012 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1014 if (pit->params().depth() > maxdepth)
1015 pit->params().depth(maxdepth);
1017 // setCounter can potentially change the labelString.
1018 setCounter(*bv()->buffer(), pit);
1020 string const & newLabel = pit->params().labelString();
1022 if (oldLabel != newLabel)
1028 void LyXText::insertInset(InsetOld * inset)
1030 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1032 recUndo(cursor.par());
1034 cursorPar()->insertInset(cursor.pos(), inset);
1035 // Just to rebreak and refresh correctly.
1036 // The character will not be inserted a second time
1037 insertChar(Paragraph::META_INSET);
1038 // If we enter a highly editable inset the cursor should be before
1039 // the inset. After an Undo LyX tries to call inset->edit(...)
1040 // and fails if the cursor is behind the inset and getInset
1041 // does not return the inset!
1042 if (isHighlyEditableInset(inset))
1048 void LyXText::cutSelection(bool doclear, bool realcut)
1050 // Stuff what we got on the clipboard. Even if there is no selection.
1052 // There is a problem with having the stuffing here in that the
1053 // larger the selection the slower LyX will get. This can be
1054 // solved by running the line below only when the selection has
1055 // finished. The solution used currently just works, to make it
1056 // faster we need to be more clever and probably also have more
1057 // calls to stuffClipboard. (Lgb)
1058 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1060 // This doesn't make sense, if there is no selection
1061 if (!selection.set())
1064 // OK, we have a selection. This is always between selection.start
1065 // and selection.end
1067 // make sure that the depth behind the selection are restored, too
1068 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1069 ParagraphList::iterator undoendpit = endpit;
1070 ParagraphList::iterator pars_end = ownerParagraphs().end();
1072 if (endpit != pars_end && endpit->getDepth()) {
1073 while (endpit != pars_end && endpit->getDepth()) {
1075 undoendpit = endpit;
1077 } else if (endpit != pars_end) {
1078 // because of parindents etc.
1082 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1084 endpit = getPar(selection.end.par());
1085 int endpos = selection.end.pos();
1087 BufferParams const & bufparams = bv()->buffer()->params();
1088 boost::tie(endpit, endpos) = realcut ?
1089 CutAndPaste::cutSelection(bufparams,
1091 getPar(selection.start.par()), endpit,
1092 selection.start.pos(), endpos,
1093 bufparams.textclass,
1095 : CutAndPaste::eraseSelection(bufparams,
1097 getPar(selection.start.par()), endpit,
1098 selection.start.pos(), endpos,
1100 // sometimes necessary
1102 getPar(selection.start.par())->stripLeadingSpaces();
1104 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1105 // cutSelection can invalidate the cursor so we need to set
1107 // we prefer the end for when tracking changes
1109 cursor.par(parOffset(endpit));
1111 // need a valid cursor. (Lgb)
1114 setCursor(cursorPar(), cursor.pos());
1115 selection.cursor = cursor;
1120 void LyXText::copySelection()
1122 // stuff the selection onto the X clipboard, from an explicit copy request
1123 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1125 // this doesnt make sense, if there is no selection
1126 if (!selection.set())
1129 // ok we have a selection. This is always between selection.start
1130 // and sel_end cursor
1132 // copy behind a space if there is one
1133 while (getPar(selection.start)->size() > selection.start.pos()
1134 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1135 && (selection.start.par() != selection.end.par()
1136 || selection.start.pos() < selection.end.pos()))
1137 selection.start.pos(selection.start.pos() + 1);
1139 CutAndPaste::copySelection(getPar(selection.start.par()),
1140 getPar(selection.end.par()),
1141 selection.start.pos(), selection.end.pos(),
1142 bv()->buffer()->params().textclass);
1146 void LyXText::pasteSelection(size_t sel_index)
1148 // this does not make sense, if there is nothing to paste
1149 if (!CutAndPaste::checkPastePossible())
1152 recUndo(cursor.par());
1154 ParagraphList::iterator endpit;
1159 boost::tie(ppp, endpit) =
1160 CutAndPaste::pasteSelection(*bv()->buffer(),
1162 cursorPar(), cursor.pos(),
1163 bv()->buffer()->params().textclass,
1165 bufferErrors(*bv()->buffer(), el);
1166 bv()->showErrorList(_("Paste"));
1168 redoParagraphs(cursorPar(), endpit);
1170 setCursor(cursor.par(), cursor.pos());
1173 selection.cursor = cursor;
1174 setCursor(ppp.first, ppp.second);
1180 void LyXText::setSelectionRange(lyx::pos_type length)
1185 selection.cursor = cursor;
1192 // simple replacing. The font of the first selected character is used
1193 void LyXText::replaceSelectionWithString(string const & str)
1195 recUndo(cursor.par());
1198 if (!selection.set()) { // create a dummy selection
1199 selection.end = cursor;
1200 selection.start = cursor;
1203 // Get font setting before we cut
1204 pos_type pos = selection.end.pos();
1205 LyXFont const font = getPar(selection.start)
1206 ->getFontSettings(bv()->buffer()->params(),
1207 selection.start.pos());
1209 // Insert the new string
1210 string::const_iterator cit = str.begin();
1211 string::const_iterator end = str.end();
1212 for (; cit != end; ++cit) {
1213 getPar(selection.end)->insertChar(pos, (*cit), font);
1217 // Cut the selection
1218 cutSelection(true, false);
1224 // needed to insert the selection
1225 void LyXText::insertStringAsLines(string const & str)
1227 ParagraphList::iterator pit = cursorPar();
1228 pos_type pos = cursor.pos();
1229 ParagraphList::iterator endpit = boost::next(cursorPar());
1231 recUndo(cursor.par());
1233 // only to be sure, should not be neccessary
1236 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1238 redoParagraphs(cursorPar(), endpit);
1239 setCursor(cursorPar(), cursor.pos());
1240 selection.cursor = cursor;
1241 setCursor(pit, pos);
1246 // turns double-CR to single CR, others where converted into one
1247 // blank. Then InsertStringAsLines is called
1248 void LyXText::insertStringAsParagraphs(string const & str)
1250 string linestr(str);
1251 bool newline_inserted = false;
1252 string::size_type const siz = linestr.length();
1254 for (string::size_type i = 0; i < siz; ++i) {
1255 if (linestr[i] == '\n') {
1256 if (newline_inserted) {
1257 // we know that \r will be ignored by
1258 // InsertStringA. Of course, it is a dirty
1259 // trick, but it works...
1260 linestr[i - 1] = '\r';
1264 newline_inserted = true;
1266 } else if (IsPrintable(linestr[i])) {
1267 newline_inserted = false;
1270 insertStringAsLines(linestr);
1274 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1276 setCursor(parOffset(pit), pos);
1280 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1282 LyXCursor old_cursor = cursor;
1283 setCursorIntern(par, pos, setfont, boundary);
1284 return deleteEmptyParagraphMechanism(old_cursor);
1288 void LyXText::redoCursor()
1290 #warning maybe the same for selections?
1291 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1295 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1296 pos_type pos, bool boundary)
1298 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1302 cur.boundary(boundary);
1304 // no rows, no fun...
1305 if (ownerParagraphs().begin()->rows.empty())
1308 // get the cursor y position in text
1310 ParagraphList::iterator pit = getPar(par);
1311 Row const & row = *getRow(*pit, pos);
1312 int y = pit->y + row.y_offset();
1314 // y is now the beginning of the cursor row
1315 y += row.baseline();
1316 // y is now the cursor baseline
1319 pos_type last = lastPos(*pit, row);
1321 // None of these should happen, but we're scaredy-cats
1322 if (pos > pit->size()) {
1323 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1326 } else if (pos > last + 1) {
1327 lyxerr << "dont like 2 please report" << endl;
1328 // This shouldn't happen.
1331 } else if (pos < row.pos()) {
1332 lyxerr << "dont like 3 please report" << endl;
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 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1364 // Place cursor before char at (logical) position pos
1365 cursor_vpos = (bidi_level(pos) % 2 == 0)
1366 ? log2vis(pos) : 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 = 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 > cursorRow()->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 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) {
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.end() == 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()) {
1529 if (bidi_level(c) % 2 == 1)
1532 c = vis2log(vc - 1);
1533 bool const rtl = (bidi_level(c) % 2 == 1);
1534 if (left_side == rtl) {
1536 boundary = 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 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);
1607 isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos()))
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)
1617 int x = cursor.x_fix();
1618 int y = cursor.y() - cursorRow()->baseline() - 1;
1619 setCursorFromCoordinates(x, y);
1621 int topy = bv_owner->top_y();
1622 int y1 = cursor.y() - topy;
1625 InsetOld * inset_hit = checkInsetHit(x, y1);
1626 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1627 inset_hit->dispatch(
1628 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1632 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1633 cursorRow()->baseline() << endl;
1634 setCursorFromCoordinates(cursor.x_fix(),
1635 cursor.y() - cursorRow()->baseline() - 1);
1640 void LyXText::cursorDown(bool selecting)
1643 int x = cursor.x_fix();
1644 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1645 setCursorFromCoordinates(x, y);
1647 int topy = bv_owner->top_y();
1648 int y1 = cursor.y() - topy;
1651 InsetOld * inset_hit = checkInsetHit(x, y1);
1652 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1653 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1654 inset_hit->dispatch(cmd);
1658 setCursorFromCoordinates(cursor.x_fix(),
1659 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1664 void LyXText::cursorUpParagraph()
1666 if (cursor.pos() > 0)
1667 setCursor(cursorPar(), 0);
1668 else if (cursorPar() != ownerParagraphs().begin())
1669 setCursor(boost::prior(cursorPar()), 0);
1673 void LyXText::cursorDownParagraph()
1675 ParagraphList::iterator par = cursorPar();
1676 ParagraphList::iterator next_par = boost::next(par);
1678 if (next_par != ownerParagraphs().end())
1679 setCursor(next_par, 0);
1681 setCursor(par, par->size());
1685 // fix the cursor `cur' after a characters has been deleted at `where'
1686 // position. Called by deleteEmptyParagraphMechanism
1687 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1689 // if cursor is not in the paragraph where the delete occured,
1691 if (cur.par() != where.par())
1694 // if cursor position is after the place where the delete occured,
1696 if (cur.pos() > where.pos())
1697 cur.pos(cur.pos()-1);
1699 // check also if we don't want to set the cursor on a spot behind the
1700 // pagragraph because we erased the last character.
1701 if (cur.pos() > getPar(cur)->size())
1702 cur.pos(getPar(cur)->size());
1704 // recompute row et al. for this cursor
1705 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1709 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1711 // Would be wrong to delete anything if we have a selection.
1712 if (selection.set())
1715 // Don't do anything if the cursor is invalid
1716 if (old_cursor.par() == -1)
1719 // We allow all kinds of "mumbo-jumbo" when freespacing.
1720 ParagraphList::iterator const old_pit = getPar(old_cursor);
1721 if (old_pit->isFreeSpacing())
1724 /* Ok I'll put some comments here about what is missing.
1725 I have fixed BackSpace (and thus Delete) to not delete
1726 double-spaces automagically. I have also changed Cut,
1727 Copy and Paste to hopefully do some sensible things.
1728 There are still some small problems that can lead to
1729 double spaces stored in the document file or space at
1730 the beginning of paragraphs. This happens if you have
1731 the cursor between to spaces and then save. Or if you
1732 cut and paste and the selection have a space at the
1733 beginning and then save right after the paste. I am
1734 sure none of these are very hard to fix, but I will
1735 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1736 that I can get some feedback. (Lgb)
1739 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1740 // delete the LineSeparator.
1743 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1744 // delete the LineSeparator.
1747 // If the pos around the old_cursor were spaces, delete one of them.
1748 if (old_cursor.par() != cursor.par()
1749 || old_cursor.pos() != cursor.pos()) {
1751 // Only if the cursor has really moved
1752 if (old_cursor.pos() > 0
1753 && old_cursor.pos() < old_pit->size()
1754 && old_pit->isLineSeparator(old_cursor.pos())
1755 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1756 bool erased = old_pit->erase(old_cursor.pos() - 1);
1757 redoParagraph(old_pit);
1761 #ifdef WITH_WARNINGS
1762 #warning This will not work anymore when we have multiple views of the same buffer
1763 // In this case, we will have to correct also the cursors held by
1764 // other bufferviews. It will probably be easier to do that in a more
1765 // automated way in LyXCursor code. (JMarc 26/09/2001)
1767 // correct all cursors held by the LyXText
1768 fixCursorAfterDelete(cursor, old_cursor);
1769 fixCursorAfterDelete(selection.cursor, old_cursor);
1770 fixCursorAfterDelete(selection.start, old_cursor);
1771 fixCursorAfterDelete(selection.end, old_cursor);
1776 // don't delete anything if this is the ONLY paragraph!
1777 if (ownerParagraphs().size() == 1)
1780 // Do not delete empty paragraphs with keepempty set.
1781 if (old_pit->allowEmpty())
1784 // only do our magic if we changed paragraph
1785 if (old_cursor.par() == cursor.par())
1788 // record if we have deleted a paragraph
1789 // we can't possibly have deleted a paragraph before this point
1790 bool deleted = false;
1792 if (old_pit->empty() ||
1793 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1794 // ok, we will delete something
1795 LyXCursor tmpcursor;
1799 bool selection_position_was_oldcursor_position =
1800 selection.cursor.par() == old_cursor.par()
1801 && selection.cursor.pos() == old_cursor.pos();
1804 cursor = old_cursor; // that undo can restore the right cursor position
1806 ParagraphList::iterator endpit = boost::next(old_pit);
1807 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1810 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1814 ownerParagraphs().erase(old_pit);
1818 setCursorIntern(cursor.par(), cursor.pos());
1820 if (selection_position_was_oldcursor_position) {
1821 // correct selection
1822 selection.cursor = cursor;
1826 if (old_pit->stripLeadingSpaces()) {
1827 redoParagraph(old_pit);
1829 setCursorIntern(cursor.par(), cursor.pos());
1830 selection.cursor = cursor;
1837 ParagraphList & LyXText::ownerParagraphs() const
1839 return *paragraphs_;
1843 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1845 recordUndo(Undo::ATOMIC, this, first, last);
1849 void LyXText::recUndo(lyx::paroffset_type par) const
1851 recordUndo(Undo::ATOMIC, this, par, par);
1855 bool LyXText::isInInset() const
1857 // Sub-level has non-null bv owner and non-null inset owner.
1858 return inset_owner != 0;
1862 int defaultRowHeight()
1864 LyXFont const font(LyXFont::ALL_SANE);
1865 return int(font_metrics::maxAscent(font)
1866 + font_metrics::maxDescent(font) * 1.5);