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.
23 #include "support/std_sstream.h"
24 #include "funcrequest.h"
25 #include "frontends/LyXView.h"
26 #include "undo_funcs.h"
28 #include "buffer_funcs.h"
30 #include "BufferView.h"
31 #include "CutAndPaste.h"
32 #include "frontends/font_metrics.h"
36 #include "FloatList.h"
38 #include "ParagraphParameters.h"
40 #include "lyxrow_funcs.h"
41 #include "metricsinfo.h"
42 #include "paragraph_funcs.h"
44 #include "insets/insetbibitem.h"
45 #include "insets/insetenv.h"
46 #include "insets/insetfloat.h"
47 #include "insets/insetwrap.h"
49 #include "support/LAssert.h"
50 #include "support/textutils.h"
51 #include "support/lstrings.h"
53 #include <boost/tuple/tuple.hpp>
56 using namespace lyx::support;
58 using std::ostringstream;
67 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
68 ParagraphList & paragraphs)
69 : height(0), width(0), anchor_y_(0),
70 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
71 in_inset_(ininset), paragraphs_(paragraphs)
76 void LyXText::init(BufferView * bview)
80 ParagraphList::iterator const beg = ownerParagraphs().begin();
81 ParagraphList::iterator const end = ownerParagraphs().end();
82 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
90 current_font = getFont(beg, 0);
92 redoParagraphs(beg, end);
93 setCursorIntern(beg, 0);
94 selection.cursor = cursor;
100 // Gets the fully instantiated font at a given position in a paragraph
101 // Basically the same routine as Paragraph::getFont() in paragraph.C.
102 // The difference is that this one is used for displaying, and thus we
103 // are allowed to make cosmetic improvements. For instance make footnotes
105 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
109 LyXLayout_ptr const & layout = pit->layout();
111 BufferParams const & params = bv()->buffer()->params;
113 // We specialize the 95% common case:
114 if (!pit->getDepth()) {
115 if (layout->labeltype == LABEL_MANUAL
116 && pos < pit->beginningOfBody()) {
118 LyXFont f = pit->getFontSettings(params, pos);
120 pit->inInset()->getDrawFont(f);
121 return f.realize(layout->reslabelfont);
123 LyXFont f = pit->getFontSettings(params, pos);
125 pit->inInset()->getDrawFont(f);
126 return f.realize(layout->resfont);
130 // The uncommon case need not be optimized as much
134 if (pos < pit->beginningOfBody()) {
136 layoutfont = layout->labelfont;
139 layoutfont = layout->font;
142 LyXFont tmpfont = pit->getFontSettings(params, pos);
143 tmpfont.realize(layoutfont);
146 pit->inInset()->getDrawFont(tmpfont);
148 // Realize with the fonts of lesser depth.
149 tmpfont.realize(outerFont(pit, ownerParagraphs()));
150 tmpfont.realize(defaultfont_);
156 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
158 LyXLayout_ptr const & layout = pit->layout();
160 if (!pit->getDepth())
161 return layout->resfont;
163 LyXFont font = layout->font;
164 // Realize with the fonts of lesser depth.
165 font.realize(outerFont(pit, ownerParagraphs()));
166 font.realize(defaultfont_);
172 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
174 LyXLayout_ptr const & layout = pit->layout();
176 if (!pit->getDepth())
177 return layout->reslabelfont;
179 LyXFont font = layout->labelfont;
180 // Realize with the fonts of lesser depth.
181 font.realize(outerFont(pit, ownerParagraphs()));
182 font.realize(defaultfont_);
188 void LyXText::setCharFont(ParagraphList::iterator pit,
189 pos_type pos, LyXFont const & fnt,
192 BufferParams const & params = bv()->buffer()->params;
193 LyXFont font = getFont(pit, pos);
194 font.update(fnt, params.language, toggleall);
195 // Let the insets convert their font
196 if (pit->isInset(pos)) {
197 InsetOld * inset = pit->getInset(pos);
198 if (isEditableInset(inset)) {
199 static_cast<UpdatableInset *>(inset)
200 ->setFont(bv(), fnt, toggleall, true);
204 // Plug through to version below:
205 setCharFont(pit, pos, font);
209 void LyXText::setCharFont(
210 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
213 LyXLayout_ptr const & layout = pit->layout();
215 // Get concrete layout font to reduce against
218 if (pos < pit->beginningOfBody())
219 layoutfont = layout->labelfont;
221 layoutfont = layout->font;
223 // Realize against environment font information
224 if (pit->getDepth()) {
225 ParagraphList::iterator tp = pit;
226 while (!layoutfont.resolved() &&
227 tp != ownerParagraphs().end() &&
229 tp = outerHook(tp, ownerParagraphs());
230 if (tp != ownerParagraphs().end())
231 layoutfont.realize(tp->layout()->font);
235 layoutfont.realize(defaultfont_);
237 // Now, reduce font against full layout font
238 font.reduce(layoutfont);
240 pit->setFont(pos, font);
244 InsetOld * LyXText::getInset() const
246 ParagraphList::iterator pit = cursor.par();
247 pos_type const pos = cursor.pos();
249 if (pos < pit->size() && pit->isInset(pos)) {
250 return pit->getInset(pos);
256 void LyXText::toggleInset()
258 InsetOld * inset = getInset();
259 // is there an editable inset at cursor position?
260 if (!isEditableInset(inset)) {
261 // No, try to see if we are inside a collapsable inset
262 if (inset_owner && inset_owner->owner()
263 && inset_owner->owner()->isOpen()) {
264 bv()->unlockInset(inset_owner->owner());
265 inset_owner->owner()->close(bv());
266 bv()->getLyXText()->cursorRight(bv());
270 //bv()->owner()->message(inset->editMessage());
272 // do we want to keep this?? (JMarc)
273 if (!isHighlyEditableInset(inset))
274 recordUndo(bv(), Undo::ATOMIC);
281 bv()->updateInset(inset);
285 /* used in setlayout */
286 // Asger is not sure we want to do this...
287 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
290 LyXLayout_ptr const & layout = par.layout();
291 pos_type const psize = par.size();
294 for (pos_type pos = 0; pos < psize; ++pos) {
295 if (pos < par.beginningOfBody())
296 layoutfont = layout->labelfont;
298 layoutfont = layout->font;
300 LyXFont tmpfont = par.getFontSettings(params, pos);
301 tmpfont.reduce(layoutfont);
302 par.setFont(pos, tmpfont);
307 ParagraphList::iterator
308 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
309 LyXCursor & send_cur,
310 string const & layout)
312 ParagraphList::iterator endpit = boost::next(send_cur.par());
313 ParagraphList::iterator undoendpit = endpit;
314 ParagraphList::iterator pars_end = ownerParagraphs().end();
316 if (endpit != pars_end && endpit->getDepth()) {
317 while (endpit != pars_end && endpit->getDepth()) {
321 } else if (endpit != pars_end) {
322 // because of parindents etc.
326 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
328 // ok we have a selection. This is always between sstart_cur
329 // and sel_end cursor
331 ParagraphList::iterator pit = sstart_cur.par();
332 ParagraphList::iterator epit = boost::next(send_cur.par());
334 LyXLayout_ptr const & lyxlayout =
335 bv()->buffer()->params.getLyXTextClass()[layout];
338 pit->applyLayout(lyxlayout);
339 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
340 pit->params().spaceTop(lyxlayout->fill_top ?
341 VSpace(VSpace::VFILL)
342 : VSpace(VSpace::NONE));
343 pit->params().spaceBottom(lyxlayout->fill_bottom ?
344 VSpace(VSpace::VFILL)
345 : VSpace(VSpace::NONE));
346 if (lyxlayout->margintype == MARGIN_MANUAL)
347 pit->setLabelWidthString(lyxlayout->labelstring());
350 } while (pit != epit);
356 // set layout over selection and make a total rebreak of those paragraphs
357 void LyXText::setLayout(string const & layout)
359 LyXCursor tmpcursor = cursor; // store the current cursor
361 // if there is no selection just set the layout
362 // of the current paragraph
363 if (!selection.set()) {
364 selection.start = cursor; // dummy selection
365 selection.end = cursor;
368 // special handling of new environment insets
369 BufferParams const & params = bv()->buffer()->params;
370 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
371 if (lyxlayout->is_environment) {
372 // move everything in a new environment inset
373 lyxerr << "setting layout " << layout << endl;
374 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
375 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
376 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
377 InsetOld * inset = new InsetEnvironment(params, layout);
378 if (bv()->insertInset(inset)) {
380 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
387 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
388 selection.end, layout);
389 redoParagraphs(selection.start.par(), endpit);
391 // we have to reset the selection, because the
392 // geometry could have changed
393 setCursor(selection.start.par(), selection.start.pos(), false);
394 selection.cursor = cursor;
395 setCursor(selection.end.par(), selection.end.pos(), false);
399 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
403 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
405 ParagraphList::iterator pit = cursor.par();
406 ParagraphList::iterator end = cursor.par();
407 ParagraphList::iterator start = pit;
409 if (selection.set()) {
410 pit = selection.start.par();
411 end = selection.end.par();
415 ParagraphList::iterator pastend = boost::next(end);
418 recordUndo(bv(), Undo::ATOMIC, start, end);
420 bool changed = false;
422 int prev_after_depth = 0;
423 #warning parlist ... could be nicer ?
424 if (start != ownerParagraphs().begin()) {
425 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
429 int const depth = pit->params().depth();
430 if (type == bv_funcs::INC_DEPTH) {
431 if (depth < prev_after_depth
432 && pit->layout()->labeltype != LABEL_BIBLIO) {
435 pit->params().depth(depth + 1);
440 pit->params().depth(depth - 1);
443 prev_after_depth = pit->getMaxDepthAfter();
455 redoParagraphs(start, pastend);
457 // We need to actually move the text->cursor. I don't
458 // understand why ...
459 LyXCursor tmpcursor = cursor;
461 // we have to reset the visual selection because the
462 // geometry could have changed
463 if (selection.set()) {
464 setCursor(selection.start.par(), selection.start.pos());
465 selection.cursor = cursor;
466 setCursor(selection.end.par(), selection.end.pos());
469 // this handles the counter labels, and also fixes up
470 // depth values for follow-on (child) paragraphs
474 setCursor(tmpcursor.par(), tmpcursor.pos());
480 // set font over selection and make a total rebreak of those paragraphs
481 void LyXText::setFont(LyXFont const & font, bool toggleall)
483 // if there is no selection just set the current_font
484 if (!selection.set()) {
485 // Determine basis font
487 if (cursor.pos() < cursor.par()->beginningOfBody()) {
488 layoutfont = getLabelFont(cursor.par());
490 layoutfont = getLayoutFont(cursor.par());
492 // Update current font
493 real_current_font.update(font,
494 bv()->buffer()->params.language,
497 // Reduce to implicit settings
498 current_font = real_current_font;
499 current_font.reduce(layoutfont);
500 // And resolve it completely
501 real_current_font.realize(layoutfont);
506 LyXCursor tmpcursor = cursor; // store the current cursor
508 // ok we have a selection. This is always between sel_start_cursor
509 // and sel_end cursor
511 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
513 cursor = selection.start;
514 while (cursor.par() != selection.end.par() ||
515 cursor.pos() < selection.end.pos())
517 if (cursor.pos() < cursor.par()->size()) {
518 // an open footnote should behave like a closed one
519 setCharFont(cursor.par(), cursor.pos(),
521 cursor.pos(cursor.pos() + 1);
524 cursor.par(boost::next(cursor.par()));
529 redoParagraph(selection.start.par());
531 // we have to reset the selection, because the
532 // geometry could have changed, but we keep
533 // it for user convenience
534 setCursor(selection.start.par(), selection.start.pos());
535 selection.cursor = cursor;
536 setCursor(selection.end.par(), selection.end.pos());
538 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
539 tmpcursor.boundary());
543 int LyXText::redoParagraphInternal(ParagraphList::iterator pit)
545 RowList::iterator rit = pit->rows.begin();
546 RowList::iterator end = pit->rows.end();
548 // remove rows of paragraph, keep track of height changes
549 for (int i = 0; rit != end; ++rit, ++i)
550 height -= rit->height();
554 InsetList::iterator ii = pit->insetlist.begin();
555 InsetList::iterator iend = pit->insetlist.end();
556 for (; ii != iend; ++ii) {
558 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
559 ii->inset->metrics(mi, dim);
562 // rebreak the paragraph
563 for (pos_type z = 0; z < pit->size() + 1; ) {
565 z = rowBreakPoint(pit, row) + 1;
567 pit->rows.push_back(row);
571 // set height and fill and width of rows
572 int const ww = workWidth();
573 for (rit = pit->rows.begin(); rit != end; ++rit) {
574 int const f = fill(pit, rit, ww);
575 int const w = ww - f;
576 par_width = std::max(par_width, w);
579 prepareToPrint(pit, rit);
580 setHeightOfRow(pit, rit);
581 height += rit->height();
584 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
589 int LyXText::redoParagraphs(ParagraphList::iterator start,
590 ParagraphList::iterator end)
593 for ( ; start != end; ++start) {
594 int par_width = redoParagraphInternal(start);
595 pars_width = std::max(par_width, pars_width);
597 updateRowPositions();
602 void LyXText::redoParagraph(ParagraphList::iterator pit)
604 redoParagraphInternal(pit);
605 updateRowPositions();
609 void LyXText::fullRebreak()
611 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
613 selection.cursor = cursor;
617 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
619 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
620 // << " workWidth: " << workWidth() << endl;
621 //Assert(mi.base.textwidth);
628 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
631 dim.asc = firstRow()->ascent_of_text();
632 dim.des = height - dim.asc;
633 dim.wid = std::max(mi.base.textwidth, int(width));
637 // important for the screen
640 // the cursor set functions have a special mechanism. When they
641 // realize, that you left an empty paragraph, they will delete it.
642 // They also delete the corresponding row
644 // need the selection cursor:
645 void LyXText::setSelection()
647 TextCursor::setSelection();
652 void LyXText::clearSelection()
654 TextCursor::clearSelection();
656 // reset this in the bv_owner!
657 if (bv_owner && bv_owner->text)
658 bv_owner->text->xsel_cache.set(false);
662 void LyXText::cursorHome()
664 setCursor(cursor.par(), cursorRow()->pos());
668 void LyXText::cursorEnd()
670 setCursor(cursor.par(), cursorRow()->end() - 1);
674 void LyXText::cursorTop()
676 setCursor(ownerParagraphs().begin(), 0);
680 void LyXText::cursorBottom()
682 ParagraphList::iterator lastpit =
683 boost::prior(ownerParagraphs().end());
684 setCursor(lastpit, lastpit->size());
688 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
690 // If the mask is completely neutral, tell user
691 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
692 // Could only happen with user style
693 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
697 // Try implicit word selection
698 // If there is a change in the language the implicit word selection
700 LyXCursor resetCursor = cursor;
701 bool implicitSelection = (font.language() == ignore_language
702 && font.number() == LyXFont::IGNORE)
703 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
706 setFont(font, toggleall);
708 // Implicit selections are cleared afterwards
709 //and cursor is set to the original position.
710 if (implicitSelection) {
712 cursor = resetCursor;
713 setCursor(cursor.par(), cursor.pos());
714 selection.cursor = cursor;
719 string LyXText::getStringToIndex()
721 // Try implicit word selection
722 // If there is a change in the language the implicit word selection
724 LyXCursor const reset_cursor = cursor;
725 bool const implicitSelection =
726 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
729 if (!selection.set())
730 bv()->owner()->message(_("Nothing to index!"));
731 else if (selection.start.par() != selection.end.par())
732 bv()->owner()->message(_("Cannot index more than one paragraph!"));
734 idxstring = selectionAsString(*bv()->buffer(), false);
736 // Reset cursors to their original position.
737 cursor = reset_cursor;
738 setCursor(cursor.par(), cursor.pos());
739 selection.cursor = cursor;
741 // Clear the implicit selection.
742 if (implicitSelection)
749 // the DTP switches for paragraphs. LyX will store them in the first
750 // physical paragraph. When a paragraph is broken, the top settings rest,
751 // the bottom settings are given to the new one. So I can make sure,
752 // they do not duplicate themself and you cannnot make dirty things with
755 void LyXText::setParagraph(bool line_top, bool line_bottom,
756 bool pagebreak_top, bool pagebreak_bottom,
757 VSpace const & space_top,
758 VSpace const & space_bottom,
759 Spacing const & spacing,
761 string const & labelwidthstring,
764 LyXCursor tmpcursor = cursor;
765 if (!selection.set()) {
766 selection.start = cursor;
767 selection.end = cursor;
770 // make sure that the depth behind the selection are restored, too
771 ParagraphList::iterator endpit = boost::next(selection.end.par());
772 ParagraphList::iterator undoendpit = endpit;
773 ParagraphList::iterator pars_end = ownerParagraphs().end();
775 if (endpit != pars_end && endpit->getDepth()) {
776 while (endpit != pars_end && endpit->getDepth()) {
780 } else if (endpit != pars_end) {
781 // because of parindents etc.
785 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
786 boost::prior(undoendpit));
789 ParagraphList::iterator tmppit = selection.end.par();
791 while (tmppit != boost::prior(selection.start.par())) {
792 setCursor(tmppit, 0);
794 ParagraphList::iterator pit = cursor.par();
795 ParagraphParameters & params = pit->params();
797 params.lineTop(line_top);
798 params.lineBottom(line_bottom);
799 params.pagebreakTop(pagebreak_top);
800 params.pagebreakBottom(pagebreak_bottom);
801 params.spaceTop(space_top);
802 params.spaceBottom(space_bottom);
803 params.spacing(spacing);
804 // does the layout allow the new alignment?
805 LyXLayout_ptr const & layout = pit->layout();
807 if (align == LYX_ALIGN_LAYOUT)
808 align = layout->align;
809 if (align & layout->alignpossible) {
810 if (align == layout->align)
811 params.align(LYX_ALIGN_LAYOUT);
815 pit->setLabelWidthString(labelwidthstring);
816 params.noindent(noindent);
817 tmppit = boost::prior(pit);
820 redoParagraphs(selection.start.par(), endpit);
823 setCursor(selection.start.par(), selection.start.pos());
824 selection.cursor = cursor;
825 setCursor(selection.end.par(), selection.end.pos());
827 setCursor(tmpcursor.par(), tmpcursor.pos());
829 bv()->updateInset(inset_owner);
833 // set the counter of a paragraph. This includes the labels
834 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
836 LyXTextClass const & textclass = buf.params.getLyXTextClass();
837 LyXLayout_ptr const & layout = pit->layout();
839 if (pit != ownerParagraphs().begin()) {
841 pit->params().appendix(boost::prior(pit)->params().appendix());
842 if (!pit->params().appendix() &&
843 pit->params().startOfAppendix()) {
844 pit->params().appendix(true);
845 textclass.counters().reset();
847 pit->enumdepth = boost::prior(pit)->enumdepth;
848 pit->itemdepth = boost::prior(pit)->itemdepth;
850 pit->params().appendix(pit->params().startOfAppendix());
855 // Maybe we have to increment the enumeration depth.
856 // BUT, enumeration in a footnote is considered in isolation from its
857 // surrounding paragraph so don't increment if this is the
858 // first line of the footnote
859 // AND, bibliographies can't have their depth changed ie. they
860 // are always of depth 0
861 if (pit != ownerParagraphs().begin()
862 && boost::prior(pit)->getDepth() < pit->getDepth()
863 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
864 && pit->enumdepth < 3
865 && layout->labeltype != LABEL_BIBLIO) {
869 // Maybe we have to decrement the enumeration depth, see note above
870 if (pit != ownerParagraphs().begin()
871 && boost::prior(pit)->getDepth() > pit->getDepth()
872 && layout->labeltype != LABEL_BIBLIO) {
873 pit->enumdepth = depthHook(pit, ownerParagraphs(),
874 pit->getDepth())->enumdepth;
877 if (!pit->params().labelString().empty()) {
878 pit->params().labelString(string());
881 if (layout->margintype == MARGIN_MANUAL) {
882 if (pit->params().labelWidthString().empty())
883 pit->setLabelWidthString(layout->labelstring());
885 pit->setLabelWidthString(string());
888 // is it a layout that has an automatic label?
889 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
890 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
894 if (i >= 0 && i <= buf.params.secnumdepth) {
898 textclass.counters().step(layout->latexname());
900 // Is there a label? Useful for Chapter layout
901 if (!pit->params().appendix()) {
902 s << buf.B_(layout->labelstring());
904 s << buf.B_(layout->labelstring_appendix());
907 // Use of an integer is here less than elegant. For now.
908 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
909 if (!pit->params().appendix()) {
910 numbertype = "sectioning";
912 numbertype = "appendix";
913 if (pit->isRightToLeftPar(buf.params))
920 << textclass.counters()
921 .numberLabel(layout->latexname(),
922 numbertype, langtype, head);
924 pit->params().labelString(STRCONV(s.str()));
926 // reset enum counters
927 textclass.counters().reset("enum");
928 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
929 textclass.counters().reset("enum");
930 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
932 // Yes I know this is a really, really! bad solution
934 string enumcounter("enum");
936 switch (pit->enumdepth) {
948 // not a valid enumdepth...
952 textclass.counters().step(enumcounter);
954 s << textclass.counters()
955 .numberLabel(enumcounter, "enumeration");
956 pit->params().labelString(STRCONV(s.str()));
958 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
959 textclass.counters().step("bibitem");
960 int number = textclass.counters().value("bibitem");
961 if (pit->bibitem()) {
962 pit->bibitem()->setCounter(number);
963 pit->params().labelString(layout->labelstring());
965 // In biblio should't be following counters but...
967 string s = buf.B_(layout->labelstring());
970 if (layout->labeltype == LABEL_SENSITIVE) {
971 ParagraphList::iterator end = ownerParagraphs().end();
972 ParagraphList::iterator tmppit = pit;
975 while (tmppit != end && tmppit->inInset()
976 // the single '=' is intended below
977 && (in = tmppit->inInset()->owner()))
979 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
980 in->lyxCode() == InsetOld::WRAP_CODE) {
984 Paragraph const * owner = &ownerPar(buf, in);
985 tmppit = ownerParagraphs().begin();
986 for ( ; tmppit != end; ++tmppit)
987 if (&*tmppit == owner)
995 if (in->lyxCode() == InsetOld::FLOAT_CODE)
996 type = static_cast<InsetFloat*>(in)->params().type;
997 else if (in->lyxCode() == InsetOld::WRAP_CODE)
998 type = static_cast<InsetWrap*>(in)->params().type;
1002 Floating const & fl = textclass.floats().getType(type);
1004 textclass.counters().step(fl.type());
1006 // Doesn't work... yet.
1007 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1009 // par->SetLayout(0);
1010 // s = layout->labelstring;
1011 s = _("Senseless: ");
1014 pit->params().labelString(s);
1016 // reset the enumeration counter. They are always reset
1017 // when there is any other layout between
1018 // Just fall-through between the cases so that all
1019 // enum counters deeper than enumdepth is also reset.
1020 switch (pit->enumdepth) {
1022 textclass.counters().reset("enumi");
1024 textclass.counters().reset("enumii");
1026 textclass.counters().reset("enumiii");
1028 textclass.counters().reset("enumiv");
1034 // Updates all counters. Paragraphs with changed label string will be rebroken
1035 void LyXText::updateCounters()
1038 bv()->buffer()->params.getLyXTextClass().counters().reset();
1040 ParagraphList::iterator beg = ownerParagraphs().begin();
1041 ParagraphList::iterator end = ownerParagraphs().end();
1042 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1043 string const oldLabel = pit->params().labelString();
1045 size_t maxdepth = 0;
1047 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1049 if (pit->params().depth() > maxdepth)
1050 pit->params().depth(maxdepth);
1052 // setCounter can potentially change the labelString.
1053 setCounter(*bv()->buffer(), pit);
1055 string const & newLabel = pit->params().labelString();
1057 if (oldLabel != newLabel)
1063 void LyXText::insertInset(InsetOld * inset)
1065 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1067 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1069 cursor.par()->insertInset(cursor.pos(), inset);
1070 // Just to rebreak and refresh correctly.
1071 // The character will not be inserted a second time
1072 insertChar(Paragraph::META_INSET);
1073 // If we enter a highly editable inset the cursor should be before
1074 // the inset. After an Undo LyX tries to call inset->edit(...)
1075 // and fails if the cursor is behind the inset and getInset
1076 // does not return the inset!
1077 if (isHighlyEditableInset(inset))
1083 void LyXText::cutSelection(bool doclear, bool realcut)
1085 // Stuff what we got on the clipboard. Even if there is no selection.
1087 // There is a problem with having the stuffing here in that the
1088 // larger the selection the slower LyX will get. This can be
1089 // solved by running the line below only when the selection has
1090 // finished. The solution used currently just works, to make it
1091 // faster we need to be more clever and probably also have more
1092 // calls to stuffClipboard. (Lgb)
1093 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1095 // This doesn't make sense, if there is no selection
1096 if (!selection.set())
1099 // OK, we have a selection. This is always between selection.start
1100 // and selection.end
1102 // make sure that the depth behind the selection are restored, too
1103 ParagraphList::iterator endpit = boost::next(selection.end.par());
1104 ParagraphList::iterator undoendpit = endpit;
1105 ParagraphList::iterator pars_end = ownerParagraphs().end();
1107 if (endpit != pars_end && endpit->getDepth()) {
1108 while (endpit != pars_end && endpit->getDepth()) {
1110 undoendpit = endpit;
1112 } else if (endpit != pars_end) {
1113 // because of parindents etc.
1117 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1118 boost::prior(undoendpit));
1120 endpit = selection.end.par();
1121 int endpos = selection.end.pos();
1123 boost::tie(endpit, endpos) = realcut ?
1124 CutAndPaste::cutSelection(bv()->buffer()->params,
1126 selection.start.par(), endpit,
1127 selection.start.pos(), endpos,
1128 bv()->buffer()->params.textclass,
1130 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1132 selection.start.par(), endpit,
1133 selection.start.pos(), endpos,
1135 // sometimes necessary
1137 selection.start.par()->stripLeadingSpaces();
1139 redoParagraphs(selection.start.par(), boost::next(endpit));
1140 // cutSelection can invalidate the cursor so we need to set
1142 // we prefer the end for when tracking changes
1146 // need a valid cursor. (Lgb)
1149 setCursor(cursor.par(), cursor.pos());
1150 selection.cursor = cursor;
1155 void LyXText::copySelection()
1157 // stuff the selection onto the X clipboard, from an explicit copy request
1158 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1160 // this doesnt make sense, if there is no selection
1161 if (!selection.set())
1164 // ok we have a selection. This is always between selection.start
1165 // and sel_end cursor
1167 // copy behind a space if there is one
1168 while (selection.start.par()->size() > selection.start.pos()
1169 && selection.start.par()->isLineSeparator(selection.start.pos())
1170 && (selection.start.par() != selection.end.par()
1171 || selection.start.pos() < selection.end.pos()))
1172 selection.start.pos(selection.start.pos() + 1);
1174 CutAndPaste::copySelection(selection.start.par(),
1175 selection.end.par(),
1176 selection.start.pos(), selection.end.pos(),
1177 bv()->buffer()->params.textclass);
1181 void LyXText::pasteSelection(size_t sel_index)
1183 // this does not make sense, if there is nothing to paste
1184 if (!CutAndPaste::checkPastePossible())
1187 recordUndo(bv(), Undo::INSERT, cursor.par());
1189 ParagraphList::iterator endpit;
1194 boost::tie(ppp, endpit) =
1195 CutAndPaste::pasteSelection(*bv()->buffer(),
1197 cursor.par(), cursor.pos(),
1198 bv()->buffer()->params.textclass,
1200 bufferErrors(*bv()->buffer(), el);
1201 bv()->showErrorList(_("Paste"));
1203 redoParagraphs(cursor.par(), endpit);
1205 setCursor(cursor.par(), cursor.pos());
1208 selection.cursor = cursor;
1209 setCursor(ppp.first, ppp.second);
1215 void LyXText::setSelectionRange(lyx::pos_type length)
1220 selection.cursor = cursor;
1227 // simple replacing. The font of the first selected character is used
1228 void LyXText::replaceSelectionWithString(string const & str)
1230 recordUndo(bv(), Undo::ATOMIC);
1233 if (!selection.set()) { // create a dummy selection
1234 selection.end = cursor;
1235 selection.start = cursor;
1238 // Get font setting before we cut
1239 pos_type pos = selection.end.pos();
1240 LyXFont const font = selection.start.par()
1241 ->getFontSettings(bv()->buffer()->params,
1242 selection.start.pos());
1244 // Insert the new string
1245 string::const_iterator cit = str.begin();
1246 string::const_iterator end = str.end();
1247 for (; cit != end; ++cit) {
1248 selection.end.par()->insertChar(pos, (*cit), font);
1252 // Cut the selection
1253 cutSelection(true, false);
1259 // needed to insert the selection
1260 void LyXText::insertStringAsLines(string const & str)
1262 ParagraphList::iterator pit = cursor.par();
1263 pos_type pos = cursor.pos();
1264 ParagraphList::iterator endpit = boost::next(cursor.par());
1266 recordUndo(bv(), Undo::ATOMIC);
1268 // only to be sure, should not be neccessary
1271 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1273 redoParagraphs(cursor.par(), endpit);
1274 setCursor(cursor.par(), cursor.pos());
1275 selection.cursor = cursor;
1276 setCursor(pit, pos);
1281 // turns double-CR to single CR, others where converted into one
1282 // blank. Then InsertStringAsLines is called
1283 void LyXText::insertStringAsParagraphs(string const & str)
1285 string linestr(str);
1286 bool newline_inserted = false;
1287 string::size_type const siz = linestr.length();
1289 for (string::size_type i = 0; i < siz; ++i) {
1290 if (linestr[i] == '\n') {
1291 if (newline_inserted) {
1292 // we know that \r will be ignored by
1293 // InsertStringA. Of course, it is a dirty
1294 // trick, but it works...
1295 linestr[i - 1] = '\r';
1299 newline_inserted = true;
1301 } else if (IsPrintable(linestr[i])) {
1302 newline_inserted = false;
1305 insertStringAsLines(linestr);
1309 bool LyXText::setCursor(ParagraphList::iterator pit,
1311 bool setfont, bool boundary)
1313 LyXCursor old_cursor = cursor;
1314 setCursorIntern(pit, pos, setfont, boundary);
1315 return deleteEmptyParagraphMechanism(old_cursor);
1319 void LyXText::redoCursor()
1321 #warning maybe the same for selections?
1322 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1326 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1327 pos_type pos, bool boundary)
1329 Assert(pit != ownerParagraphs().end());
1333 cur.boundary(boundary);
1337 // get the cursor y position in text
1339 RowList::iterator row = getRow(pit, pos);
1342 // y is now the beginning of the cursor row
1343 y += row->baseline();
1344 // y is now the cursor baseline
1347 pos_type last = lastPos(*pit, row);
1349 // None of these should happen, but we're scaredy-cats
1350 if (pos > pit->size()) {
1351 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1354 } else if (pos > last + 1) {
1355 lyxerr << "dont like 2 please report" << endl;
1356 // This shouldn't happen.
1359 } else if (pos < row->pos()) {
1360 lyxerr << "dont like 3 please report" << endl;
1365 // now get the cursors x position
1366 float x = getCursorX(pit, row, pos, last, boundary);
1372 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1373 pos_type pos, pos_type last, bool boundary) const
1375 pos_type cursor_vpos = 0;
1376 double x = rit->x();
1377 double fill_separator = rit->fill_separator();
1378 double fill_hfill = rit->fill_hfill();
1379 double fill_label_hfill = rit->fill_label_hfill();
1380 pos_type const rit_pos = rit->pos();
1383 cursor_vpos = rit_pos;
1384 else if (pos > last && !boundary)
1385 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1386 ? rit_pos : last + 1;
1387 else if (pos > rit_pos && (pos > last || boundary))
1388 // Place cursor after char at (logical) position pos - 1
1389 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1390 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1392 // Place cursor before char at (logical) position pos
1393 cursor_vpos = (bidi_level(pos) % 2 == 0)
1394 ? log2vis(pos) : log2vis(pos) + 1;
1396 pos_type body_pos = pit->beginningOfBody();
1398 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1401 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1402 pos_type pos = vis2log(vpos);
1403 if (body_pos > 0 && pos == body_pos - 1) {
1404 x += fill_label_hfill +
1405 font_metrics::width(
1406 pit->layout()->labelsep, getLabelFont(pit));
1407 if (pit->isLineSeparator(body_pos - 1))
1408 x -= singleWidth(pit, body_pos - 1);
1411 if (hfillExpansion(*pit, rit, pos)) {
1412 x += singleWidth(pit, pos);
1413 if (pos >= body_pos)
1416 x += fill_label_hfill;
1417 } else if (pit->isSeparator(pos)) {
1418 x += singleWidth(pit, pos);
1419 if (pos >= body_pos)
1420 x += fill_separator;
1422 x += singleWidth(pit, pos);
1428 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1429 pos_type pos, bool setfont, bool boundary)
1431 setCursor(cursor, pit, pos, boundary);
1437 void LyXText::setCurrentFont()
1439 pos_type pos = cursor.pos();
1440 ParagraphList::iterator pit = cursor.par();
1442 if (cursor.boundary() && pos > 0)
1446 if (pos == pit->size())
1448 else // potentional bug... BUG (Lgb)
1449 if (pit->isSeparator(pos)) {
1450 if (pos > cursorRow()->pos() &&
1451 bidi_level(pos) % 2 ==
1452 bidi_level(pos - 1) % 2)
1454 else if (pos + 1 < pit->size())
1459 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1460 real_current_font = getFont(pit, pos);
1462 if (cursor.pos() == pit->size() &&
1463 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1464 !cursor.boundary()) {
1465 Language const * lang =
1466 pit->getParLanguage(bv()->buffer()->params);
1467 current_font.setLanguage(lang);
1468 current_font.setNumber(LyXFont::OFF);
1469 real_current_font.setLanguage(lang);
1470 real_current_font.setNumber(LyXFont::OFF);
1475 // returns the column near the specified x-coordinate of the row
1476 // x is set to the real beginning of this column
1477 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1478 RowList::iterator rit, int & x, bool & boundary) const
1480 double tmpx = rit->x();
1481 double fill_separator = rit->fill_separator();
1482 double fill_hfill = rit->fill_hfill();
1483 double fill_label_hfill = rit->fill_label_hfill();
1485 pos_type vc = rit->pos();
1486 pos_type last = lastPos(*pit, rit);
1488 LyXLayout_ptr const & layout = pit->layout();
1490 bool left_side = false;
1492 pos_type body_pos = pit->beginningOfBody();
1493 double last_tmpx = tmpx;
1496 (body_pos - 1 > last ||
1497 !pit->isLineSeparator(body_pos - 1)))
1500 // check for empty row
1506 while (vc <= last && tmpx <= x) {
1509 if (body_pos > 0 && c == body_pos - 1) {
1510 tmpx += fill_label_hfill +
1511 font_metrics::width(layout->labelsep, getLabelFont(pit));
1512 if (pit->isLineSeparator(body_pos - 1))
1513 tmpx -= singleWidth(pit, body_pos - 1);
1516 if (hfillExpansion(*pit, rit, c)) {
1517 tmpx += singleWidth(pit, c);
1521 tmpx += fill_label_hfill;
1522 } else if (pit->isSeparator(c)) {
1523 tmpx += singleWidth(pit, c);
1525 tmpx += fill_separator;
1527 tmpx += singleWidth(pit, c);
1532 if ((tmpx + last_tmpx) / 2 > x) {
1537 if (vc > last + 1) // This shouldn't happen.
1541 // This (rtl_support test) is not needed, but gives
1542 // some speedup if rtl_support == false
1543 bool const lastrow = lyxrc.rtl_support
1544 && boost::next(rit) == pit->rows.end();
1546 // If lastrow is false, we don't need to compute
1547 // the value of rtl.
1548 bool const rtl = (lastrow)
1549 ? pit->isRightToLeftPar(bv()->buffer()->params)
1552 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1553 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1555 else if (vc == rit->pos()) {
1557 if (bidi_level(c) % 2 == 1)
1560 c = vis2log(vc - 1);
1561 bool const rtl = (bidi_level(c) % 2 == 1);
1562 if (left_side == rtl) {
1564 boundary = isBoundary(*bv()->buffer(), *pit, c);
1568 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1569 if (bidi_level(last) % 2 == 0)
1570 tmpx -= singleWidth(pit, last);
1572 tmpx += singleWidth(pit, last);
1582 void LyXText::setCursorFromCoordinates(int x, int y)
1584 LyXCursor old_cursor = cursor;
1585 setCursorFromCoordinates(cursor, x, y);
1587 deleteEmptyParagraphMechanism(old_cursor);
1591 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1593 // Get the row first.
1594 ParagraphList::iterator pit;
1595 RowList::iterator rit = getRowNearY(y, pit);
1599 pos_type const column = getColumnNearX(pit, rit, x, bound);
1601 cur.pos(rit->pos() + column);
1603 cur.y(y + rit->baseline());
1605 cur.boundary(bound);
1609 void LyXText::cursorLeft(bool internal)
1611 if (cursor.pos() > 0) {
1612 bool boundary = cursor.boundary();
1613 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1614 if (!internal && !boundary &&
1615 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1616 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1617 } else if (cursor.par() != ownerParagraphs().begin()) {
1618 // steps into the paragraph above
1619 ParagraphList::iterator pit = boost::prior(cursor.par());
1620 setCursor(pit, pit->size());
1625 void LyXText::cursorRight(bool internal)
1627 bool const at_end = (cursor.pos() == cursor.par()->size());
1628 bool const at_newline = !at_end &&
1629 cursor.par()->isNewline(cursor.pos());
1631 if (!internal && cursor.boundary() && !at_newline)
1632 setCursor(cursor.par(), cursor.pos(), true, false);
1634 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1636 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1637 setCursor(cursor.par(), cursor.pos(), true, true);
1638 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1639 setCursor(boost::next(cursor.par()), 0);
1643 void LyXText::cursorUp(bool selecting)
1646 int x = cursor.x_fix();
1647 int y = cursor.y() - cursorRow()->baseline() - 1;
1648 setCursorFromCoordinates(x, y);
1650 int topy = bv_owner->top_y();
1651 int y1 = cursor.y() - topy;
1654 InsetOld * inset_hit = checkInsetHit(x, y1);
1655 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1656 inset_hit->localDispatch(
1657 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1661 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1662 cursorRow()->baseline() << endl;
1663 setCursorFromCoordinates(cursor.x_fix(),
1664 cursor.y() - cursorRow()->baseline() - 1);
1669 void LyXText::cursorDown(bool selecting)
1672 int x = cursor.x_fix();
1673 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1674 setCursorFromCoordinates(x, y);
1675 if (!selecting && cursorRow() == cursorIRow()) {
1676 int topy = bv_owner->top_y();
1677 int y1 = cursor.y() - topy;
1680 InsetOld * inset_hit = checkInsetHit(x, y1);
1681 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1682 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1683 inset_hit->localDispatch(cmd);
1687 setCursorFromCoordinates(cursor.x_fix(),
1688 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1693 void LyXText::cursorUpParagraph()
1695 if (cursor.pos() > 0)
1696 setCursor(cursor.par(), 0);
1697 else if (cursor.par() != ownerParagraphs().begin())
1698 setCursor(boost::prior(cursor.par()), 0);
1702 void LyXText::cursorDownParagraph()
1704 ParagraphList::iterator par = cursor.par();
1705 ParagraphList::iterator next_par = boost::next(par);
1707 if (next_par != ownerParagraphs().end())
1708 setCursor(next_par, 0);
1710 setCursor(par, par->size());
1714 // fix the cursor `cur' after a characters has been deleted at `where'
1715 // position. Called by deleteEmptyParagraphMechanism
1716 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1718 // if cursor is not in the paragraph where the delete occured,
1720 if (cur.par() != where.par())
1723 // if cursor position is after the place where the delete occured,
1725 if (cur.pos() > where.pos())
1726 cur.pos(cur.pos()-1);
1728 // check also if we don't want to set the cursor on a spot behind the
1729 // pagragraph because we erased the last character.
1730 if (cur.pos() > cur.par()->size())
1731 cur.pos(cur.par()->size());
1733 // recompute row et al. for this cursor
1734 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1738 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1740 // Would be wrong to delete anything if we have a selection.
1741 if (selection.set())
1744 // We allow all kinds of "mumbo-jumbo" when freespacing.
1745 if (old_cursor.par()->isFreeSpacing())
1748 /* Ok I'll put some comments here about what is missing.
1749 I have fixed BackSpace (and thus Delete) to not delete
1750 double-spaces automagically. I have also changed Cut,
1751 Copy and Paste to hopefully do some sensible things.
1752 There are still some small problems that can lead to
1753 double spaces stored in the document file or space at
1754 the beginning of paragraphs. This happens if you have
1755 the cursor between to spaces and then save. Or if you
1756 cut and paste and the selection have a space at the
1757 beginning and then save right after the paste. I am
1758 sure none of these are very hard to fix, but I will
1759 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1760 that I can get some feedback. (Lgb)
1763 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1764 // delete the LineSeparator.
1767 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1768 // delete the LineSeparator.
1771 // If the pos around the old_cursor were spaces, delete one of them.
1772 if (old_cursor.par() != cursor.par()
1773 || old_cursor.pos() != cursor.pos()) {
1775 // Only if the cursor has really moved
1776 if (old_cursor.pos() > 0
1777 && old_cursor.pos() < old_cursor.par()->size()
1778 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1779 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1780 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1781 redoParagraph(old_cursor.par());
1785 #ifdef WITH_WARNINGS
1786 #warning This will not work anymore when we have multiple views of the same buffer
1787 // In this case, we will have to correct also the cursors held by
1788 // other bufferviews. It will probably be easier to do that in a more
1789 // automated way in LyXCursor code. (JMarc 26/09/2001)
1791 // correct all cursors held by the LyXText
1792 fixCursorAfterDelete(cursor, old_cursor);
1793 fixCursorAfterDelete(selection.cursor, old_cursor);
1794 fixCursorAfterDelete(selection.start, old_cursor);
1795 fixCursorAfterDelete(selection.end, old_cursor);
1800 // don't delete anything if this is the ONLY paragraph!
1801 if (ownerParagraphs().size() == 1)
1804 // Do not delete empty paragraphs with keepempty set.
1805 if (old_cursor.par()->allowEmpty())
1808 // only do our magic if we changed paragraph
1809 if (old_cursor.par() == cursor.par())
1812 // record if we have deleted a paragraph
1813 // we can't possibly have deleted a paragraph before this point
1814 bool deleted = false;
1816 if (old_cursor.par()->empty() ||
1817 (old_cursor.par()->size() == 1 &&
1818 old_cursor.par()->isLineSeparator(0))) {
1819 // ok, we will delete something
1820 LyXCursor tmpcursor;
1824 bool selection_position_was_oldcursor_position = (
1825 selection.cursor.par() == old_cursor.par()
1826 && selection.cursor.pos() == old_cursor.pos());
1829 cursor = old_cursor; // that undo can restore the right cursor position
1831 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1832 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1835 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1839 ownerParagraphs().erase(old_cursor.par());
1843 setCursorIntern(cursor.par(), cursor.pos());
1845 if (selection_position_was_oldcursor_position) {
1846 // correct selection
1847 selection.cursor = cursor;
1851 if (old_cursor.par()->stripLeadingSpaces()) {
1852 redoParagraph(old_cursor.par());
1854 setCursorIntern(cursor.par(), cursor.pos());
1855 selection.cursor = cursor;
1862 ParagraphList & LyXText::ownerParagraphs() const
1868 bool LyXText::isInInset() const
1870 // Sub-level has non-null bv owner and non-null inset owner.
1871 return inset_owner != 0;
1875 int defaultRowHeight()
1877 LyXFont const font(LyXFont::ALL_SANE);
1878 return int(font_metrics::maxAscent(font)
1879 + font_metrics::maxDescent(font) * 1.5);