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 "BufferView.h"
28 #include "CutAndPaste.h"
30 #include "errorlist.h"
32 #include "FloatList.h"
33 #include "funcrequest.h"
38 #include "lyxrow_funcs.h"
39 #include "metricsinfo.h"
40 #include "paragraph_funcs.h"
41 #include "ParagraphParameters.h"
42 #include "undo_funcs.h"
44 #include "frontends/font_metrics.h"
45 #include "frontends/LyXView.h"
47 #include "insets/insetbibitem.h"
48 #include "insets/insetenv.h"
49 #include "insets/insetfloat.h"
50 #include "insets/insetwrap.h"
52 #include "support/LAssert.h"
53 #include "support/lstrings.h"
54 #include "support/textutils.h"
56 #include <boost/tuple/tuple.hpp>
58 #include "support/std_sstream.h"
60 using namespace lyx::support;
62 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_(paragraphs)
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(beg, 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
113 LyXLayout_ptr const & layout = pit->layout();
115 BufferParams const & params = bv()->buffer()->params;
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 if (layout->labeltype == LABEL_MANUAL
120 && pos < pit->beginningOfBody()) {
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->reslabelfont);
127 LyXFont f = pit->getFontSettings(params, pos);
129 pit->inInset()->getDrawFont(f);
130 return f.realize(layout->resfont);
134 // The uncommon case need not be optimized as much
138 if (pos < pit->beginningOfBody()) {
140 layoutfont = layout->labelfont;
143 layoutfont = layout->font;
146 LyXFont tmpfont = pit->getFontSettings(params, pos);
147 tmpfont.realize(layoutfont);
150 pit->inInset()->getDrawFont(tmpfont);
152 // Realize with the fonts of lesser depth.
153 tmpfont.realize(outerFont(pit, ownerParagraphs()));
154 tmpfont.realize(defaultfont_);
160 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
162 LyXLayout_ptr const & layout = pit->layout();
164 if (!pit->getDepth())
165 return layout->resfont;
167 LyXFont font = layout->font;
168 // Realize with the fonts of lesser depth.
169 font.realize(outerFont(pit, ownerParagraphs()));
170 font.realize(defaultfont_);
176 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
178 LyXLayout_ptr const & layout = pit->layout();
180 if (!pit->getDepth())
181 return layout->reslabelfont;
183 LyXFont font = layout->labelfont;
184 // Realize with the fonts of lesser depth.
185 font.realize(outerFont(pit, ownerParagraphs()));
186 font.realize(defaultfont_);
192 void LyXText::setCharFont(ParagraphList::iterator pit,
193 pos_type pos, LyXFont const & fnt,
196 BufferParams const & params = bv()->buffer()->params;
197 LyXFont font = getFont(pit, pos);
198 font.update(fnt, params.language, toggleall);
199 // Let the insets convert their font
200 if (pit->isInset(pos)) {
201 InsetOld * inset = pit->getInset(pos);
202 if (isEditableInset(inset)) {
203 static_cast<UpdatableInset *>(inset)
204 ->setFont(bv(), fnt, toggleall, true);
208 // Plug through to version below:
209 setCharFont(pit, pos, font);
213 void LyXText::setCharFont(
214 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
217 LyXLayout_ptr const & layout = pit->layout();
219 // Get concrete layout font to reduce against
222 if (pos < pit->beginningOfBody())
223 layoutfont = layout->labelfont;
225 layoutfont = layout->font;
227 // Realize against environment font information
228 if (pit->getDepth()) {
229 ParagraphList::iterator tp = pit;
230 while (!layoutfont.resolved() &&
231 tp != ownerParagraphs().end() &&
233 tp = outerHook(tp, ownerParagraphs());
234 if (tp != ownerParagraphs().end())
235 layoutfont.realize(tp->layout()->font);
239 layoutfont.realize(defaultfont_);
241 // Now, reduce font against full layout font
242 font.reduce(layoutfont);
244 pit->setFont(pos, font);
248 InsetOld * LyXText::getInset() const
250 ParagraphList::iterator pit = cursor.par();
251 pos_type const pos = cursor.pos();
253 if (pos < pit->size() && pit->isInset(pos)) {
254 return pit->getInset(pos);
260 void LyXText::toggleInset()
262 InsetOld * inset = getInset();
263 // is there an editable inset at cursor position?
264 if (!isEditableInset(inset)) {
265 // No, try to see if we are inside a collapsable inset
266 if (inset_owner && inset_owner->owner()
267 && inset_owner->owner()->isOpen()) {
268 bv()->unlockInset(inset_owner->owner());
269 inset_owner->owner()->close(bv());
270 bv()->getLyXText()->cursorRight(bv());
274 //bv()->owner()->message(inset->editMessage());
276 // do we want to keep this?? (JMarc)
277 if (!isHighlyEditableInset(inset))
278 recordUndo(bv(), Undo::ATOMIC);
285 bv()->updateInset(inset);
289 /* used in setlayout */
290 // Asger is not sure we want to do this...
291 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
294 LyXLayout_ptr const & layout = par.layout();
295 pos_type const psize = par.size();
298 for (pos_type pos = 0; pos < psize; ++pos) {
299 if (pos < par.beginningOfBody())
300 layoutfont = layout->labelfont;
302 layoutfont = layout->font;
304 LyXFont tmpfont = par.getFontSettings(params, pos);
305 tmpfont.reduce(layoutfont);
306 par.setFont(pos, tmpfont);
311 ParagraphList::iterator
312 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
313 LyXCursor & send_cur,
314 string const & layout)
316 ParagraphList::iterator endpit = boost::next(send_cur.par());
317 ParagraphList::iterator undoendpit = endpit;
318 ParagraphList::iterator pars_end = ownerParagraphs().end();
320 if (endpit != pars_end && endpit->getDepth()) {
321 while (endpit != pars_end && endpit->getDepth()) {
325 } else if (endpit != pars_end) {
326 // because of parindents etc.
330 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
332 // ok we have a selection. This is always between sstart_cur
333 // and sel_end cursor
335 ParagraphList::iterator pit = sstart_cur.par();
336 ParagraphList::iterator epit = boost::next(send_cur.par());
338 LyXLayout_ptr const & lyxlayout =
339 bv()->buffer()->params.getLyXTextClass()[layout];
342 pit->applyLayout(lyxlayout);
343 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
344 pit->params().spaceTop(lyxlayout->fill_top ?
345 VSpace(VSpace::VFILL)
346 : VSpace(VSpace::NONE));
347 pit->params().spaceBottom(lyxlayout->fill_bottom ?
348 VSpace(VSpace::VFILL)
349 : VSpace(VSpace::NONE));
350 if (lyxlayout->margintype == MARGIN_MANUAL)
351 pit->setLabelWidthString(lyxlayout->labelstring());
354 } while (pit != epit);
360 // set layout over selection and make a total rebreak of those paragraphs
361 void LyXText::setLayout(string const & layout)
363 LyXCursor tmpcursor = cursor; // store the current cursor
365 // if there is no selection just set the layout
366 // of the current paragraph
367 if (!selection.set()) {
368 selection.start = cursor; // dummy selection
369 selection.end = cursor;
372 // special handling of new environment insets
373 BufferParams const & params = bv()->buffer()->params;
374 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
375 if (lyxlayout->is_environment) {
376 // move everything in a new environment inset
377 lyxerr << "setting layout " << layout << endl;
378 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
379 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
380 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
381 InsetOld * inset = new InsetEnvironment(params, layout);
382 if (bv()->insertInset(inset)) {
384 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
391 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
392 selection.end, layout);
393 redoParagraphs(selection.start.par(), endpit);
395 // we have to reset the selection, because the
396 // geometry could have changed
397 setCursor(selection.start.par(), selection.start.pos(), false);
398 selection.cursor = cursor;
399 setCursor(selection.end.par(), selection.end.pos(), false);
403 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
407 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
409 ParagraphList::iterator pit = cursor.par();
410 ParagraphList::iterator end = cursor.par();
411 ParagraphList::iterator start = pit;
413 if (selection.set()) {
414 pit = selection.start.par();
415 end = selection.end.par();
419 ParagraphList::iterator pastend = boost::next(end);
422 recordUndo(bv(), Undo::ATOMIC, start, end);
424 bool changed = false;
426 int prev_after_depth = 0;
427 #warning parlist ... could be nicer ?
428 if (start != ownerParagraphs().begin()) {
429 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
433 int const depth = pit->params().depth();
434 if (type == bv_funcs::INC_DEPTH) {
435 if (depth < prev_after_depth
436 && pit->layout()->labeltype != LABEL_BIBLIO) {
439 pit->params().depth(depth + 1);
444 pit->params().depth(depth - 1);
447 prev_after_depth = pit->getMaxDepthAfter();
459 redoParagraphs(start, pastend);
461 // We need to actually move the text->cursor. I don't
462 // understand why ...
463 LyXCursor tmpcursor = cursor;
465 // we have to reset the visual selection because the
466 // geometry could have changed
467 if (selection.set()) {
468 setCursor(selection.start.par(), selection.start.pos());
469 selection.cursor = cursor;
470 setCursor(selection.end.par(), selection.end.pos());
473 // this handles the counter labels, and also fixes up
474 // depth values for follow-on (child) paragraphs
478 setCursor(tmpcursor.par(), tmpcursor.pos());
484 // set font over selection and make a total rebreak of those paragraphs
485 void LyXText::setFont(LyXFont const & font, bool toggleall)
487 // if there is no selection just set the current_font
488 if (!selection.set()) {
489 // Determine basis font
491 if (cursor.pos() < cursor.par()->beginningOfBody()) {
492 layoutfont = getLabelFont(cursor.par());
494 layoutfont = getLayoutFont(cursor.par());
496 // Update current font
497 real_current_font.update(font,
498 bv()->buffer()->params.language,
501 // Reduce to implicit settings
502 current_font = real_current_font;
503 current_font.reduce(layoutfont);
504 // And resolve it completely
505 real_current_font.realize(layoutfont);
510 LyXCursor tmpcursor = cursor; // store the current cursor
512 // ok we have a selection. This is always between sel_start_cursor
513 // and sel_end cursor
515 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
517 cursor = selection.start;
518 while (cursor.par() != selection.end.par() ||
519 cursor.pos() < selection.end.pos())
521 if (cursor.pos() < cursor.par()->size()) {
522 // an open footnote should behave like a closed one
523 setCharFont(cursor.par(), cursor.pos(),
525 cursor.pos(cursor.pos() + 1);
528 cursor.par(boost::next(cursor.par()));
533 redoParagraph(selection.start.par());
535 // we have to reset the selection, because the
536 // geometry could have changed, but we keep
537 // it for user convenience
538 setCursor(selection.start.par(), selection.start.pos());
539 selection.cursor = cursor;
540 setCursor(selection.end.par(), selection.end.pos());
542 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
543 tmpcursor.boundary());
547 int LyXText::redoParagraphInternal(ParagraphList::iterator pit)
549 RowList::iterator rit = pit->rows.begin();
550 RowList::iterator end = pit->rows.end();
552 // remove rows of paragraph, keep track of height changes
553 for (int i = 0; rit != end; ++rit, ++i)
554 height -= rit->height();
558 InsetList::iterator ii = pit->insetlist.begin();
559 InsetList::iterator iend = pit->insetlist.end();
560 for (; ii != iend; ++ii) {
562 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
563 ii->inset->metrics(mi, dim);
566 // rebreak the paragraph
567 for (pos_type z = 0; z < pit->size() + 1; ) {
569 z = rowBreakPoint(pit, row) + 1;
571 pit->rows.push_back(row);
575 // set height and fill and width of rows
576 int const ww = workWidth();
577 for (rit = pit->rows.begin(); rit != end; ++rit) {
578 int const f = fill(pit, rit, ww);
579 int const w = ww - f;
580 par_width = std::max(par_width, w);
583 prepareToPrint(pit, rit);
584 setHeightOfRow(pit, rit);
585 height += rit->height();
588 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
593 int LyXText::redoParagraphs(ParagraphList::iterator start,
594 ParagraphList::iterator end)
597 for ( ; start != end; ++start) {
598 int par_width = redoParagraphInternal(start);
599 pars_width = std::max(par_width, pars_width);
601 updateRowPositions();
606 void LyXText::redoParagraph(ParagraphList::iterator pit)
608 redoParagraphInternal(pit);
609 updateRowPositions();
613 void LyXText::fullRebreak()
615 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
617 selection.cursor = cursor;
621 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
623 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
624 // << " workWidth: " << workWidth() << endl;
625 //Assert(mi.base.textwidth);
632 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
635 dim.asc = firstRow()->ascent_of_text();
636 dim.des = height - dim.asc;
637 dim.wid = std::max(mi.base.textwidth, int(width));
641 // important for the screen
644 // the cursor set functions have a special mechanism. When they
645 // realize, that you left an empty paragraph, they will delete it.
646 // They also delete the corresponding row
648 // need the selection cursor:
649 void LyXText::setSelection()
651 TextCursor::setSelection();
656 void LyXText::clearSelection()
658 TextCursor::clearSelection();
660 // reset this in the bv_owner!
661 if (bv_owner && bv_owner->text)
662 bv_owner->text->xsel_cache.set(false);
666 void LyXText::cursorHome()
668 setCursor(cursor.par(), cursorRow()->pos());
672 void LyXText::cursorEnd()
674 setCursor(cursor.par(), cursorRow()->end() - 1);
678 void LyXText::cursorTop()
680 setCursor(ownerParagraphs().begin(), 0);
684 void LyXText::cursorBottom()
686 ParagraphList::iterator lastpit =
687 boost::prior(ownerParagraphs().end());
688 setCursor(lastpit, lastpit->size());
692 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
694 // If the mask is completely neutral, tell user
695 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
696 // Could only happen with user style
697 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
701 // Try implicit word selection
702 // If there is a change in the language the implicit word selection
704 LyXCursor resetCursor = cursor;
705 bool implicitSelection = (font.language() == ignore_language
706 && font.number() == LyXFont::IGNORE)
707 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
710 setFont(font, toggleall);
712 // Implicit selections are cleared afterwards
713 //and cursor is set to the original position.
714 if (implicitSelection) {
716 cursor = resetCursor;
717 setCursor(cursor.par(), cursor.pos());
718 selection.cursor = cursor;
723 string LyXText::getStringToIndex()
725 // Try implicit word selection
726 // If there is a change in the language the implicit word selection
728 LyXCursor const reset_cursor = cursor;
729 bool const implicitSelection =
730 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
733 if (!selection.set())
734 bv()->owner()->message(_("Nothing to index!"));
735 else if (selection.start.par() != selection.end.par())
736 bv()->owner()->message(_("Cannot index more than one paragraph!"));
738 idxstring = selectionAsString(*bv()->buffer(), false);
740 // Reset cursors to their original position.
741 cursor = reset_cursor;
742 setCursor(cursor.par(), cursor.pos());
743 selection.cursor = cursor;
745 // Clear the implicit selection.
746 if (implicitSelection)
753 // the DTP switches for paragraphs. LyX will store them in the first
754 // physical paragraph. When a paragraph is broken, the top settings rest,
755 // the bottom settings are given to the new one. So I can make sure,
756 // they do not duplicate themself and you cannnot make dirty things with
759 void LyXText::setParagraph(bool line_top, bool line_bottom,
760 bool pagebreak_top, bool pagebreak_bottom,
761 VSpace const & space_top,
762 VSpace const & space_bottom,
763 Spacing const & spacing,
765 string const & labelwidthstring,
768 LyXCursor tmpcursor = cursor;
769 if (!selection.set()) {
770 selection.start = cursor;
771 selection.end = cursor;
774 // make sure that the depth behind the selection are restored, too
775 ParagraphList::iterator endpit = boost::next(selection.end.par());
776 ParagraphList::iterator undoendpit = endpit;
777 ParagraphList::iterator pars_end = ownerParagraphs().end();
779 if (endpit != pars_end && endpit->getDepth()) {
780 while (endpit != pars_end && endpit->getDepth()) {
784 } else if (endpit != pars_end) {
785 // because of parindents etc.
789 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
790 boost::prior(undoendpit));
793 ParagraphList::iterator tmppit = selection.end.par();
795 while (tmppit != boost::prior(selection.start.par())) {
796 setCursor(tmppit, 0);
798 ParagraphList::iterator pit = cursor.par();
799 ParagraphParameters & params = pit->params();
801 params.lineTop(line_top);
802 params.lineBottom(line_bottom);
803 params.pagebreakTop(pagebreak_top);
804 params.pagebreakBottom(pagebreak_bottom);
805 params.spaceTop(space_top);
806 params.spaceBottom(space_bottom);
807 params.spacing(spacing);
808 // does the layout allow the new alignment?
809 LyXLayout_ptr const & layout = pit->layout();
811 if (align == LYX_ALIGN_LAYOUT)
812 align = layout->align;
813 if (align & layout->alignpossible) {
814 if (align == layout->align)
815 params.align(LYX_ALIGN_LAYOUT);
819 pit->setLabelWidthString(labelwidthstring);
820 params.noindent(noindent);
821 tmppit = boost::prior(pit);
824 redoParagraphs(selection.start.par(), endpit);
827 setCursor(selection.start.par(), selection.start.pos());
828 selection.cursor = cursor;
829 setCursor(selection.end.par(), selection.end.pos());
831 setCursor(tmpcursor.par(), tmpcursor.pos());
833 bv()->updateInset(inset_owner);
837 // set the counter of a paragraph. This includes the labels
838 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
840 LyXTextClass const & textclass = buf.params.getLyXTextClass();
841 LyXLayout_ptr const & layout = pit->layout();
843 if (pit != ownerParagraphs().begin()) {
845 pit->params().appendix(boost::prior(pit)->params().appendix());
846 if (!pit->params().appendix() &&
847 pit->params().startOfAppendix()) {
848 pit->params().appendix(true);
849 textclass.counters().reset();
851 pit->enumdepth = boost::prior(pit)->enumdepth;
852 pit->itemdepth = boost::prior(pit)->itemdepth;
854 pit->params().appendix(pit->params().startOfAppendix());
859 // Maybe we have to increment the enumeration depth.
860 // BUT, enumeration in a footnote is considered in isolation from its
861 // surrounding paragraph so don't increment if this is the
862 // first line of the footnote
863 // AND, bibliographies can't have their depth changed ie. they
864 // are always of depth 0
865 if (pit != ownerParagraphs().begin()
866 && boost::prior(pit)->getDepth() < pit->getDepth()
867 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
868 && pit->enumdepth < 3
869 && layout->labeltype != LABEL_BIBLIO) {
873 // Maybe we have to decrement the enumeration depth, see note above
874 if (pit != ownerParagraphs().begin()
875 && boost::prior(pit)->getDepth() > pit->getDepth()
876 && layout->labeltype != LABEL_BIBLIO) {
877 pit->enumdepth = depthHook(pit, ownerParagraphs(),
878 pit->getDepth())->enumdepth;
881 if (!pit->params().labelString().empty()) {
882 pit->params().labelString(string());
885 if (layout->margintype == MARGIN_MANUAL) {
886 if (pit->params().labelWidthString().empty())
887 pit->setLabelWidthString(layout->labelstring());
889 pit->setLabelWidthString(string());
892 // is it a layout that has an automatic label?
893 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
894 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
898 if (i >= 0 && i <= buf.params.secnumdepth) {
902 textclass.counters().step(layout->latexname());
904 // Is there a label? Useful for Chapter layout
905 if (!pit->params().appendix()) {
906 s << buf.B_(layout->labelstring());
908 s << buf.B_(layout->labelstring_appendix());
911 // Use of an integer is here less than elegant. For now.
912 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
913 if (!pit->params().appendix()) {
914 numbertype = "sectioning";
916 numbertype = "appendix";
917 if (pit->isRightToLeftPar(buf.params))
924 << textclass.counters()
925 .numberLabel(layout->latexname(),
926 numbertype, langtype, head);
928 pit->params().labelString(STRCONV(s.str()));
930 // reset enum counters
931 textclass.counters().reset("enum");
932 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
933 textclass.counters().reset("enum");
934 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
936 // Yes I know this is a really, really! bad solution
938 string enumcounter("enum");
940 switch (pit->enumdepth) {
952 // not a valid enumdepth...
956 textclass.counters().step(enumcounter);
958 s << textclass.counters()
959 .numberLabel(enumcounter, "enumeration");
960 pit->params().labelString(STRCONV(s.str()));
962 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
963 textclass.counters().step("bibitem");
964 int number = textclass.counters().value("bibitem");
965 if (pit->bibitem()) {
966 pit->bibitem()->setCounter(number);
967 pit->params().labelString(layout->labelstring());
969 // In biblio should't be following counters but...
971 string s = buf.B_(layout->labelstring());
974 if (layout->labeltype == LABEL_SENSITIVE) {
975 ParagraphList::iterator end = ownerParagraphs().end();
976 ParagraphList::iterator tmppit = pit;
979 while (tmppit != end && tmppit->inInset()
980 // the single '=' is intended below
981 && (in = tmppit->inInset()->owner()))
983 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
984 in->lyxCode() == InsetOld::WRAP_CODE) {
988 Paragraph const * owner = &ownerPar(buf, in);
989 tmppit = ownerParagraphs().begin();
990 for ( ; tmppit != end; ++tmppit)
991 if (&*tmppit == owner)
999 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1000 type = static_cast<InsetFloat*>(in)->params().type;
1001 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1002 type = static_cast<InsetWrap*>(in)->params().type;
1006 Floating const & fl = textclass.floats().getType(type);
1008 textclass.counters().step(fl.type());
1010 // Doesn't work... yet.
1011 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1013 // par->SetLayout(0);
1014 // s = layout->labelstring;
1015 s = _("Senseless: ");
1018 pit->params().labelString(s);
1020 // reset the enumeration counter. They are always reset
1021 // when there is any other layout between
1022 // Just fall-through between the cases so that all
1023 // enum counters deeper than enumdepth is also reset.
1024 switch (pit->enumdepth) {
1026 textclass.counters().reset("enumi");
1028 textclass.counters().reset("enumii");
1030 textclass.counters().reset("enumiii");
1032 textclass.counters().reset("enumiv");
1038 // Updates all counters. Paragraphs with changed label string will be rebroken
1039 void LyXText::updateCounters()
1042 bv()->buffer()->params.getLyXTextClass().counters().reset();
1044 ParagraphList::iterator beg = ownerParagraphs().begin();
1045 ParagraphList::iterator end = ownerParagraphs().end();
1046 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1047 string const oldLabel = pit->params().labelString();
1049 size_t maxdepth = 0;
1051 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1053 if (pit->params().depth() > maxdepth)
1054 pit->params().depth(maxdepth);
1056 // setCounter can potentially change the labelString.
1057 setCounter(*bv()->buffer(), pit);
1059 string const & newLabel = pit->params().labelString();
1061 if (oldLabel != newLabel)
1067 void LyXText::insertInset(InsetOld * inset)
1069 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1071 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1073 cursor.par()->insertInset(cursor.pos(), inset);
1074 // Just to rebreak and refresh correctly.
1075 // The character will not be inserted a second time
1076 insertChar(Paragraph::META_INSET);
1077 // If we enter a highly editable inset the cursor should be before
1078 // the inset. After an Undo LyX tries to call inset->edit(...)
1079 // and fails if the cursor is behind the inset and getInset
1080 // does not return the inset!
1081 if (isHighlyEditableInset(inset))
1087 void LyXText::cutSelection(bool doclear, bool realcut)
1089 // Stuff what we got on the clipboard. Even if there is no selection.
1091 // There is a problem with having the stuffing here in that the
1092 // larger the selection the slower LyX will get. This can be
1093 // solved by running the line below only when the selection has
1094 // finished. The solution used currently just works, to make it
1095 // faster we need to be more clever and probably also have more
1096 // calls to stuffClipboard. (Lgb)
1097 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1099 // This doesn't make sense, if there is no selection
1100 if (!selection.set())
1103 // OK, we have a selection. This is always between selection.start
1104 // and selection.end
1106 // make sure that the depth behind the selection are restored, too
1107 ParagraphList::iterator endpit = boost::next(selection.end.par());
1108 ParagraphList::iterator undoendpit = endpit;
1109 ParagraphList::iterator pars_end = ownerParagraphs().end();
1111 if (endpit != pars_end && endpit->getDepth()) {
1112 while (endpit != pars_end && endpit->getDepth()) {
1114 undoendpit = endpit;
1116 } else if (endpit != pars_end) {
1117 // because of parindents etc.
1121 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1122 boost::prior(undoendpit));
1124 endpit = selection.end.par();
1125 int endpos = selection.end.pos();
1127 boost::tie(endpit, endpos) = realcut ?
1128 CutAndPaste::cutSelection(bv()->buffer()->params,
1130 selection.start.par(), endpit,
1131 selection.start.pos(), endpos,
1132 bv()->buffer()->params.textclass,
1134 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1136 selection.start.par(), endpit,
1137 selection.start.pos(), endpos,
1139 // sometimes necessary
1141 selection.start.par()->stripLeadingSpaces();
1143 redoParagraphs(selection.start.par(), boost::next(endpit));
1144 // cutSelection can invalidate the cursor so we need to set
1146 // we prefer the end for when tracking changes
1150 // need a valid cursor. (Lgb)
1153 setCursor(cursor.par(), cursor.pos());
1154 selection.cursor = cursor;
1159 void LyXText::copySelection()
1161 // stuff the selection onto the X clipboard, from an explicit copy request
1162 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1164 // this doesnt make sense, if there is no selection
1165 if (!selection.set())
1168 // ok we have a selection. This is always between selection.start
1169 // and sel_end cursor
1171 // copy behind a space if there is one
1172 while (selection.start.par()->size() > selection.start.pos()
1173 && selection.start.par()->isLineSeparator(selection.start.pos())
1174 && (selection.start.par() != selection.end.par()
1175 || selection.start.pos() < selection.end.pos()))
1176 selection.start.pos(selection.start.pos() + 1);
1178 CutAndPaste::copySelection(selection.start.par(),
1179 selection.end.par(),
1180 selection.start.pos(), selection.end.pos(),
1181 bv()->buffer()->params.textclass);
1185 void LyXText::pasteSelection(size_t sel_index)
1187 // this does not make sense, if there is nothing to paste
1188 if (!CutAndPaste::checkPastePossible())
1191 recordUndo(bv(), Undo::INSERT, cursor.par());
1193 ParagraphList::iterator endpit;
1198 boost::tie(ppp, endpit) =
1199 CutAndPaste::pasteSelection(*bv()->buffer(),
1201 cursor.par(), cursor.pos(),
1202 bv()->buffer()->params.textclass,
1204 bufferErrors(*bv()->buffer(), el);
1205 bv()->showErrorList(_("Paste"));
1207 redoParagraphs(cursor.par(), endpit);
1209 setCursor(cursor.par(), cursor.pos());
1212 selection.cursor = cursor;
1213 setCursor(ppp.first, ppp.second);
1219 void LyXText::setSelectionRange(lyx::pos_type length)
1224 selection.cursor = cursor;
1231 // simple replacing. The font of the first selected character is used
1232 void LyXText::replaceSelectionWithString(string const & str)
1234 recordUndo(bv(), Undo::ATOMIC);
1237 if (!selection.set()) { // create a dummy selection
1238 selection.end = cursor;
1239 selection.start = cursor;
1242 // Get font setting before we cut
1243 pos_type pos = selection.end.pos();
1244 LyXFont const font = selection.start.par()
1245 ->getFontSettings(bv()->buffer()->params,
1246 selection.start.pos());
1248 // Insert the new string
1249 string::const_iterator cit = str.begin();
1250 string::const_iterator end = str.end();
1251 for (; cit != end; ++cit) {
1252 selection.end.par()->insertChar(pos, (*cit), font);
1256 // Cut the selection
1257 cutSelection(true, false);
1263 // needed to insert the selection
1264 void LyXText::insertStringAsLines(string const & str)
1266 ParagraphList::iterator pit = cursor.par();
1267 pos_type pos = cursor.pos();
1268 ParagraphList::iterator endpit = boost::next(cursor.par());
1270 recordUndo(bv(), Undo::ATOMIC);
1272 // only to be sure, should not be neccessary
1275 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1277 redoParagraphs(cursor.par(), endpit);
1278 setCursor(cursor.par(), cursor.pos());
1279 selection.cursor = cursor;
1280 setCursor(pit, pos);
1285 // turns double-CR to single CR, others where converted into one
1286 // blank. Then InsertStringAsLines is called
1287 void LyXText::insertStringAsParagraphs(string const & str)
1289 string linestr(str);
1290 bool newline_inserted = false;
1291 string::size_type const siz = linestr.length();
1293 for (string::size_type i = 0; i < siz; ++i) {
1294 if (linestr[i] == '\n') {
1295 if (newline_inserted) {
1296 // we know that \r will be ignored by
1297 // InsertStringA. Of course, it is a dirty
1298 // trick, but it works...
1299 linestr[i - 1] = '\r';
1303 newline_inserted = true;
1305 } else if (IsPrintable(linestr[i])) {
1306 newline_inserted = false;
1309 insertStringAsLines(linestr);
1313 bool LyXText::setCursor(ParagraphList::iterator pit,
1315 bool setfont, bool boundary)
1317 LyXCursor old_cursor = cursor;
1318 setCursorIntern(pit, pos, setfont, boundary);
1319 return deleteEmptyParagraphMechanism(old_cursor);
1323 void LyXText::redoCursor()
1325 #warning maybe the same for selections?
1326 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1330 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1331 pos_type pos, bool boundary)
1333 Assert(pit != ownerParagraphs().end());
1337 cur.boundary(boundary);
1341 // get the cursor y position in text
1343 RowList::iterator row = getRow(pit, pos);
1346 // y is now the beginning of the cursor row
1347 y += row->baseline();
1348 // y is now the cursor baseline
1351 pos_type last = lastPos(*pit, row);
1353 // None of these should happen, but we're scaredy-cats
1354 if (pos > pit->size()) {
1355 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1358 } else if (pos > last + 1) {
1359 lyxerr << "dont like 2 please report" << endl;
1360 // This shouldn't happen.
1363 } else if (pos < row->pos()) {
1364 lyxerr << "dont like 3 please report" << endl;
1369 // now get the cursors x position
1370 float x = getCursorX(pit, row, pos, last, boundary);
1376 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1377 pos_type pos, pos_type last, bool boundary) const
1379 pos_type cursor_vpos = 0;
1380 double x = rit->x();
1381 double fill_separator = rit->fill_separator();
1382 double fill_hfill = rit->fill_hfill();
1383 double fill_label_hfill = rit->fill_label_hfill();
1384 pos_type const rit_pos = rit->pos();
1387 cursor_vpos = rit_pos;
1388 else if (pos > last && !boundary)
1389 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1390 ? rit_pos : last + 1;
1391 else if (pos > rit_pos && (pos > last || boundary))
1392 // Place cursor after char at (logical) position pos - 1
1393 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1394 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1396 // Place cursor before char at (logical) position pos
1397 cursor_vpos = (bidi_level(pos) % 2 == 0)
1398 ? log2vis(pos) : log2vis(pos) + 1;
1400 pos_type body_pos = pit->beginningOfBody();
1402 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1405 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1406 pos_type pos = vis2log(vpos);
1407 if (body_pos > 0 && pos == body_pos - 1) {
1408 x += fill_label_hfill +
1409 font_metrics::width(
1410 pit->layout()->labelsep, getLabelFont(pit));
1411 if (pit->isLineSeparator(body_pos - 1))
1412 x -= singleWidth(pit, body_pos - 1);
1415 if (hfillExpansion(*pit, rit, pos)) {
1416 x += singleWidth(pit, pos);
1417 if (pos >= body_pos)
1420 x += fill_label_hfill;
1421 } else if (pit->isSeparator(pos)) {
1422 x += singleWidth(pit, pos);
1423 if (pos >= body_pos)
1424 x += fill_separator;
1426 x += singleWidth(pit, pos);
1432 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1433 pos_type pos, bool setfont, bool boundary)
1435 setCursor(cursor, pit, pos, boundary);
1441 void LyXText::setCurrentFont()
1443 pos_type pos = cursor.pos();
1444 ParagraphList::iterator pit = cursor.par();
1446 if (cursor.boundary() && pos > 0)
1450 if (pos == pit->size())
1452 else // potentional bug... BUG (Lgb)
1453 if (pit->isSeparator(pos)) {
1454 if (pos > cursorRow()->pos() &&
1455 bidi_level(pos) % 2 ==
1456 bidi_level(pos - 1) % 2)
1458 else if (pos + 1 < pit->size())
1463 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1464 real_current_font = getFont(pit, pos);
1466 if (cursor.pos() == pit->size() &&
1467 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1468 !cursor.boundary()) {
1469 Language const * lang =
1470 pit->getParLanguage(bv()->buffer()->params);
1471 current_font.setLanguage(lang);
1472 current_font.setNumber(LyXFont::OFF);
1473 real_current_font.setLanguage(lang);
1474 real_current_font.setNumber(LyXFont::OFF);
1479 // returns the column near the specified x-coordinate of the row
1480 // x is set to the real beginning of this column
1481 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1482 RowList::iterator rit, int & x, bool & boundary) const
1484 double tmpx = rit->x();
1485 double fill_separator = rit->fill_separator();
1486 double fill_hfill = rit->fill_hfill();
1487 double fill_label_hfill = rit->fill_label_hfill();
1489 pos_type vc = rit->pos();
1490 pos_type last = lastPos(*pit, rit);
1492 LyXLayout_ptr const & layout = pit->layout();
1494 bool left_side = false;
1496 pos_type body_pos = pit->beginningOfBody();
1497 double last_tmpx = tmpx;
1500 (body_pos - 1 > last ||
1501 !pit->isLineSeparator(body_pos - 1)))
1504 // check for empty row
1510 while (vc <= last && tmpx <= x) {
1513 if (body_pos > 0 && c == body_pos - 1) {
1514 tmpx += fill_label_hfill +
1515 font_metrics::width(layout->labelsep, getLabelFont(pit));
1516 if (pit->isLineSeparator(body_pos - 1))
1517 tmpx -= singleWidth(pit, body_pos - 1);
1520 if (hfillExpansion(*pit, rit, c)) {
1521 tmpx += singleWidth(pit, c);
1525 tmpx += fill_label_hfill;
1526 } else if (pit->isSeparator(c)) {
1527 tmpx += singleWidth(pit, c);
1529 tmpx += fill_separator;
1531 tmpx += singleWidth(pit, c);
1536 if ((tmpx + last_tmpx) / 2 > x) {
1541 if (vc > last + 1) // This shouldn't happen.
1545 // This (rtl_support test) is not needed, but gives
1546 // some speedup if rtl_support == false
1547 bool const lastrow = lyxrc.rtl_support
1548 && boost::next(rit) == pit->rows.end();
1550 // If lastrow is false, we don't need to compute
1551 // the value of rtl.
1552 bool const rtl = (lastrow)
1553 ? pit->isRightToLeftPar(bv()->buffer()->params)
1556 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1557 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1559 else if (vc == rit->pos()) {
1561 if (bidi_level(c) % 2 == 1)
1564 c = vis2log(vc - 1);
1565 bool const rtl = (bidi_level(c) % 2 == 1);
1566 if (left_side == rtl) {
1568 boundary = isBoundary(*bv()->buffer(), *pit, c);
1572 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1573 if (bidi_level(last) % 2 == 0)
1574 tmpx -= singleWidth(pit, last);
1576 tmpx += singleWidth(pit, last);
1586 void LyXText::setCursorFromCoordinates(int x, int y)
1588 LyXCursor old_cursor = cursor;
1589 setCursorFromCoordinates(cursor, x, y);
1591 deleteEmptyParagraphMechanism(old_cursor);
1595 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1597 // Get the row first.
1598 ParagraphList::iterator pit;
1599 RowList::iterator rit = getRowNearY(y, pit);
1603 pos_type const column = getColumnNearX(pit, rit, x, bound);
1605 cur.pos(rit->pos() + column);
1607 cur.y(y + rit->baseline());
1609 cur.boundary(bound);
1613 void LyXText::cursorLeft(bool internal)
1615 if (cursor.pos() > 0) {
1616 bool boundary = cursor.boundary();
1617 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1618 if (!internal && !boundary &&
1619 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1620 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1621 } else if (cursor.par() != ownerParagraphs().begin()) {
1622 // steps into the paragraph above
1623 ParagraphList::iterator pit = boost::prior(cursor.par());
1624 setCursor(pit, pit->size());
1629 void LyXText::cursorRight(bool internal)
1631 bool const at_end = (cursor.pos() == cursor.par()->size());
1632 bool const at_newline = !at_end &&
1633 cursor.par()->isNewline(cursor.pos());
1635 if (!internal && cursor.boundary() && !at_newline)
1636 setCursor(cursor.par(), cursor.pos(), true, false);
1638 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1640 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1641 setCursor(cursor.par(), cursor.pos(), true, true);
1642 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1643 setCursor(boost::next(cursor.par()), 0);
1647 void LyXText::cursorUp(bool selecting)
1650 int x = cursor.x_fix();
1651 int y = cursor.y() - cursorRow()->baseline() - 1;
1652 setCursorFromCoordinates(x, y);
1654 int topy = bv_owner->top_y();
1655 int y1 = cursor.y() - topy;
1658 InsetOld * inset_hit = checkInsetHit(x, y1);
1659 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1660 inset_hit->localDispatch(
1661 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1665 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1666 cursorRow()->baseline() << endl;
1667 setCursorFromCoordinates(cursor.x_fix(),
1668 cursor.y() - cursorRow()->baseline() - 1);
1673 void LyXText::cursorDown(bool selecting)
1676 int x = cursor.x_fix();
1677 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1678 setCursorFromCoordinates(x, y);
1679 if (!selecting && cursorRow() == cursorIRow()) {
1680 int topy = bv_owner->top_y();
1681 int y1 = cursor.y() - topy;
1684 InsetOld * inset_hit = checkInsetHit(x, y1);
1685 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1686 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1687 inset_hit->localDispatch(cmd);
1691 setCursorFromCoordinates(cursor.x_fix(),
1692 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1697 void LyXText::cursorUpParagraph()
1699 if (cursor.pos() > 0)
1700 setCursor(cursor.par(), 0);
1701 else if (cursor.par() != ownerParagraphs().begin())
1702 setCursor(boost::prior(cursor.par()), 0);
1706 void LyXText::cursorDownParagraph()
1708 ParagraphList::iterator par = cursor.par();
1709 ParagraphList::iterator next_par = boost::next(par);
1711 if (next_par != ownerParagraphs().end())
1712 setCursor(next_par, 0);
1714 setCursor(par, par->size());
1718 // fix the cursor `cur' after a characters has been deleted at `where'
1719 // position. Called by deleteEmptyParagraphMechanism
1720 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1722 // if cursor is not in the paragraph where the delete occured,
1724 if (cur.par() != where.par())
1727 // if cursor position is after the place where the delete occured,
1729 if (cur.pos() > where.pos())
1730 cur.pos(cur.pos()-1);
1732 // check also if we don't want to set the cursor on a spot behind the
1733 // pagragraph because we erased the last character.
1734 if (cur.pos() > cur.par()->size())
1735 cur.pos(cur.par()->size());
1737 // recompute row et al. for this cursor
1738 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1742 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1744 // Would be wrong to delete anything if we have a selection.
1745 if (selection.set())
1748 // We allow all kinds of "mumbo-jumbo" when freespacing.
1749 if (old_cursor.par()->isFreeSpacing())
1752 /* Ok I'll put some comments here about what is missing.
1753 I have fixed BackSpace (and thus Delete) to not delete
1754 double-spaces automagically. I have also changed Cut,
1755 Copy and Paste to hopefully do some sensible things.
1756 There are still some small problems that can lead to
1757 double spaces stored in the document file or space at
1758 the beginning of paragraphs. This happens if you have
1759 the cursor between to spaces and then save. Or if you
1760 cut and paste and the selection have a space at the
1761 beginning and then save right after the paste. I am
1762 sure none of these are very hard to fix, but I will
1763 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1764 that I can get some feedback. (Lgb)
1767 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1768 // delete the LineSeparator.
1771 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1772 // delete the LineSeparator.
1775 // If the pos around the old_cursor were spaces, delete one of them.
1776 if (old_cursor.par() != cursor.par()
1777 || old_cursor.pos() != cursor.pos()) {
1779 // Only if the cursor has really moved
1780 if (old_cursor.pos() > 0
1781 && old_cursor.pos() < old_cursor.par()->size()
1782 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1783 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1784 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1785 redoParagraph(old_cursor.par());
1789 #ifdef WITH_WARNINGS
1790 #warning This will not work anymore when we have multiple views of the same buffer
1791 // In this case, we will have to correct also the cursors held by
1792 // other bufferviews. It will probably be easier to do that in a more
1793 // automated way in LyXCursor code. (JMarc 26/09/2001)
1795 // correct all cursors held by the LyXText
1796 fixCursorAfterDelete(cursor, old_cursor);
1797 fixCursorAfterDelete(selection.cursor, old_cursor);
1798 fixCursorAfterDelete(selection.start, old_cursor);
1799 fixCursorAfterDelete(selection.end, old_cursor);
1804 // don't delete anything if this is the ONLY paragraph!
1805 if (ownerParagraphs().size() == 1)
1808 // Do not delete empty paragraphs with keepempty set.
1809 if (old_cursor.par()->allowEmpty())
1812 // only do our magic if we changed paragraph
1813 if (old_cursor.par() == cursor.par())
1816 // record if we have deleted a paragraph
1817 // we can't possibly have deleted a paragraph before this point
1818 bool deleted = false;
1820 if (old_cursor.par()->empty() ||
1821 (old_cursor.par()->size() == 1 &&
1822 old_cursor.par()->isLineSeparator(0))) {
1823 // ok, we will delete something
1824 LyXCursor tmpcursor;
1828 bool selection_position_was_oldcursor_position = (
1829 selection.cursor.par() == old_cursor.par()
1830 && selection.cursor.pos() == old_cursor.pos());
1833 cursor = old_cursor; // that undo can restore the right cursor position
1835 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1836 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1839 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1843 ownerParagraphs().erase(old_cursor.par());
1847 setCursorIntern(cursor.par(), cursor.pos());
1849 if (selection_position_was_oldcursor_position) {
1850 // correct selection
1851 selection.cursor = cursor;
1855 if (old_cursor.par()->stripLeadingSpaces()) {
1856 redoParagraph(old_cursor.par());
1858 setCursorIntern(cursor.par(), cursor.pos());
1859 selection.cursor = cursor;
1866 ParagraphList & LyXText::ownerParagraphs() const
1872 bool LyXText::isInInset() const
1874 // Sub-level has non-null bv owner and non-null inset owner.
1875 return inset_owner != 0;
1879 int defaultRowHeight()
1881 LyXFont const font(LyXFont::ALL_SANE);
1882 return int(font_metrics::maxAscent(font)
1883 + font_metrics::maxDescent(font) * 1.5);