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;
66 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
67 ParagraphList & paragraphs)
68 : height(0), width(0), anchor_y_(0),
69 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
70 in_inset_(ininset), paragraphs_(paragraphs)
75 void LyXText::init(BufferView * bview)
79 ParagraphList::iterator const beg = ownerParagraphs().begin();
80 ParagraphList::iterator const end = ownerParagraphs().end();
81 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
89 current_font = getFont(beg, 0);
91 redoParagraphs(beg, end);
92 setCursorIntern(beg, 0);
93 selection.cursor = cursor;
99 // Gets the fully instantiated font at a given position in a paragraph
100 // Basically the same routine as Paragraph::getFont() in paragraph.C.
101 // The difference is that this one is used for displaying, and thus we
102 // are allowed to make cosmetic improvements. For instance make footnotes
104 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
108 LyXLayout_ptr const & layout = pit->layout();
110 BufferParams const & params = bv()->buffer()->params;
112 // We specialize the 95% common case:
113 if (!pit->getDepth()) {
114 if (layout->labeltype == LABEL_MANUAL
115 && pos < pit->beginningOfBody()) {
117 LyXFont f = pit->getFontSettings(params, pos);
119 pit->inInset()->getDrawFont(f);
120 return f.realize(layout->reslabelfont);
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->resfont);
129 // The uncommon case need not be optimized as much
133 if (pos < pit->beginningOfBody()) {
135 layoutfont = layout->labelfont;
138 layoutfont = layout->font;
141 LyXFont tmpfont = pit->getFontSettings(params, pos);
142 tmpfont.realize(layoutfont);
145 pit->inInset()->getDrawFont(tmpfont);
147 // Realize with the fonts of lesser depth.
148 tmpfont.realize(outerFont(pit, ownerParagraphs()));
149 tmpfont.realize(defaultfont_);
155 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
157 LyXLayout_ptr const & layout = pit->layout();
159 if (!pit->getDepth())
160 return layout->resfont;
162 LyXFont font = layout->font;
163 // Realize with the fonts of lesser depth.
164 font.realize(outerFont(pit, ownerParagraphs()));
165 font.realize(defaultfont_);
171 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
173 LyXLayout_ptr const & layout = pit->layout();
175 if (!pit->getDepth())
176 return layout->reslabelfont;
178 LyXFont font = layout->labelfont;
179 // Realize with the fonts of lesser depth.
180 font.realize(outerFont(pit, ownerParagraphs()));
181 font.realize(defaultfont_);
187 void LyXText::setCharFont(ParagraphList::iterator pit,
188 pos_type pos, LyXFont const & fnt,
191 BufferParams const & params = bv()->buffer()->params;
192 LyXFont font = getFont(pit, pos);
193 font.update(fnt, params.language, toggleall);
194 // Let the insets convert their font
195 if (pit->isInset(pos)) {
196 InsetOld * inset = pit->getInset(pos);
197 if (isEditableInset(inset)) {
198 static_cast<UpdatableInset *>(inset)
199 ->setFont(bv(), fnt, toggleall, true);
203 // Plug through to version below:
204 setCharFont(pit, pos, font);
208 void LyXText::setCharFont(
209 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
212 LyXLayout_ptr const & layout = pit->layout();
214 // Get concrete layout font to reduce against
217 if (pos < pit->beginningOfBody())
218 layoutfont = layout->labelfont;
220 layoutfont = layout->font;
222 // Realize against environment font information
223 if (pit->getDepth()) {
224 ParagraphList::iterator tp = pit;
225 while (!layoutfont.resolved() &&
226 tp != ownerParagraphs().end() &&
228 tp = outerHook(tp, ownerParagraphs());
229 if (tp != ownerParagraphs().end())
230 layoutfont.realize(tp->layout()->font);
234 layoutfont.realize(defaultfont_);
236 // Now, reduce font against full layout font
237 font.reduce(layoutfont);
239 pit->setFont(pos, font);
243 InsetOld * LyXText::getInset() const
245 ParagraphList::iterator pit = cursor.par();
246 pos_type const pos = cursor.pos();
248 if (pos < pit->size() && pit->isInset(pos)) {
249 return pit->getInset(pos);
255 void LyXText::toggleInset()
257 InsetOld * inset = getInset();
258 // is there an editable inset at cursor position?
259 if (!isEditableInset(inset)) {
260 // No, try to see if we are inside a collapsable inset
261 if (inset_owner && inset_owner->owner()
262 && inset_owner->owner()->isOpen()) {
263 bv()->unlockInset(inset_owner->owner());
264 inset_owner->owner()->close(bv());
265 bv()->getLyXText()->cursorRight(bv());
269 //bv()->owner()->message(inset->editMessage());
271 // do we want to keep this?? (JMarc)
272 if (!isHighlyEditableInset(inset))
273 recordUndo(bv(), Undo::ATOMIC);
280 bv()->updateInset(inset);
284 /* used in setlayout */
285 // Asger is not sure we want to do this...
286 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
289 LyXLayout_ptr const & layout = par.layout();
290 pos_type const psize = par.size();
293 for (pos_type pos = 0; pos < psize; ++pos) {
294 if (pos < par.beginningOfBody())
295 layoutfont = layout->labelfont;
297 layoutfont = layout->font;
299 LyXFont tmpfont = par.getFontSettings(params, pos);
300 tmpfont.reduce(layoutfont);
301 par.setFont(pos, tmpfont);
306 ParagraphList::iterator
307 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
308 LyXCursor & send_cur,
309 string const & layout)
311 ParagraphList::iterator endpit = boost::next(send_cur.par());
312 ParagraphList::iterator undoendpit = endpit;
313 ParagraphList::iterator pars_end = ownerParagraphs().end();
315 if (endpit != pars_end && endpit->getDepth()) {
316 while (endpit != pars_end && endpit->getDepth()) {
320 } else if (endpit != pars_end) {
321 // because of parindents etc.
325 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
327 // ok we have a selection. This is always between sstart_cur
328 // and sel_end cursor
330 ParagraphList::iterator pit = sstart_cur.par();
331 ParagraphList::iterator epit = boost::next(send_cur.par());
333 LyXLayout_ptr const & lyxlayout =
334 bv()->buffer()->params.getLyXTextClass()[layout];
337 pit->applyLayout(lyxlayout);
338 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
339 pit->params().spaceTop(lyxlayout->fill_top ?
340 VSpace(VSpace::VFILL)
341 : VSpace(VSpace::NONE));
342 pit->params().spaceBottom(lyxlayout->fill_bottom ?
343 VSpace(VSpace::VFILL)
344 : VSpace(VSpace::NONE));
345 if (lyxlayout->margintype == MARGIN_MANUAL)
346 pit->setLabelWidthString(lyxlayout->labelstring());
349 } while (pit != epit);
355 // set layout over selection and make a total rebreak of those paragraphs
356 void LyXText::setLayout(string const & layout)
358 LyXCursor tmpcursor = cursor; // store the current cursor
360 // if there is no selection just set the layout
361 // of the current paragraph
362 if (!selection.set()) {
363 selection.start = cursor; // dummy selection
364 selection.end = cursor;
367 // special handling of new environment insets
368 BufferParams const & params = bv()->buffer()->params;
369 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
370 if (lyxlayout->is_environment) {
371 // move everything in a new environment inset
372 lyxerr << "setting layout " << layout << endl;
373 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
374 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
375 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
376 InsetOld * inset = new InsetEnvironment(params, layout);
377 if (bv()->insertInset(inset)) {
379 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
386 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
387 selection.end, layout);
388 redoParagraphs(selection.start.par(), endpit);
390 // we have to reset the selection, because the
391 // geometry could have changed
392 setCursor(selection.start.par(), selection.start.pos(), false);
393 selection.cursor = cursor;
394 setCursor(selection.end.par(), selection.end.pos(), false);
398 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
402 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
404 ParagraphList::iterator pit = cursor.par();
405 ParagraphList::iterator end = cursor.par();
406 ParagraphList::iterator start = pit;
408 if (selection.set()) {
409 pit = selection.start.par();
410 end = selection.end.par();
414 ParagraphList::iterator pastend = boost::next(end);
417 recordUndo(bv(), Undo::ATOMIC, start, end);
419 bool changed = false;
421 int prev_after_depth = 0;
422 #warning parlist ... could be nicer ?
423 if (start != ownerParagraphs().begin()) {
424 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
428 int const depth = pit->params().depth();
429 if (type == bv_funcs::INC_DEPTH) {
430 if (depth < prev_after_depth
431 && pit->layout()->labeltype != LABEL_BIBLIO) {
434 pit->params().depth(depth + 1);
439 pit->params().depth(depth - 1);
442 prev_after_depth = pit->getMaxDepthAfter();
454 redoParagraphs(start, pastend);
456 // We need to actually move the text->cursor. I don't
457 // understand why ...
458 LyXCursor tmpcursor = cursor;
460 // we have to reset the visual selection because the
461 // geometry could have changed
462 if (selection.set()) {
463 setCursor(selection.start.par(), selection.start.pos());
464 selection.cursor = cursor;
465 setCursor(selection.end.par(), selection.end.pos());
468 // this handles the counter labels, and also fixes up
469 // depth values for follow-on (child) paragraphs
473 setCursor(tmpcursor.par(), tmpcursor.pos());
479 // set font over selection and make a total rebreak of those paragraphs
480 void LyXText::setFont(LyXFont const & font, bool toggleall)
482 // if there is no selection just set the current_font
483 if (!selection.set()) {
484 // Determine basis font
486 if (cursor.pos() < cursor.par()->beginningOfBody()) {
487 layoutfont = getLabelFont(cursor.par());
489 layoutfont = getLayoutFont(cursor.par());
491 // Update current font
492 real_current_font.update(font,
493 bv()->buffer()->params.language,
496 // Reduce to implicit settings
497 current_font = real_current_font;
498 current_font.reduce(layoutfont);
499 // And resolve it completely
500 real_current_font.realize(layoutfont);
505 LyXCursor tmpcursor = cursor; // store the current cursor
507 // ok we have a selection. This is always between sel_start_cursor
508 // and sel_end cursor
510 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
512 cursor = selection.start;
513 while (cursor.par() != selection.end.par() ||
514 cursor.pos() < selection.end.pos())
516 if (cursor.pos() < cursor.par()->size()) {
517 // an open footnote should behave like a closed one
518 setCharFont(cursor.par(), cursor.pos(),
520 cursor.pos(cursor.pos() + 1);
523 cursor.par(boost::next(cursor.par()));
528 redoParagraph(selection.start.par());
530 // we have to reset the selection, because the
531 // geometry could have changed, but we keep
532 // it for user convenience
533 setCursor(selection.start.par(), selection.start.pos());
534 selection.cursor = cursor;
535 setCursor(selection.end.par(), selection.end.pos());
537 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
538 tmpcursor.boundary());
542 int LyXText::redoParagraphInternal(ParagraphList::iterator pit)
544 RowList::iterator rit = pit->rows.begin();
545 RowList::iterator end = pit->rows.end();
547 // remove rows of paragraph, keep track of height changes
548 for (int i = 0; rit != end; ++rit, ++i)
549 height -= rit->height();
553 InsetList::iterator ii = pit->insetlist.begin();
554 InsetList::iterator iend = pit->insetlist.end();
555 for (; ii != iend; ++ii) {
557 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
558 ii->inset->metrics(mi, dim);
561 // rebreak the paragraph
562 for (pos_type z = 0; z < pit->size() + 1; ) {
564 z = rowBreakPoint(pit, row) + 1;
566 pit->rows.push_back(row);
570 // set height and fill and width of rows
571 int const ww = workWidth();
572 for (rit = pit->rows.begin(); rit != end; ++rit) {
573 int const f = fill(pit, rit, ww);
574 int const w = ww - f;
575 par_width = std::max(par_width, w);
578 prepareToPrint(pit, rit);
579 setHeightOfRow(pit, rit);
580 height += rit->height();
583 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
588 int LyXText::redoParagraphs(ParagraphList::iterator start,
589 ParagraphList::iterator end)
592 for ( ; start != end; ++start) {
593 int par_width = redoParagraphInternal(start);
594 pars_width = std::max(par_width, pars_width);
596 updateRowPositions();
601 void LyXText::redoParagraph(ParagraphList::iterator pit)
603 redoParagraphInternal(pit);
604 updateRowPositions();
608 void LyXText::fullRebreak()
610 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
612 selection.cursor = cursor;
616 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
618 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
619 // << " workWidth: " << workWidth() << endl;
620 //Assert(mi.base.textwidth);
627 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
630 dim.asc = firstRow()->ascent_of_text();
631 dim.des = height - dim.asc;
632 dim.wid = std::max(mi.base.textwidth, int(width));
636 // important for the screen
639 // the cursor set functions have a special mechanism. When they
640 // realize, that you left an empty paragraph, they will delete it.
641 // They also delete the corresponding row
643 // need the selection cursor:
644 void LyXText::setSelection()
646 TextCursor::setSelection();
651 void LyXText::clearSelection()
653 TextCursor::clearSelection();
655 // reset this in the bv_owner!
656 if (bv_owner && bv_owner->text)
657 bv_owner->text->xsel_cache.set(false);
661 void LyXText::cursorHome()
663 setCursor(cursor.par(), cursorRow()->pos());
667 void LyXText::cursorEnd()
669 setCursor(cursor.par(), cursorRow()->end() - 1);
673 void LyXText::cursorTop()
675 setCursor(ownerParagraphs().begin(), 0);
679 void LyXText::cursorBottom()
681 ParagraphList::iterator lastpit =
682 boost::prior(ownerParagraphs().end());
683 setCursor(lastpit, lastpit->size());
687 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
689 // If the mask is completely neutral, tell user
690 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
691 // Could only happen with user style
692 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
696 // Try implicit word selection
697 // If there is a change in the language the implicit word selection
699 LyXCursor resetCursor = cursor;
700 bool implicitSelection = (font.language() == ignore_language
701 && font.number() == LyXFont::IGNORE)
702 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
705 setFont(font, toggleall);
707 // Implicit selections are cleared afterwards
708 //and cursor is set to the original position.
709 if (implicitSelection) {
711 cursor = resetCursor;
712 setCursor(cursor.par(), cursor.pos());
713 selection.cursor = cursor;
718 string LyXText::getStringToIndex()
720 // Try implicit word selection
721 // If there is a change in the language the implicit word selection
723 LyXCursor const reset_cursor = cursor;
724 bool const implicitSelection =
725 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
728 if (!selection.set())
729 bv()->owner()->message(_("Nothing to index!"));
730 else if (selection.start.par() != selection.end.par())
731 bv()->owner()->message(_("Cannot index more than one paragraph!"));
733 idxstring = selectionAsString(*bv()->buffer(), false);
735 // Reset cursors to their original position.
736 cursor = reset_cursor;
737 setCursor(cursor.par(), cursor.pos());
738 selection.cursor = cursor;
740 // Clear the implicit selection.
741 if (implicitSelection)
748 // the DTP switches for paragraphs. LyX will store them in the first
749 // physical paragraph. When a paragraph is broken, the top settings rest,
750 // the bottom settings are given to the new one. So I can make sure,
751 // they do not duplicate themself and you cannnot make dirty things with
754 void LyXText::setParagraph(bool line_top, bool line_bottom,
755 bool pagebreak_top, bool pagebreak_bottom,
756 VSpace const & space_top,
757 VSpace const & space_bottom,
758 Spacing const & spacing,
760 string const & labelwidthstring,
763 LyXCursor tmpcursor = cursor;
764 if (!selection.set()) {
765 selection.start = cursor;
766 selection.end = cursor;
769 // make sure that the depth behind the selection are restored, too
770 ParagraphList::iterator endpit = boost::next(selection.end.par());
771 ParagraphList::iterator undoendpit = endpit;
772 ParagraphList::iterator pars_end = ownerParagraphs().end();
774 if (endpit != pars_end && endpit->getDepth()) {
775 while (endpit != pars_end && endpit->getDepth()) {
779 } else if (endpit != pars_end) {
780 // because of parindents etc.
784 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
785 boost::prior(undoendpit));
788 ParagraphList::iterator tmppit = selection.end.par();
790 while (tmppit != boost::prior(selection.start.par())) {
791 setCursor(tmppit, 0);
793 ParagraphList::iterator pit = cursor.par();
794 ParagraphParameters & params = pit->params();
796 params.lineTop(line_top);
797 params.lineBottom(line_bottom);
798 params.pagebreakTop(pagebreak_top);
799 params.pagebreakBottom(pagebreak_bottom);
800 params.spaceTop(space_top);
801 params.spaceBottom(space_bottom);
802 params.spacing(spacing);
803 // does the layout allow the new alignment?
804 LyXLayout_ptr const & layout = pit->layout();
806 if (align == LYX_ALIGN_LAYOUT)
807 align = layout->align;
808 if (align & layout->alignpossible) {
809 if (align == layout->align)
810 params.align(LYX_ALIGN_LAYOUT);
814 pit->setLabelWidthString(labelwidthstring);
815 params.noindent(noindent);
816 tmppit = boost::prior(pit);
819 redoParagraphs(selection.start.par(), endpit);
822 setCursor(selection.start.par(), selection.start.pos());
823 selection.cursor = cursor;
824 setCursor(selection.end.par(), selection.end.pos());
826 setCursor(tmpcursor.par(), tmpcursor.pos());
828 bv()->updateInset(inset_owner);
832 // set the counter of a paragraph. This includes the labels
833 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
835 LyXTextClass const & textclass = buf.params.getLyXTextClass();
836 LyXLayout_ptr const & layout = pit->layout();
838 if (pit != ownerParagraphs().begin()) {
840 pit->params().appendix(boost::prior(pit)->params().appendix());
841 if (!pit->params().appendix() &&
842 pit->params().startOfAppendix()) {
843 pit->params().appendix(true);
844 textclass.counters().reset();
846 pit->enumdepth = boost::prior(pit)->enumdepth;
847 pit->itemdepth = boost::prior(pit)->itemdepth;
849 pit->params().appendix(pit->params().startOfAppendix());
854 // Maybe we have to increment the enumeration depth.
855 // BUT, enumeration in a footnote is considered in isolation from its
856 // surrounding paragraph so don't increment if this is the
857 // first line of the footnote
858 // AND, bibliographies can't have their depth changed ie. they
859 // are always of depth 0
860 if (pit != ownerParagraphs().begin()
861 && boost::prior(pit)->getDepth() < pit->getDepth()
862 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
863 && pit->enumdepth < 3
864 && layout->labeltype != LABEL_BIBLIO) {
868 // Maybe we have to decrement the enumeration depth, see note above
869 if (pit != ownerParagraphs().begin()
870 && boost::prior(pit)->getDepth() > pit->getDepth()
871 && layout->labeltype != LABEL_BIBLIO) {
872 pit->enumdepth = depthHook(pit, ownerParagraphs(),
873 pit->getDepth())->enumdepth;
876 if (!pit->params().labelString().empty()) {
877 pit->params().labelString(string());
880 if (layout->margintype == MARGIN_MANUAL) {
881 if (pit->params().labelWidthString().empty())
882 pit->setLabelWidthString(layout->labelstring());
884 pit->setLabelWidthString(string());
887 // is it a layout that has an automatic label?
888 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
889 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
893 if (i >= 0 && i <= buf.params.secnumdepth) {
897 textclass.counters().step(layout->latexname());
899 // Is there a label? Useful for Chapter layout
900 if (!pit->params().appendix()) {
901 s << buf.B_(layout->labelstring());
903 s << buf.B_(layout->labelstring_appendix());
906 // Use of an integer is here less than elegant. For now.
907 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
908 if (!pit->params().appendix()) {
909 numbertype = "sectioning";
911 numbertype = "appendix";
912 if (pit->isRightToLeftPar(buf.params))
919 << textclass.counters()
920 .numberLabel(layout->latexname(),
921 numbertype, langtype, head);
923 pit->params().labelString(STRCONV(s.str()));
925 // reset enum counters
926 textclass.counters().reset("enum");
927 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
928 textclass.counters().reset("enum");
929 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
931 // Yes I know this is a really, really! bad solution
933 string enumcounter("enum");
935 switch (pit->enumdepth) {
947 // not a valid enumdepth...
951 textclass.counters().step(enumcounter);
953 s << textclass.counters()
954 .numberLabel(enumcounter, "enumeration");
955 pit->params().labelString(STRCONV(s.str()));
957 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
958 textclass.counters().step("bibitem");
959 int number = textclass.counters().value("bibitem");
960 if (pit->bibitem()) {
961 pit->bibitem()->setCounter(number);
962 pit->params().labelString(layout->labelstring());
964 // In biblio should't be following counters but...
966 string s = buf.B_(layout->labelstring());
969 if (layout->labeltype == LABEL_SENSITIVE) {
970 ParagraphList::iterator end = ownerParagraphs().end();
971 ParagraphList::iterator tmppit = pit;
974 while (tmppit != end && tmppit->inInset()
975 // the single '=' is intended below
976 && (in = tmppit->inInset()->owner()))
978 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
979 in->lyxCode() == InsetOld::WRAP_CODE) {
983 Paragraph const * owner = &ownerPar(buf, in);
984 tmppit = ownerParagraphs().begin();
985 for ( ; tmppit != end; ++tmppit)
986 if (&*tmppit == owner)
994 if (in->lyxCode() == InsetOld::FLOAT_CODE)
995 type = static_cast<InsetFloat*>(in)->params().type;
996 else if (in->lyxCode() == InsetOld::WRAP_CODE)
997 type = static_cast<InsetWrap*>(in)->params().type;
1001 Floating const & fl = textclass.floats().getType(type);
1003 textclass.counters().step(fl.type());
1005 // Doesn't work... yet.
1006 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1008 // par->SetLayout(0);
1009 // s = layout->labelstring;
1010 s = _("Senseless: ");
1013 pit->params().labelString(s);
1015 // reset the enumeration counter. They are always reset
1016 // when there is any other layout between
1017 // Just fall-through between the cases so that all
1018 // enum counters deeper than enumdepth is also reset.
1019 switch (pit->enumdepth) {
1021 textclass.counters().reset("enumi");
1023 textclass.counters().reset("enumii");
1025 textclass.counters().reset("enumiii");
1027 textclass.counters().reset("enumiv");
1033 // Updates all counters. Paragraphs with changed label string will be rebroken
1034 void LyXText::updateCounters()
1037 bv()->buffer()->params.getLyXTextClass().counters().reset();
1039 ParagraphList::iterator beg = ownerParagraphs().begin();
1040 ParagraphList::iterator end = ownerParagraphs().end();
1041 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1042 string const oldLabel = pit->params().labelString();
1044 size_t maxdepth = 0;
1046 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1048 if (pit->params().depth() > maxdepth)
1049 pit->params().depth(maxdepth);
1051 // setCounter can potentially change the labelString.
1052 setCounter(*bv()->buffer(), pit);
1054 string const & newLabel = pit->params().labelString();
1056 if (oldLabel != newLabel)
1062 void LyXText::insertInset(InsetOld * inset)
1064 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1066 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1068 cursor.par()->insertInset(cursor.pos(), inset);
1069 // Just to rebreak and refresh correctly.
1070 // The character will not be inserted a second time
1071 insertChar(Paragraph::META_INSET);
1072 // If we enter a highly editable inset the cursor should be before
1073 // the inset. After an Undo LyX tries to call inset->edit(...)
1074 // and fails if the cursor is behind the inset and getInset
1075 // does not return the inset!
1076 if (isHighlyEditableInset(inset))
1082 void LyXText::cutSelection(bool doclear, bool realcut)
1084 // Stuff what we got on the clipboard. Even if there is no selection.
1086 // There is a problem with having the stuffing here in that the
1087 // larger the selection the slower LyX will get. This can be
1088 // solved by running the line below only when the selection has
1089 // finished. The solution used currently just works, to make it
1090 // faster we need to be more clever and probably also have more
1091 // calls to stuffClipboard. (Lgb)
1092 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1094 // This doesn't make sense, if there is no selection
1095 if (!selection.set())
1098 // OK, we have a selection. This is always between selection.start
1099 // and selection.end
1101 // make sure that the depth behind the selection are restored, too
1102 ParagraphList::iterator endpit = boost::next(selection.end.par());
1103 ParagraphList::iterator undoendpit = endpit;
1104 ParagraphList::iterator pars_end = ownerParagraphs().end();
1106 if (endpit != pars_end && endpit->getDepth()) {
1107 while (endpit != pars_end && endpit->getDepth()) {
1109 undoendpit = endpit;
1111 } else if (endpit != pars_end) {
1112 // because of parindents etc.
1116 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1117 boost::prior(undoendpit));
1119 endpit = selection.end.par();
1120 int endpos = selection.end.pos();
1122 boost::tie(endpit, endpos) = realcut ?
1123 CutAndPaste::cutSelection(bv()->buffer()->params,
1125 selection.start.par(), endpit,
1126 selection.start.pos(), endpos,
1127 bv()->buffer()->params.textclass,
1129 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1131 selection.start.par(), endpit,
1132 selection.start.pos(), endpos,
1134 // sometimes necessary
1136 selection.start.par()->stripLeadingSpaces();
1138 redoParagraphs(selection.start.par(), boost::next(endpit));
1139 // cutSelection can invalidate the cursor so we need to set
1141 // we prefer the end for when tracking changes
1145 // need a valid cursor. (Lgb)
1148 setCursor(cursor.par(), cursor.pos());
1149 selection.cursor = cursor;
1154 void LyXText::copySelection()
1156 // stuff the selection onto the X clipboard, from an explicit copy request
1157 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1159 // this doesnt make sense, if there is no selection
1160 if (!selection.set())
1163 // ok we have a selection. This is always between selection.start
1164 // and sel_end cursor
1166 // copy behind a space if there is one
1167 while (selection.start.par()->size() > selection.start.pos()
1168 && selection.start.par()->isLineSeparator(selection.start.pos())
1169 && (selection.start.par() != selection.end.par()
1170 || selection.start.pos() < selection.end.pos()))
1171 selection.start.pos(selection.start.pos() + 1);
1173 CutAndPaste::copySelection(selection.start.par(),
1174 selection.end.par(),
1175 selection.start.pos(), selection.end.pos(),
1176 bv()->buffer()->params.textclass);
1180 void LyXText::pasteSelection(size_t sel_index)
1182 // this does not make sense, if there is nothing to paste
1183 if (!CutAndPaste::checkPastePossible())
1186 recordUndo(bv(), Undo::INSERT, cursor.par());
1188 ParagraphList::iterator endpit;
1193 boost::tie(ppp, endpit) =
1194 CutAndPaste::pasteSelection(*bv()->buffer(),
1196 cursor.par(), cursor.pos(),
1197 bv()->buffer()->params.textclass,
1199 bufferErrors(*bv()->buffer(), el);
1200 bv()->showErrorList(_("Paste"));
1202 redoParagraphs(cursor.par(), endpit);
1204 setCursor(cursor.par(), cursor.pos());
1207 selection.cursor = cursor;
1208 setCursor(ppp.first, ppp.second);
1214 void LyXText::setSelectionRange(lyx::pos_type length)
1219 selection.cursor = cursor;
1226 // simple replacing. The font of the first selected character is used
1227 void LyXText::replaceSelectionWithString(string const & str)
1229 recordUndo(bv(), Undo::ATOMIC);
1232 if (!selection.set()) { // create a dummy selection
1233 selection.end = cursor;
1234 selection.start = cursor;
1237 // Get font setting before we cut
1238 pos_type pos = selection.end.pos();
1239 LyXFont const font = selection.start.par()
1240 ->getFontSettings(bv()->buffer()->params,
1241 selection.start.pos());
1243 // Insert the new string
1244 string::const_iterator cit = str.begin();
1245 string::const_iterator end = str.end();
1246 for (; cit != end; ++cit) {
1247 selection.end.par()->insertChar(pos, (*cit), font);
1251 // Cut the selection
1252 cutSelection(true, false);
1258 // needed to insert the selection
1259 void LyXText::insertStringAsLines(string const & str)
1261 ParagraphList::iterator pit = cursor.par();
1262 pos_type pos = cursor.pos();
1263 ParagraphList::iterator endpit = boost::next(cursor.par());
1265 recordUndo(bv(), Undo::ATOMIC);
1267 // only to be sure, should not be neccessary
1270 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1272 redoParagraphs(cursor.par(), endpit);
1273 setCursor(cursor.par(), cursor.pos());
1274 selection.cursor = cursor;
1275 setCursor(pit, pos);
1280 // turns double-CR to single CR, others where converted into one
1281 // blank. Then InsertStringAsLines is called
1282 void LyXText::insertStringAsParagraphs(string const & str)
1284 string linestr(str);
1285 bool newline_inserted = false;
1286 string::size_type const siz = linestr.length();
1288 for (string::size_type i = 0; i < siz; ++i) {
1289 if (linestr[i] == '\n') {
1290 if (newline_inserted) {
1291 // we know that \r will be ignored by
1292 // InsertStringA. Of course, it is a dirty
1293 // trick, but it works...
1294 linestr[i - 1] = '\r';
1298 newline_inserted = true;
1300 } else if (IsPrintable(linestr[i])) {
1301 newline_inserted = false;
1304 insertStringAsLines(linestr);
1308 bool LyXText::setCursor(ParagraphList::iterator pit,
1310 bool setfont, bool boundary)
1312 LyXCursor old_cursor = cursor;
1313 setCursorIntern(pit, pos, setfont, boundary);
1314 return deleteEmptyParagraphMechanism(old_cursor);
1318 void LyXText::redoCursor()
1320 #warning maybe the same for selections?
1321 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1325 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1326 pos_type pos, bool boundary)
1328 Assert(pit != ownerParagraphs().end());
1332 cur.boundary(boundary);
1336 // get the cursor y position in text
1338 RowList::iterator row = getRow(pit, pos);
1341 // y is now the beginning of the cursor row
1342 y += row->baseline();
1343 // y is now the cursor baseline
1346 pos_type last = lastPos(*pit, row);
1348 // None of these should happen, but we're scaredy-cats
1349 if (pos > pit->size()) {
1350 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1353 } else if (pos > last + 1) {
1354 lyxerr << "dont like 2 please report" << endl;
1355 // This shouldn't happen.
1358 } else if (pos < row->pos()) {
1359 lyxerr << "dont like 3 please report" << endl;
1364 // now get the cursors x position
1365 float x = getCursorX(pit, row, pos, last, boundary);
1371 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1372 pos_type pos, pos_type last, bool boundary) const
1374 pos_type cursor_vpos = 0;
1375 double x = rit->x();
1376 double fill_separator = rit->fill_separator();
1377 double fill_hfill = rit->fill_hfill();
1378 double fill_label_hfill = rit->fill_label_hfill();
1379 pos_type const rit_pos = rit->pos();
1382 cursor_vpos = rit_pos;
1383 else if (pos > last && !boundary)
1384 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1385 ? rit_pos : last + 1;
1386 else if (pos > rit_pos && (pos > last || boundary))
1387 // Place cursor after char at (logical) position pos - 1
1388 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1389 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1391 // Place cursor before char at (logical) position pos
1392 cursor_vpos = (bidi_level(pos) % 2 == 0)
1393 ? log2vis(pos) : log2vis(pos) + 1;
1395 pos_type body_pos = pit->beginningOfBody();
1397 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1400 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1401 pos_type pos = vis2log(vpos);
1402 if (body_pos > 0 && pos == body_pos - 1) {
1403 x += fill_label_hfill +
1404 font_metrics::width(
1405 pit->layout()->labelsep, getLabelFont(pit));
1406 if (pit->isLineSeparator(body_pos - 1))
1407 x -= singleWidth(pit, body_pos - 1);
1410 if (hfillExpansion(*pit, rit, pos)) {
1411 x += singleWidth(pit, pos);
1412 if (pos >= body_pos)
1415 x += fill_label_hfill;
1416 } else if (pit->isSeparator(pos)) {
1417 x += singleWidth(pit, pos);
1418 if (pos >= body_pos)
1419 x += fill_separator;
1421 x += singleWidth(pit, pos);
1427 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1428 pos_type pos, bool setfont, bool boundary)
1430 setCursor(cursor, pit, pos, boundary);
1436 void LyXText::setCurrentFont()
1438 pos_type pos = cursor.pos();
1439 ParagraphList::iterator pit = cursor.par();
1441 if (cursor.boundary() && pos > 0)
1445 if (pos == pit->size())
1447 else // potentional bug... BUG (Lgb)
1448 if (pit->isSeparator(pos)) {
1449 if (pos > cursorRow()->pos() &&
1450 bidi_level(pos) % 2 ==
1451 bidi_level(pos - 1) % 2)
1453 else if (pos + 1 < pit->size())
1458 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1459 real_current_font = getFont(pit, pos);
1461 if (cursor.pos() == pit->size() &&
1462 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1463 !cursor.boundary()) {
1464 Language const * lang =
1465 pit->getParLanguage(bv()->buffer()->params);
1466 current_font.setLanguage(lang);
1467 current_font.setNumber(LyXFont::OFF);
1468 real_current_font.setLanguage(lang);
1469 real_current_font.setNumber(LyXFont::OFF);
1474 // returns the column near the specified x-coordinate of the row
1475 // x is set to the real beginning of this column
1476 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1477 RowList::iterator rit, int & x, bool & boundary) const
1479 double tmpx = rit->x();
1480 double fill_separator = rit->fill_separator();
1481 double fill_hfill = rit->fill_hfill();
1482 double fill_label_hfill = rit->fill_label_hfill();
1484 pos_type vc = rit->pos();
1485 pos_type last = lastPos(*pit, rit);
1487 LyXLayout_ptr const & layout = pit->layout();
1489 bool left_side = false;
1491 pos_type body_pos = pit->beginningOfBody();
1492 double last_tmpx = tmpx;
1495 (body_pos - 1 > last ||
1496 !pit->isLineSeparator(body_pos - 1)))
1499 // check for empty row
1505 while (vc <= last && tmpx <= x) {
1508 if (body_pos > 0 && c == body_pos - 1) {
1509 tmpx += fill_label_hfill +
1510 font_metrics::width(layout->labelsep, getLabelFont(pit));
1511 if (pit->isLineSeparator(body_pos - 1))
1512 tmpx -= singleWidth(pit, body_pos - 1);
1515 if (hfillExpansion(*pit, rit, c)) {
1516 tmpx += singleWidth(pit, c);
1520 tmpx += fill_label_hfill;
1521 } else if (pit->isSeparator(c)) {
1522 tmpx += singleWidth(pit, c);
1524 tmpx += fill_separator;
1526 tmpx += singleWidth(pit, c);
1531 if ((tmpx + last_tmpx) / 2 > x) {
1536 if (vc > last + 1) // This shouldn't happen.
1540 // This (rtl_support test) is not needed, but gives
1541 // some speedup if rtl_support == false
1542 bool const lastrow = lyxrc.rtl_support
1543 && boost::next(rit) == pit->rows.end();
1545 // If lastrow is false, we don't need to compute
1546 // the value of rtl.
1547 bool const rtl = (lastrow)
1548 ? pit->isRightToLeftPar(bv()->buffer()->params)
1551 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1552 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1554 else if (vc == rit->pos()) {
1556 if (bidi_level(c) % 2 == 1)
1559 c = vis2log(vc - 1);
1560 bool const rtl = (bidi_level(c) % 2 == 1);
1561 if (left_side == rtl) {
1563 boundary = isBoundary(*bv()->buffer(), *pit, c);
1567 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1568 if (bidi_level(last) % 2 == 0)
1569 tmpx -= singleWidth(pit, last);
1571 tmpx += singleWidth(pit, last);
1581 void LyXText::setCursorFromCoordinates(int x, int y)
1583 LyXCursor old_cursor = cursor;
1584 setCursorFromCoordinates(cursor, x, y);
1586 deleteEmptyParagraphMechanism(old_cursor);
1590 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1592 // Get the row first.
1593 ParagraphList::iterator pit;
1594 RowList::iterator rit = getRowNearY(y, pit);
1598 pos_type const column = getColumnNearX(pit, rit, x, bound);
1600 cur.pos(rit->pos() + column);
1602 cur.y(y + rit->baseline());
1604 cur.boundary(bound);
1608 void LyXText::cursorLeft(bool internal)
1610 if (cursor.pos() > 0) {
1611 bool boundary = cursor.boundary();
1612 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1613 if (!internal && !boundary &&
1614 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1615 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1616 } else if (cursor.par() != ownerParagraphs().begin()) {
1617 // steps into the paragraph above
1618 ParagraphList::iterator pit = boost::prior(cursor.par());
1619 setCursor(pit, pit->size());
1624 void LyXText::cursorRight(bool internal)
1626 bool const at_end = (cursor.pos() == cursor.par()->size());
1627 bool const at_newline = !at_end &&
1628 cursor.par()->isNewline(cursor.pos());
1630 if (!internal && cursor.boundary() && !at_newline)
1631 setCursor(cursor.par(), cursor.pos(), true, false);
1633 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1635 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1636 setCursor(cursor.par(), cursor.pos(), true, true);
1637 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1638 setCursor(boost::next(cursor.par()), 0);
1642 void LyXText::cursorUp(bool selecting)
1645 int x = cursor.x_fix();
1646 int y = cursor.y() - cursorRow()->baseline() - 1;
1647 setCursorFromCoordinates(x, y);
1649 int topy = bv_owner->top_y();
1650 int y1 = cursor.y() - topy;
1653 InsetOld * inset_hit = checkInsetHit(x, y1);
1654 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1655 inset_hit->localDispatch(
1656 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1660 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1661 cursorRow()->baseline() << endl;
1662 setCursorFromCoordinates(cursor.x_fix(),
1663 cursor.y() - cursorRow()->baseline() - 1);
1668 void LyXText::cursorDown(bool selecting)
1671 int x = cursor.x_fix();
1672 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1673 setCursorFromCoordinates(x, y);
1674 if (!selecting && cursorRow() == cursorIRow()) {
1675 int topy = bv_owner->top_y();
1676 int y1 = cursor.y() - topy;
1679 InsetOld * inset_hit = checkInsetHit(x, y1);
1680 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1681 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1682 inset_hit->localDispatch(cmd);
1686 setCursorFromCoordinates(cursor.x_fix(),
1687 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1692 void LyXText::cursorUpParagraph()
1694 if (cursor.pos() > 0)
1695 setCursor(cursor.par(), 0);
1696 else if (cursor.par() != ownerParagraphs().begin())
1697 setCursor(boost::prior(cursor.par()), 0);
1701 void LyXText::cursorDownParagraph()
1703 ParagraphList::iterator par = cursor.par();
1704 ParagraphList::iterator next_par = boost::next(par);
1706 if (next_par != ownerParagraphs().end())
1707 setCursor(next_par, 0);
1709 setCursor(par, par->size());
1713 // fix the cursor `cur' after a characters has been deleted at `where'
1714 // position. Called by deleteEmptyParagraphMechanism
1715 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1717 // if cursor is not in the paragraph where the delete occured,
1719 if (cur.par() != where.par())
1722 // if cursor position is after the place where the delete occured,
1724 if (cur.pos() > where.pos())
1725 cur.pos(cur.pos()-1);
1727 // check also if we don't want to set the cursor on a spot behind the
1728 // pagragraph because we erased the last character.
1729 if (cur.pos() > cur.par()->size())
1730 cur.pos(cur.par()->size());
1732 // recompute row et al. for this cursor
1733 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1737 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1739 // Would be wrong to delete anything if we have a selection.
1740 if (selection.set())
1743 // We allow all kinds of "mumbo-jumbo" when freespacing.
1744 if (old_cursor.par()->isFreeSpacing())
1747 /* Ok I'll put some comments here about what is missing.
1748 I have fixed BackSpace (and thus Delete) to not delete
1749 double-spaces automagically. I have also changed Cut,
1750 Copy and Paste to hopefully do some sensible things.
1751 There are still some small problems that can lead to
1752 double spaces stored in the document file or space at
1753 the beginning of paragraphs. This happens if you have
1754 the cursor between to spaces and then save. Or if you
1755 cut and paste and the selection have a space at the
1756 beginning and then save right after the paste. I am
1757 sure none of these are very hard to fix, but I will
1758 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1759 that I can get some feedback. (Lgb)
1762 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1763 // delete the LineSeparator.
1766 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1767 // delete the LineSeparator.
1770 // If the pos around the old_cursor were spaces, delete one of them.
1771 if (old_cursor.par() != cursor.par()
1772 || old_cursor.pos() != cursor.pos()) {
1774 // Only if the cursor has really moved
1775 if (old_cursor.pos() > 0
1776 && old_cursor.pos() < old_cursor.par()->size()
1777 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1778 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1779 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1780 redoParagraph(old_cursor.par());
1784 #ifdef WITH_WARNINGS
1785 #warning This will not work anymore when we have multiple views of the same buffer
1786 // In this case, we will have to correct also the cursors held by
1787 // other bufferviews. It will probably be easier to do that in a more
1788 // automated way in LyXCursor code. (JMarc 26/09/2001)
1790 // correct all cursors held by the LyXText
1791 fixCursorAfterDelete(cursor, old_cursor);
1792 fixCursorAfterDelete(selection.cursor, old_cursor);
1793 fixCursorAfterDelete(selection.start, old_cursor);
1794 fixCursorAfterDelete(selection.end, old_cursor);
1799 // don't delete anything if this is the ONLY paragraph!
1800 if (ownerParagraphs().size() == 1)
1803 // Do not delete empty paragraphs with keepempty set.
1804 if (old_cursor.par()->allowEmpty())
1807 // only do our magic if we changed paragraph
1808 if (old_cursor.par() == cursor.par())
1811 // record if we have deleted a paragraph
1812 // we can't possibly have deleted a paragraph before this point
1813 bool deleted = false;
1815 if (old_cursor.par()->empty() ||
1816 (old_cursor.par()->size() == 1 &&
1817 old_cursor.par()->isLineSeparator(0))) {
1818 // ok, we will delete something
1819 LyXCursor tmpcursor;
1823 bool selection_position_was_oldcursor_position = (
1824 selection.cursor.par() == old_cursor.par()
1825 && selection.cursor.pos() == old_cursor.pos());
1828 cursor = old_cursor; // that undo can restore the right cursor position
1830 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1831 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1834 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1838 ownerParagraphs().erase(old_cursor.par());
1842 setCursorIntern(cursor.par(), cursor.pos());
1844 if (selection_position_was_oldcursor_position) {
1845 // correct selection
1846 selection.cursor = cursor;
1850 if (old_cursor.par()->stripLeadingSpaces()) {
1851 redoParagraph(old_cursor.par());
1853 setCursorIntern(cursor.par(), cursor.pos());
1854 selection.cursor = cursor;
1861 ParagraphList & LyXText::ownerParagraphs() const
1867 bool LyXText::isInInset() const
1869 // Sub-level has non-null bv owner and non-null inset owner.
1870 return inset_owner != 0;
1874 int defaultRowHeight()
1876 LyXFont const font(LyXFont::ALL_SANE);
1877 return int(font_metrics::maxAscent(font)
1878 + font_metrics::maxDescent(font) * 1.5);