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 setCursor(cursorPar(), cursorRow()->pos());
567 void LyXText::cursorEnd()
569 setCursor(cursorPar(), cursorRow()->end() - 1);
573 void LyXText::cursorTop()
575 setCursor(ownerParagraphs().begin(), 0);
579 void LyXText::cursorBottom()
581 ParagraphList::iterator lastpit =
582 boost::prior(ownerParagraphs().end());
583 setCursor(lastpit, lastpit->size());
587 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
589 // If the mask is completely neutral, tell user
590 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
591 // Could only happen with user style
592 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
596 // Try implicit word selection
597 // If there is a change in the language the implicit word selection
599 LyXCursor resetCursor = cursor;
600 bool implicitSelection = (font.language() == ignore_language
601 && font.number() == LyXFont::IGNORE)
602 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
605 setFont(font, toggleall);
607 // Implicit selections are cleared afterwards
608 //and cursor is set to the original position.
609 if (implicitSelection) {
611 cursor = resetCursor;
612 setCursor(cursorPar(), cursor.pos());
613 selection.cursor = cursor;
618 string LyXText::getStringToIndex()
620 // Try implicit word selection
621 // If there is a change in the language the implicit word selection
623 LyXCursor const reset_cursor = cursor;
624 bool const implicitSelection =
625 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
628 if (!selection.set())
629 bv()->owner()->message(_("Nothing to index!"));
630 else if (selection.start.par() != selection.end.par())
631 bv()->owner()->message(_("Cannot index more than one paragraph!"));
633 idxstring = selectionAsString(*bv()->buffer(), false);
635 // Reset cursors to their original position.
636 cursor = reset_cursor;
637 setCursor(cursorPar(), cursor.pos());
638 selection.cursor = cursor;
640 // Clear the implicit selection.
641 if (implicitSelection)
648 // the DTP switches for paragraphs. LyX will store them in the first
649 // physical paragraph. When a paragraph is broken, the top settings rest,
650 // the bottom settings are given to the new one. So I can make sure,
651 // they do not duplicate themself and you cannnot make dirty things with
654 void LyXText::setParagraph(bool line_top, bool line_bottom,
655 bool pagebreak_top, bool pagebreak_bottom,
656 VSpace const & space_top,
657 VSpace const & space_bottom,
658 Spacing const & spacing,
660 string const & labelwidthstring,
663 LyXCursor tmpcursor = cursor;
664 if (!selection.set()) {
665 selection.start = cursor;
666 selection.end = cursor;
669 // make sure that the depth behind the selection are restored, too
670 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
671 ParagraphList::iterator undoendpit = endpit;
672 ParagraphList::iterator pars_end = ownerParagraphs().end();
674 if (endpit != pars_end && endpit->getDepth()) {
675 while (endpit != pars_end && endpit->getDepth()) {
679 } else if (endpit != pars_end) {
680 // because of parindents etc.
684 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
687 int tmppit = selection.end.par();
689 while (tmppit != selection.start.par() - 1) {
690 setCursor(tmppit, 0);
692 ParagraphList::iterator const pit = cursorPar();
693 ParagraphParameters & params = pit->params();
695 params.lineTop(line_top);
696 params.lineBottom(line_bottom);
697 params.pagebreakTop(pagebreak_top);
698 params.pagebreakBottom(pagebreak_bottom);
699 params.spaceTop(space_top);
700 params.spaceBottom(space_bottom);
701 params.spacing(spacing);
702 // does the layout allow the new alignment?
703 LyXLayout_ptr const & layout = pit->layout();
705 if (align == LYX_ALIGN_LAYOUT)
706 align = layout->align;
707 if (align & layout->alignpossible) {
708 if (align == layout->align)
709 params.align(LYX_ALIGN_LAYOUT);
713 pit->setLabelWidthString(labelwidthstring);
714 params.noindent(noindent);
718 redoParagraphs(getPar(selection.start), endpit);
721 setCursor(selection.start.par(), selection.start.pos());
722 selection.cursor = cursor;
723 setCursor(selection.end.par(), selection.end.pos());
725 setCursor(tmpcursor.par(), tmpcursor.pos());
727 bv()->updateInset(inset_owner);
733 string expandLabel(LyXTextClass const & textclass,
734 LyXLayout_ptr const & layout, bool appendix)
736 string fmt = appendix ?
737 layout->labelstring_appendix() : layout->labelstring();
739 // handle 'inherited level parts' in 'fmt',
740 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
741 size_t const i = fmt.find('@', 0);
742 if (i != string::npos) {
743 size_t const j = fmt.find('@', i + 1);
744 if (j != string::npos) {
745 string parent(fmt, i + 1, j - i - 1);
746 string label = expandLabel(textclass, textclass[parent], appendix);
747 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
751 return textclass.counters().counterLabel(fmt);
755 void incrementItemDepth(ParagraphList::iterator pit,
756 ParagraphList::iterator first_pit)
758 int const cur_labeltype = pit->layout()->labeltype;
760 if (cur_labeltype != LABEL_ENUMERATE &&
761 cur_labeltype != LABEL_ITEMIZE)
764 int const cur_depth = pit->getDepth();
766 ParagraphList::iterator prev_pit = boost::prior(pit);
768 int const prev_depth = prev_pit->getDepth();
769 int const prev_labeltype = prev_pit->layout()->labeltype;
770 if (prev_depth == 0 && cur_depth > 0) {
771 if (prev_labeltype == cur_labeltype) {
772 pit->itemdepth = prev_pit->itemdepth + 1;
775 } else if (prev_depth < cur_depth) {
776 if (prev_labeltype == cur_labeltype) {
777 pit->itemdepth = prev_pit->itemdepth + 1;
780 } else if (prev_depth == cur_depth) {
781 if (prev_labeltype == cur_labeltype) {
782 pit->itemdepth = prev_pit->itemdepth;
786 if (prev_pit == first_pit)
794 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
795 ParagraphList::iterator firstpit,
801 int const cur_depth = pit->getDepth();
802 ParagraphList::iterator prev_pit = boost::prior(pit);
804 int const prev_depth = prev_pit->getDepth();
805 int const prev_labeltype = prev_pit->layout()->labeltype;
806 if (prev_depth <= cur_depth) {
807 if (prev_labeltype != LABEL_ENUMERATE) {
808 switch (pit->itemdepth) {
810 counters.reset("enumi");
812 counters.reset("enumii");
814 counters.reset("enumiii");
816 counters.reset("enumiv");
822 if (prev_pit == firstpit)
832 // set the counter of a paragraph. This includes the labels
833 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
835 BufferParams const & bufparams = buf.params();
836 LyXTextClass const & textclass = bufparams.getLyXTextClass();
837 LyXLayout_ptr const & layout = pit->layout();
838 ParagraphList::iterator first_pit = ownerParagraphs().begin();
839 Counters & counters = textclass.counters();
844 if (pit == first_pit) {
845 pit->params().appendix(pit->params().startOfAppendix());
847 pit->params().appendix(boost::prior(pit)->params().appendix());
848 if (!pit->params().appendix() &&
849 pit->params().startOfAppendix()) {
850 pit->params().appendix(true);
851 textclass.counters().reset();
854 // Maybe we have to increment the item depth.
855 incrementItemDepth(pit, first_pit);
858 // erase what was there before
859 pit->params().labelString(string());
861 if (layout->margintype == MARGIN_MANUAL) {
862 if (pit->params().labelWidthString().empty())
863 pit->setLabelWidthString(layout->labelstring());
865 pit->setLabelWidthString(string());
868 // is it a layout that has an automatic label?
869 if (layout->labeltype == LABEL_COUNTER) {
870 BufferParams const & bufparams = buf.params();
871 LyXTextClass const & textclass = bufparams.getLyXTextClass();
872 counters.step(layout->counter);
873 string label = expandLabel(textclass, layout, pit->params().appendix());
874 pit->params().labelString(label);
875 } else if (layout->labeltype == LABEL_ITEMIZE) {
876 // At some point of time we should do something more
877 // clever here, like:
878 // pit->params().labelString(
879 // bufparams.user_defined_bullet(pit->itemdepth).getText());
880 // for now, use a simple hardcoded label
882 switch (pit->itemdepth) {
897 pit->params().labelString(itemlabel);
898 } else if (layout->labeltype == LABEL_ENUMERATE) {
899 // Maybe we have to reset the enumeration counter.
900 resetEnumCounterIfNeeded(pit, first_pit, counters);
903 // Yes I know this is a really, really! bad solution
905 string enumcounter = "enum";
907 switch (pit->itemdepth) {
919 // not a valid enumdepth...
923 counters.step(enumcounter);
925 pit->params().labelString(counters.enumLabel(enumcounter));
926 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
927 counters.step("bibitem");
928 int number = counters.value("bibitem");
929 if (pit->bibitem()) {
930 pit->bibitem()->setCounter(number);
931 pit->params().labelString(layout->labelstring());
933 // In biblio should't be following counters but...
935 string s = buf.B_(layout->labelstring());
938 if (layout->labeltype == LABEL_SENSITIVE) {
939 ParagraphList::iterator end = ownerParagraphs().end();
940 ParagraphList::iterator tmppit = pit;
943 while (tmppit != end && tmppit->inInset()
944 // the single '=' is intended below
945 && (in = tmppit->inInset()->owner()))
947 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
948 in->lyxCode() == InsetOld::WRAP_CODE) {
952 Paragraph const * owner = &ownerPar(buf, in);
954 for ( ; tmppit != end; ++tmppit)
955 if (&*tmppit == owner)
963 if (in->lyxCode() == InsetOld::FLOAT_CODE)
964 type = static_cast<InsetFloat*>(in)->params().type;
965 else if (in->lyxCode() == InsetOld::WRAP_CODE)
966 type = static_cast<InsetWrap*>(in)->params().type;
970 Floating const & fl = textclass.floats().getType(type);
972 counters.step(fl.type());
974 // Doesn't work... yet.
975 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
977 // par->SetLayout(0);
978 // s = layout->labelstring;
979 s = _("Senseless: ");
982 pit->params().labelString(s);
988 // Updates all counters. Paragraphs with changed label string will be rebroken
989 void LyXText::updateCounters()
992 bv()->buffer()->params().getLyXTextClass().counters().reset();
994 ParagraphList::iterator beg = ownerParagraphs().begin();
995 ParagraphList::iterator end = ownerParagraphs().end();
996 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
997 string const oldLabel = pit->params().labelString();
1001 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1003 if (pit->params().depth() > maxdepth)
1004 pit->params().depth(maxdepth);
1006 // setCounter can potentially change the labelString.
1007 setCounter(*bv()->buffer(), pit);
1009 string const & newLabel = pit->params().labelString();
1011 if (oldLabel != newLabel)
1017 void LyXText::insertInset(InsetOld * inset)
1019 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1021 recUndo(cursor.par());
1023 cursorPar()->insertInset(cursor.pos(), inset);
1024 // Just to rebreak and refresh correctly.
1025 // The character will not be inserted a second time
1026 insertChar(Paragraph::META_INSET);
1027 // If we enter a highly editable inset the cursor should be before
1028 // the inset. After an Undo LyX tries to call inset->edit(...)
1029 // and fails if the cursor is behind the inset and getInset
1030 // does not return the inset!
1031 if (isHighlyEditableInset(inset))
1037 void LyXText::cutSelection(bool doclear, bool realcut)
1039 // Stuff what we got on the clipboard. Even if there is no selection.
1041 // There is a problem with having the stuffing here in that the
1042 // larger the selection the slower LyX will get. This can be
1043 // solved by running the line below only when the selection has
1044 // finished. The solution used currently just works, to make it
1045 // faster we need to be more clever and probably also have more
1046 // calls to stuffClipboard. (Lgb)
1047 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1049 // This doesn't make sense, if there is no selection
1050 if (!selection.set())
1053 // OK, we have a selection. This is always between selection.start
1054 // and selection.end
1056 // make sure that the depth behind the selection are restored, too
1057 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1058 ParagraphList::iterator undoendpit = endpit;
1059 ParagraphList::iterator pars_end = ownerParagraphs().end();
1061 if (endpit != pars_end && endpit->getDepth()) {
1062 while (endpit != pars_end && endpit->getDepth()) {
1064 undoendpit = endpit;
1066 } else if (endpit != pars_end) {
1067 // because of parindents etc.
1071 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1073 endpit = getPar(selection.end.par());
1074 int endpos = selection.end.pos();
1076 BufferParams const & bufparams = bv()->buffer()->params();
1077 boost::tie(endpit, endpos) = realcut ?
1078 CutAndPaste::cutSelection(bufparams,
1080 getPar(selection.start.par()), endpit,
1081 selection.start.pos(), endpos,
1082 bufparams.textclass,
1084 : CutAndPaste::eraseSelection(bufparams,
1086 getPar(selection.start.par()), endpit,
1087 selection.start.pos(), endpos,
1089 // sometimes necessary
1091 getPar(selection.start.par())->stripLeadingSpaces();
1093 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1094 // cutSelection can invalidate the cursor so we need to set
1096 // we prefer the end for when tracking changes
1098 cursor.par(parOffset(endpit));
1100 // need a valid cursor. (Lgb)
1103 setCursor(cursorPar(), cursor.pos());
1104 selection.cursor = cursor;
1109 void LyXText::copySelection()
1111 // stuff the selection onto the X clipboard, from an explicit copy request
1112 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1114 // this doesnt make sense, if there is no selection
1115 if (!selection.set())
1118 // ok we have a selection. This is always between selection.start
1119 // and sel_end cursor
1121 // copy behind a space if there is one
1122 while (getPar(selection.start)->size() > selection.start.pos()
1123 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1124 && (selection.start.par() != selection.end.par()
1125 || selection.start.pos() < selection.end.pos()))
1126 selection.start.pos(selection.start.pos() + 1);
1128 CutAndPaste::copySelection(getPar(selection.start.par()),
1129 getPar(selection.end.par()),
1130 selection.start.pos(), selection.end.pos(),
1131 bv()->buffer()->params().textclass);
1135 void LyXText::pasteSelection(size_t sel_index)
1137 // this does not make sense, if there is nothing to paste
1138 if (!CutAndPaste::checkPastePossible())
1141 recUndo(cursor.par());
1143 ParagraphList::iterator endpit;
1148 boost::tie(ppp, endpit) =
1149 CutAndPaste::pasteSelection(*bv()->buffer(),
1151 cursorPar(), cursor.pos(),
1152 bv()->buffer()->params().textclass,
1154 bufferErrors(*bv()->buffer(), el);
1155 bv()->showErrorList(_("Paste"));
1157 redoParagraphs(cursorPar(), endpit);
1159 setCursor(cursor.par(), cursor.pos());
1162 selection.cursor = cursor;
1163 setCursor(ppp.first, ppp.second);
1169 void LyXText::setSelectionRange(lyx::pos_type length)
1174 selection.cursor = cursor;
1181 // simple replacing. The font of the first selected character is used
1182 void LyXText::replaceSelectionWithString(string const & str)
1184 recUndo(cursor.par());
1187 if (!selection.set()) { // create a dummy selection
1188 selection.end = cursor;
1189 selection.start = cursor;
1192 // Get font setting before we cut
1193 pos_type pos = selection.end.pos();
1194 LyXFont const font = getPar(selection.start)
1195 ->getFontSettings(bv()->buffer()->params(),
1196 selection.start.pos());
1198 // Insert the new string
1199 string::const_iterator cit = str.begin();
1200 string::const_iterator end = str.end();
1201 for (; cit != end; ++cit) {
1202 getPar(selection.end)->insertChar(pos, (*cit), font);
1206 // Cut the selection
1207 cutSelection(true, false);
1213 // needed to insert the selection
1214 void LyXText::insertStringAsLines(string const & str)
1216 ParagraphList::iterator pit = cursorPar();
1217 pos_type pos = cursor.pos();
1218 ParagraphList::iterator endpit = boost::next(cursorPar());
1220 recUndo(cursor.par());
1222 // only to be sure, should not be neccessary
1225 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1227 redoParagraphs(cursorPar(), endpit);
1228 setCursor(cursorPar(), cursor.pos());
1229 selection.cursor = cursor;
1230 setCursor(pit, pos);
1235 // turns double-CR to single CR, others where converted into one
1236 // blank. Then InsertStringAsLines is called
1237 void LyXText::insertStringAsParagraphs(string const & str)
1239 string linestr(str);
1240 bool newline_inserted = false;
1241 string::size_type const siz = linestr.length();
1243 for (string::size_type i = 0; i < siz; ++i) {
1244 if (linestr[i] == '\n') {
1245 if (newline_inserted) {
1246 // we know that \r will be ignored by
1247 // InsertStringA. Of course, it is a dirty
1248 // trick, but it works...
1249 linestr[i - 1] = '\r';
1253 newline_inserted = true;
1255 } else if (IsPrintable(linestr[i])) {
1256 newline_inserted = false;
1259 insertStringAsLines(linestr);
1263 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1265 setCursor(parOffset(pit), pos);
1269 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1271 LyXCursor old_cursor = cursor;
1272 setCursorIntern(par, pos, setfont, boundary);
1273 return deleteEmptyParagraphMechanism(old_cursor);
1277 void LyXText::redoCursor()
1279 #warning maybe the same for selections?
1280 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1284 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1285 pos_type pos, bool boundary)
1287 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1291 cur.boundary(boundary);
1293 // no rows, no fun...
1294 if (ownerParagraphs().begin()->rows.empty())
1297 // get the cursor y position in text
1299 ParagraphList::iterator pit = getPar(par);
1300 Row const & row = *getRow(*pit, pos);
1301 int y = pit->y + row.y_offset();
1303 // y is now the beginning of the cursor row
1304 y += row.baseline();
1305 // y is now the cursor baseline
1308 pos_type last = lastPos(*pit, row);
1310 // None of these should happen, but we're scaredy-cats
1311 if (pos > pit->size()) {
1312 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1315 } else if (pos > last + 1) {
1316 lyxerr << "dont like 2 please report" << endl;
1317 // This shouldn't happen.
1320 } else if (pos < row.pos()) {
1321 lyxerr << "dont like 3 please report" << endl;
1326 // now get the cursors x position
1327 float x = getCursorX(pit, row, pos, last, boundary);
1333 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1334 pos_type pos, pos_type last, bool boundary) const
1336 pos_type cursor_vpos = 0;
1338 double fill_separator = row.fill_separator();
1339 double fill_hfill = row.fill_hfill();
1340 double fill_label_hfill = row.fill_label_hfill();
1341 pos_type const row_pos = row.pos();
1344 cursor_vpos = row_pos;
1345 else if (pos > last && !boundary)
1346 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1347 ? row_pos : last + 1;
1348 else if (pos > row_pos && (pos > last || boundary))
1349 // Place cursor after char at (logical) position pos - 1
1350 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1351 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1353 // Place cursor before char at (logical) position pos
1354 cursor_vpos = (bidi_level(pos) % 2 == 0)
1355 ? log2vis(pos) : log2vis(pos) + 1;
1357 pos_type body_pos = pit->beginningOfBody();
1359 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1362 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1363 pos_type pos = vis2log(vpos);
1364 if (body_pos > 0 && pos == body_pos - 1) {
1365 x += fill_label_hfill +
1366 font_metrics::width(
1367 pit->layout()->labelsep, getLabelFont(pit));
1368 if (pit->isLineSeparator(body_pos - 1))
1369 x -= singleWidth(pit, body_pos - 1);
1372 if (hfillExpansion(*pit, row, pos)) {
1373 x += singleWidth(pit, pos);
1374 if (pos >= body_pos)
1377 x += fill_label_hfill;
1378 } else if (pit->isSeparator(pos)) {
1379 x += singleWidth(pit, pos);
1380 if (pos >= body_pos)
1381 x += fill_separator;
1383 x += singleWidth(pit, pos);
1389 void LyXText::setCursorIntern(paroffset_type par,
1390 pos_type pos, bool setfont, bool boundary)
1392 setCursor(cursor, par, pos, boundary);
1398 void LyXText::setCurrentFont()
1400 pos_type pos = cursor.pos();
1401 ParagraphList::iterator pit = cursorPar();
1403 if (cursor.boundary() && pos > 0)
1407 if (pos == pit->size())
1409 else // potentional bug... BUG (Lgb)
1410 if (pit->isSeparator(pos)) {
1411 if (pos > cursorRow()->pos() &&
1412 bidi_level(pos) % 2 ==
1413 bidi_level(pos - 1) % 2)
1415 else if (pos + 1 < pit->size())
1420 BufferParams const & bufparams = bv()->buffer()->params();
1421 current_font = pit->getFontSettings(bufparams, pos);
1422 real_current_font = getFont(pit, pos);
1424 if (cursor.pos() == pit->size() &&
1425 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1426 !cursor.boundary()) {
1427 Language const * lang =
1428 pit->getParLanguage(bufparams);
1429 current_font.setLanguage(lang);
1430 current_font.setNumber(LyXFont::OFF);
1431 real_current_font.setLanguage(lang);
1432 real_current_font.setNumber(LyXFont::OFF);
1437 // returns the column near the specified x-coordinate of the row
1438 // x is set to the real beginning of this column
1439 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1440 Row const & row, int & x, bool & boundary) const
1442 double tmpx = row.x();
1443 double fill_separator = row.fill_separator();
1444 double fill_hfill = row.fill_hfill();
1445 double fill_label_hfill = row.fill_label_hfill();
1447 pos_type vc = row.pos();
1448 pos_type last = lastPos(*pit, row);
1450 LyXLayout_ptr const & layout = pit->layout();
1452 bool left_side = false;
1454 pos_type body_pos = pit->beginningOfBody();
1455 double last_tmpx = tmpx;
1458 (body_pos - 1 > last ||
1459 !pit->isLineSeparator(body_pos - 1)))
1462 // check for empty row
1468 while (vc <= last && tmpx <= x) {
1471 if (body_pos > 0 && c == body_pos - 1) {
1472 tmpx += fill_label_hfill +
1473 font_metrics::width(layout->labelsep, getLabelFont(pit));
1474 if (pit->isLineSeparator(body_pos - 1))
1475 tmpx -= singleWidth(pit, body_pos - 1);
1478 if (hfillExpansion(*pit, row, c)) {
1479 tmpx += singleWidth(pit, c);
1483 tmpx += fill_label_hfill;
1484 } else if (pit->isSeparator(c)) {
1485 tmpx += singleWidth(pit, c);
1487 tmpx += fill_separator;
1489 tmpx += singleWidth(pit, c);
1494 if ((tmpx + last_tmpx) / 2 > x) {
1499 if (vc > last + 1) // This shouldn't happen.
1503 // This (rtl_support test) is not needed, but gives
1504 // some speedup if rtl_support == false
1505 bool const lastrow = lyxrc.rtl_support && row.end() == pit->size();
1507 // If lastrow is false, we don't need to compute
1508 // the value of rtl.
1509 bool const rtl = (lastrow)
1510 ? pit->isRightToLeftPar(bv()->buffer()->params())
1513 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1514 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1516 else if (vc == row.pos()) {
1518 if (bidi_level(c) % 2 == 1)
1521 c = vis2log(vc - 1);
1522 bool const rtl = (bidi_level(c) % 2 == 1);
1523 if (left_side == rtl) {
1525 boundary = isBoundary(*bv()->buffer(), *pit, c);
1529 if (row.pos() <= last && c > last && pit->isNewline(last)) {
1530 if (bidi_level(last) % 2 == 0)
1531 tmpx -= singleWidth(pit, last);
1533 tmpx += singleWidth(pit, last);
1543 void LyXText::setCursorFromCoordinates(int x, int y)
1545 LyXCursor old_cursor = cursor;
1546 setCursorFromCoordinates(cursor, x, y);
1548 deleteEmptyParagraphMechanism(old_cursor);
1552 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1554 // Get the row first.
1555 ParagraphList::iterator pit;
1556 Row const & row = *getRowNearY(y, pit);
1557 y = pit->y + row.y_offset();
1560 pos_type const column = getColumnNearX(pit, row, x, bound);
1561 cur.par(parOffset(pit));
1562 cur.pos(row.pos() + column);
1564 cur.y(y + row.baseline());
1566 cur.boundary(bound);
1570 void LyXText::cursorLeft(bool internal)
1572 if (cursor.pos() > 0) {
1573 bool boundary = cursor.boundary();
1574 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1575 if (!internal && !boundary &&
1576 isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1577 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1578 } else if (cursor.par() != 0) {
1579 // steps into the paragraph above
1580 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1585 void LyXText::cursorRight(bool internal)
1587 bool const at_end = (cursor.pos() == cursorPar()->size());
1588 bool const at_newline = !at_end &&
1589 cursorPar()->isNewline(cursor.pos());
1591 if (!internal && cursor.boundary() && !at_newline)
1592 setCursor(cursor.par(), cursor.pos(), true, false);
1594 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1596 isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos()))
1597 setCursor(cursor.par(), cursor.pos(), true, true);
1598 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1599 setCursor(cursor.par() + 1, 0);
1603 void LyXText::cursorUp(bool selecting)
1606 int x = cursor.x_fix();
1607 int y = cursor.y() - cursorRow()->baseline() - 1;
1608 setCursorFromCoordinates(x, y);
1610 int topy = bv_owner->top_y();
1611 int y1 = cursor.y() - topy;
1614 InsetOld * inset_hit = checkInsetHit(x, y1);
1615 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1616 inset_hit->dispatch(
1617 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1621 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1622 cursorRow()->baseline() << endl;
1623 setCursorFromCoordinates(cursor.x_fix(),
1624 cursor.y() - cursorRow()->baseline() - 1);
1629 void LyXText::cursorDown(bool selecting)
1632 int x = cursor.x_fix();
1633 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1634 setCursorFromCoordinates(x, y);
1636 int topy = bv_owner->top_y();
1637 int y1 = cursor.y() - topy;
1640 InsetOld * inset_hit = checkInsetHit(x, y1);
1641 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1642 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1643 inset_hit->dispatch(cmd);
1647 setCursorFromCoordinates(cursor.x_fix(),
1648 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1653 void LyXText::cursorUpParagraph()
1655 if (cursor.pos() > 0)
1656 setCursor(cursorPar(), 0);
1657 else if (cursorPar() != ownerParagraphs().begin())
1658 setCursor(boost::prior(cursorPar()), 0);
1662 void LyXText::cursorDownParagraph()
1664 ParagraphList::iterator par = cursorPar();
1665 ParagraphList::iterator next_par = boost::next(par);
1667 if (next_par != ownerParagraphs().end())
1668 setCursor(next_par, 0);
1670 setCursor(par, par->size());
1674 // fix the cursor `cur' after a characters has been deleted at `where'
1675 // position. Called by deleteEmptyParagraphMechanism
1676 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1678 // if cursor is not in the paragraph where the delete occured,
1680 if (cur.par() != where.par())
1683 // if cursor position is after the place where the delete occured,
1685 if (cur.pos() > where.pos())
1686 cur.pos(cur.pos()-1);
1688 // check also if we don't want to set the cursor on a spot behind the
1689 // pagragraph because we erased the last character.
1690 if (cur.pos() > getPar(cur)->size())
1691 cur.pos(getPar(cur)->size());
1693 // recompute row et al. for this cursor
1694 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1698 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1700 // Would be wrong to delete anything if we have a selection.
1701 if (selection.set())
1704 // Don't do anything if the cursor is invalid
1705 if (old_cursor.par() == -1)
1708 // We allow all kinds of "mumbo-jumbo" when freespacing.
1709 ParagraphList::iterator const old_pit = getPar(old_cursor);
1710 if (old_pit->isFreeSpacing())
1713 /* Ok I'll put some comments here about what is missing.
1714 I have fixed BackSpace (and thus Delete) to not delete
1715 double-spaces automagically. I have also changed Cut,
1716 Copy and Paste to hopefully do some sensible things.
1717 There are still some small problems that can lead to
1718 double spaces stored in the document file or space at
1719 the beginning of paragraphs. This happens if you have
1720 the cursor between to spaces and then save. Or if you
1721 cut and paste and the selection have a space at the
1722 beginning and then save right after the paste. I am
1723 sure none of these are very hard to fix, but I will
1724 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1725 that I can get some feedback. (Lgb)
1728 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1729 // delete the LineSeparator.
1732 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1733 // delete the LineSeparator.
1736 // If the pos around the old_cursor were spaces, delete one of them.
1737 if (old_cursor.par() != cursor.par()
1738 || old_cursor.pos() != cursor.pos()) {
1740 // Only if the cursor has really moved
1741 if (old_cursor.pos() > 0
1742 && old_cursor.pos() < old_pit->size()
1743 && old_pit->isLineSeparator(old_cursor.pos())
1744 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1745 bool erased = old_pit->erase(old_cursor.pos() - 1);
1746 redoParagraph(old_pit);
1750 #ifdef WITH_WARNINGS
1751 #warning This will not work anymore when we have multiple views of the same buffer
1752 // In this case, we will have to correct also the cursors held by
1753 // other bufferviews. It will probably be easier to do that in a more
1754 // automated way in LyXCursor code. (JMarc 26/09/2001)
1756 // correct all cursors held by the LyXText
1757 fixCursorAfterDelete(cursor, old_cursor);
1758 fixCursorAfterDelete(selection.cursor, old_cursor);
1759 fixCursorAfterDelete(selection.start, old_cursor);
1760 fixCursorAfterDelete(selection.end, old_cursor);
1765 // don't delete anything if this is the ONLY paragraph!
1766 if (ownerParagraphs().size() == 1)
1769 // Do not delete empty paragraphs with keepempty set.
1770 if (old_pit->allowEmpty())
1773 // only do our magic if we changed paragraph
1774 if (old_cursor.par() == cursor.par())
1777 // record if we have deleted a paragraph
1778 // we can't possibly have deleted a paragraph before this point
1779 bool deleted = false;
1781 if (old_pit->empty() ||
1782 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1783 // ok, we will delete something
1784 LyXCursor tmpcursor;
1788 bool selection_position_was_oldcursor_position =
1789 selection.cursor.par() == old_cursor.par()
1790 && selection.cursor.pos() == old_cursor.pos();
1793 cursor = old_cursor; // that undo can restore the right cursor position
1795 ParagraphList::iterator endpit = boost::next(old_pit);
1796 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1799 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1803 ownerParagraphs().erase(old_pit);
1807 setCursorIntern(cursor.par(), cursor.pos());
1809 if (selection_position_was_oldcursor_position) {
1810 // correct selection
1811 selection.cursor = cursor;
1815 if (old_pit->stripLeadingSpaces()) {
1816 redoParagraph(old_pit);
1818 setCursorIntern(cursor.par(), cursor.pos());
1819 selection.cursor = cursor;
1826 ParagraphList & LyXText::ownerParagraphs() const
1828 return *paragraphs_;
1832 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1834 recordUndo(Undo::ATOMIC, this, first, last);
1838 void LyXText::recUndo(lyx::paroffset_type par) const
1840 recordUndo(Undo::ATOMIC, this, par, par);
1844 bool LyXText::isInInset() const
1846 // Sub-level has non-null bv owner and non-null inset owner.
1847 return inset_owner != 0;
1851 int defaultRowHeight()
1853 LyXFont const font(LyXFont::ALL_SANE);
1854 return int(font_metrics::maxAscent(font)
1855 + font_metrics::maxDescent(font) * 1.5);