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 "paragraph.h"
26 #include "funcrequest.h"
27 #include "frontends/LyXView.h"
28 #include "undo_funcs.h"
30 #include "buffer_funcs.h"
31 #include "bufferparams.h"
32 #include "errorlist.h"
34 #include "BufferView.h"
35 #include "CutAndPaste.h"
36 #include "frontends/Painter.h"
37 #include "frontends/font_metrics.h"
41 #include "FloatList.h"
43 #include "ParagraphParameters.h"
45 #include "lyxrow_funcs.h"
46 #include "metricsinfo.h"
47 #include "paragraph_funcs.h"
49 #include "insets/insetbibitem.h"
50 #include "insets/insetenv.h"
51 #include "insets/insetfloat.h"
52 #include "insets/insetwrap.h"
54 #include "support/LAssert.h"
55 #include "support/textutils.h"
56 #include "support/lstrings.h"
58 #include <boost/tuple/tuple.hpp>
62 using namespace lyx::support;
72 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
73 ParagraphList & paragraphs)
74 : height(0), width(0), anchor_y_(0),
75 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
76 in_inset_(ininset), paragraphs_(paragraphs)
81 void LyXText::init(BufferView * bview)
85 ParagraphList::iterator const beg = ownerParagraphs().begin();
86 ParagraphList::iterator const end = ownerParagraphs().end();
87 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
95 current_font = getFont(beg, 0);
97 redoParagraphs(beg, end);
98 setCursorIntern(beg, 0);
99 selection.cursor = cursor;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as Paragraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
114 LyXLayout_ptr const & layout = pit->layout();
116 BufferParams const & params = bv()->buffer()->params;
118 // We specialize the 95% common case:
119 if (!pit->getDepth()) {
120 if (layout->labeltype == LABEL_MANUAL
121 && pos < pit->beginningOfBody()) {
123 LyXFont f = pit->getFontSettings(params, pos);
125 pit->inInset()->getDrawFont(f);
126 return f.realize(layout->reslabelfont);
128 LyXFont f = pit->getFontSettings(params, pos);
130 pit->inInset()->getDrawFont(f);
131 return f.realize(layout->resfont);
135 // The uncommon case need not be optimized as much
139 if (pos < pit->beginningOfBody()) {
141 layoutfont = layout->labelfont;
144 layoutfont = layout->font;
147 LyXFont tmpfont = pit->getFontSettings(params, pos);
148 tmpfont.realize(layoutfont);
151 pit->inInset()->getDrawFont(tmpfont);
153 // Realize with the fonts of lesser depth.
154 tmpfont.realize(outerFont(pit, ownerParagraphs()));
155 tmpfont.realize(defaultfont_);
161 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
163 LyXLayout_ptr const & layout = pit->layout();
165 if (!pit->getDepth())
166 return layout->resfont;
168 LyXFont font = layout->font;
169 // Realize with the fonts of lesser depth.
170 font.realize(outerFont(pit, ownerParagraphs()));
171 font.realize(defaultfont_);
177 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
179 LyXLayout_ptr const & layout = pit->layout();
181 if (!pit->getDepth())
182 return layout->reslabelfont;
184 LyXFont font = layout->labelfont;
185 // Realize with the fonts of lesser depth.
186 font.realize(outerFont(pit, ownerParagraphs()));
187 font.realize(defaultfont_);
193 void LyXText::setCharFont(ParagraphList::iterator pit,
194 pos_type pos, LyXFont const & fnt,
197 BufferParams const & params = bv()->buffer()->params;
198 LyXFont font = getFont(pit, pos);
199 font.update(fnt, params.language, toggleall);
200 // Let the insets convert their font
201 if (pit->isInset(pos)) {
202 InsetOld * inset = pit->getInset(pos);
203 if (isEditableInset(inset)) {
204 static_cast<UpdatableInset *>(inset)
205 ->setFont(bv(), fnt, toggleall, true);
209 // Plug through to version below:
210 setCharFont(pit, pos, font);
214 void LyXText::setCharFont(
215 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
218 LyXLayout_ptr const & layout = pit->layout();
220 // Get concrete layout font to reduce against
223 if (pos < pit->beginningOfBody())
224 layoutfont = layout->labelfont;
226 layoutfont = layout->font;
228 // Realize against environment font information
229 if (pit->getDepth()) {
230 ParagraphList::iterator tp = pit;
231 while (!layoutfont.resolved() &&
232 tp != ownerParagraphs().end() &&
234 tp = outerHook(tp, ownerParagraphs());
235 if (tp != ownerParagraphs().end())
236 layoutfont.realize(tp->layout()->font);
240 layoutfont.realize(defaultfont_);
242 // Now, reduce font against full layout font
243 font.reduce(layoutfont);
245 pit->setFont(pos, font);
249 InsetOld * LyXText::getInset() const
251 ParagraphList::iterator pit = cursor.par();
252 pos_type const pos = cursor.pos();
254 if (pos < pit->size() && pit->isInset(pos)) {
255 return pit->getInset(pos);
261 void LyXText::toggleInset()
263 InsetOld * inset = getInset();
264 // is there an editable inset at cursor position?
265 if (!isEditableInset(inset)) {
266 // No, try to see if we are inside a collapsable inset
267 if (inset_owner && inset_owner->owner()
268 && inset_owner->owner()->isOpen()) {
269 bv()->unlockInset(inset_owner->owner());
270 inset_owner->owner()->close(bv());
271 bv()->getLyXText()->cursorRight(bv());
275 //bv()->owner()->message(inset->editMessage());
277 // do we want to keep this?? (JMarc)
278 if (!isHighlyEditableInset(inset))
279 recordUndo(bv(), Undo::ATOMIC);
286 bv()->updateInset(inset);
290 /* used in setlayout */
291 // Asger is not sure we want to do this...
292 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
295 LyXLayout_ptr const & layout = par.layout();
296 pos_type const psize = par.size();
299 for (pos_type pos = 0; pos < psize; ++pos) {
300 if (pos < par.beginningOfBody())
301 layoutfont = layout->labelfont;
303 layoutfont = layout->font;
305 LyXFont tmpfont = par.getFontSettings(params, pos);
306 tmpfont.reduce(layoutfont);
307 par.setFont(pos, tmpfont);
312 ParagraphList::iterator
313 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
314 LyXCursor & send_cur,
315 string const & layout)
317 ParagraphList::iterator endpit = boost::next(send_cur.par());
318 ParagraphList::iterator undoendpit = endpit;
319 ParagraphList::iterator pars_end = ownerParagraphs().end();
321 if (endpit != pars_end && endpit->getDepth()) {
322 while (endpit != pars_end && endpit->getDepth()) {
326 } else if (endpit != pars_end) {
327 // because of parindents etc.
331 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
333 // ok we have a selection. This is always between sstart_cur
334 // and sel_end cursor
336 ParagraphList::iterator pit = sstart_cur.par();
337 ParagraphList::iterator epit = boost::next(send_cur.par());
339 LyXLayout_ptr const & lyxlayout =
340 bv()->buffer()->params.getLyXTextClass()[layout];
343 pit->applyLayout(lyxlayout);
344 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
345 pit->params().spaceTop(lyxlayout->fill_top ?
346 VSpace(VSpace::VFILL)
347 : VSpace(VSpace::NONE));
348 pit->params().spaceBottom(lyxlayout->fill_bottom ?
349 VSpace(VSpace::VFILL)
350 : VSpace(VSpace::NONE));
351 if (lyxlayout->margintype == MARGIN_MANUAL)
352 pit->setLabelWidthString(lyxlayout->labelstring());
355 } while (pit != epit);
361 // set layout over selection and make a total rebreak of those paragraphs
362 void LyXText::setLayout(string const & layout)
364 LyXCursor tmpcursor = cursor; // store the current cursor
366 // if there is no selection just set the layout
367 // of the current paragraph
368 if (!selection.set()) {
369 selection.start = cursor; // dummy selection
370 selection.end = cursor;
373 // special handling of new environment insets
374 BufferParams const & params = bv()->buffer()->params;
375 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
376 if (lyxlayout->is_environment) {
377 // move everything in a new environment inset
378 lyxerr << "setting layout " << layout << endl;
379 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
380 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
381 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
382 InsetOld * inset = new InsetEnvironment(params, layout);
383 if (bv()->insertInset(inset)) {
385 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
392 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
393 selection.end, layout);
394 redoParagraphs(selection.start.par(), endpit);
396 // we have to reset the selection, because the
397 // geometry could have changed
398 setCursor(selection.start.par(), selection.start.pos(), false);
399 selection.cursor = cursor;
400 setCursor(selection.end.par(), selection.end.pos(), false);
404 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
408 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
410 ParagraphList::iterator pit = cursor.par();
411 ParagraphList::iterator end = cursor.par();
412 ParagraphList::iterator start = pit;
414 if (selection.set()) {
415 pit = selection.start.par();
416 end = selection.end.par();
420 ParagraphList::iterator pastend = boost::next(end);
423 recordUndo(bv(), Undo::ATOMIC, start, end);
425 bool changed = false;
427 int prev_after_depth = 0;
428 #warning parlist ... could be nicer ?
429 if (start != ownerParagraphs().begin()) {
430 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
434 int const depth = pit->params().depth();
435 if (type == bv_funcs::INC_DEPTH) {
436 if (depth < prev_after_depth
437 && pit->layout()->labeltype != LABEL_BIBLIO) {
440 pit->params().depth(depth + 1);
445 pit->params().depth(depth - 1);
448 prev_after_depth = pit->getMaxDepthAfter();
460 redoParagraphs(start, pastend);
462 // We need to actually move the text->cursor. I don't
463 // understand why ...
464 LyXCursor tmpcursor = cursor;
466 // we have to reset the visual selection because the
467 // geometry could have changed
468 if (selection.set()) {
469 setCursor(selection.start.par(), selection.start.pos());
470 selection.cursor = cursor;
471 setCursor(selection.end.par(), selection.end.pos());
474 // this handles the counter labels, and also fixes up
475 // depth values for follow-on (child) paragraphs
479 setCursor(tmpcursor.par(), tmpcursor.pos());
485 // set font over selection and make a total rebreak of those paragraphs
486 void LyXText::setFont(LyXFont const & font, bool toggleall)
488 // if there is no selection just set the current_font
489 if (!selection.set()) {
490 // Determine basis font
492 if (cursor.pos() < cursor.par()->beginningOfBody()) {
493 layoutfont = getLabelFont(cursor.par());
495 layoutfont = getLayoutFont(cursor.par());
497 // Update current font
498 real_current_font.update(font,
499 bv()->buffer()->params.language,
502 // Reduce to implicit settings
503 current_font = real_current_font;
504 current_font.reduce(layoutfont);
505 // And resolve it completely
506 real_current_font.realize(layoutfont);
511 LyXCursor tmpcursor = cursor; // store the current cursor
513 // ok we have a selection. This is always between sel_start_cursor
514 // and sel_end cursor
516 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
518 cursor = selection.start;
519 while (cursor.par() != selection.end.par() ||
520 cursor.pos() < selection.end.pos())
522 if (cursor.pos() < cursor.par()->size()) {
523 // an open footnote should behave like a closed one
524 setCharFont(cursor.par(), cursor.pos(),
526 cursor.pos(cursor.pos() + 1);
529 cursor.par(boost::next(cursor.par()));
534 redoParagraph(selection.start.par());
536 // we have to reset the selection, because the
537 // geometry could have changed, but we keep
538 // it for user convenience
539 setCursor(selection.start.par(), selection.start.pos());
540 selection.cursor = cursor;
541 setCursor(selection.end.par(), selection.end.pos());
543 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
544 tmpcursor.boundary());
548 int LyXText::redoParagraphInternal(ParagraphList::iterator pit)
550 RowList::iterator rit = pit->rows.begin();
551 RowList::iterator end = pit->rows.end();
553 // remove rows of paragraph, keep track of height changes
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
559 InsetList::iterator ii = pit->insetlist.begin();
560 InsetList::iterator iend = pit->insetlist.end();
561 for (; ii != iend; ++ii) {
563 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
564 ii->inset->metrics(mi, dim);
567 // rebreak the paragraph
568 for (pos_type z = 0; z < pit->size() + 1; ) {
570 z = rowBreakPoint(pit, row) + 1;
572 pit->rows.push_back(row);
576 // set height and fill and width of rows
577 int const ww = workWidth();
578 for (rit = pit->rows.begin(); rit != end; ++rit) {
579 int const f = fill(pit, rit, ww);
580 int const w = ww - f;
581 par_width = std::max(par_width, w);
584 prepareToPrint(pit, rit);
585 setHeightOfRow(pit, rit);
586 height += rit->height();
589 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
594 int LyXText::redoParagraphs(ParagraphList::iterator start,
595 ParagraphList::iterator end)
598 for ( ; start != end; ++start) {
599 int par_width = redoParagraphInternal(start);
600 pars_width = std::max(par_width, pars_width);
602 updateRowPositions();
607 void LyXText::redoParagraph(ParagraphList::iterator pit)
609 redoParagraphInternal(pit);
610 updateRowPositions();
614 void LyXText::fullRebreak()
616 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
618 selection.cursor = cursor;
622 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
624 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
625 // << " workWidth: " << workWidth() << endl;
626 //Assert(mi.base.textwidth);
633 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
636 dim.asc = firstRow()->ascent_of_text();
637 dim.des = height - dim.asc;
638 dim.wid = std::max(mi.base.textwidth, int(width));
642 // important for the screen
645 // the cursor set functions have a special mechanism. When they
646 // realize, that you left an empty paragraph, they will delete it.
647 // They also delete the corresponding row
649 // need the selection cursor:
650 void LyXText::setSelection()
652 TextCursor::setSelection();
657 void LyXText::clearSelection()
659 TextCursor::clearSelection();
661 // reset this in the bv_owner!
662 if (bv_owner && bv_owner->text)
663 bv_owner->text->xsel_cache.set(false);
667 void LyXText::cursorHome()
669 setCursor(cursor.par(), cursorRow()->pos());
673 void LyXText::cursorEnd()
675 if (cursor.par()->empty())
678 RowList::iterator rit = cursorRow();
679 ParagraphList::iterator pit = cursor.par();
680 pos_type pos = lastPos(*pit, rit);
681 /* cursor should be before a hard newline only */
682 if (!pit->isNewline(pos))
688 void LyXText::cursorTop()
690 setCursor(ownerParagraphs().begin(), 0);
694 void LyXText::cursorBottom()
696 ParagraphList::iterator lastpit =
697 boost::prior(ownerParagraphs().end());
698 setCursor(lastpit, lastpit->size());
702 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
704 // If the mask is completely neutral, tell user
705 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
706 // Could only happen with user style
707 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
711 // Try implicit word selection
712 // If there is a change in the language the implicit word selection
714 LyXCursor resetCursor = cursor;
715 bool implicitSelection = (font.language() == ignore_language
716 && font.number() == LyXFont::IGNORE)
717 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
720 setFont(font, toggleall);
722 // Implicit selections are cleared afterwards
723 //and cursor is set to the original position.
724 if (implicitSelection) {
726 cursor = resetCursor;
727 setCursor(cursor.par(), cursor.pos());
728 selection.cursor = cursor;
733 string LyXText::getStringToIndex()
735 // Try implicit word selection
736 // If there is a change in the language the implicit word selection
738 LyXCursor const reset_cursor = cursor;
739 bool const implicitSelection =
740 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
743 if (!selection.set())
744 bv()->owner()->message(_("Nothing to index!"));
745 else if (selection.start.par() != selection.end.par())
746 bv()->owner()->message(_("Cannot index more than one paragraph!"));
748 idxstring = selectionAsString(*bv()->buffer(), false);
750 // Reset cursors to their original position.
751 cursor = reset_cursor;
752 setCursor(cursor.par(), cursor.pos());
753 selection.cursor = cursor;
755 // Clear the implicit selection.
756 if (implicitSelection)
763 // the DTP switches for paragraphs. LyX will store them in the first
764 // physical paragraph. When a paragraph is broken, the top settings rest,
765 // the bottom settings are given to the new one. So I can make sure,
766 // they do not duplicate themself and you cannnot make dirty things with
769 void LyXText::setParagraph(bool line_top, bool line_bottom,
770 bool pagebreak_top, bool pagebreak_bottom,
771 VSpace const & space_top,
772 VSpace const & space_bottom,
773 Spacing const & spacing,
775 string const & labelwidthstring,
778 LyXCursor tmpcursor = cursor;
779 if (!selection.set()) {
780 selection.start = cursor;
781 selection.end = cursor;
784 // make sure that the depth behind the selection are restored, too
785 ParagraphList::iterator endpit = boost::next(selection.end.par());
786 ParagraphList::iterator undoendpit = endpit;
787 ParagraphList::iterator pars_end = ownerParagraphs().end();
789 if (endpit != pars_end && endpit->getDepth()) {
790 while (endpit != pars_end && endpit->getDepth()) {
794 } else if (endpit != pars_end) {
795 // because of parindents etc.
799 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
800 boost::prior(undoendpit));
803 ParagraphList::iterator tmppit = selection.end.par();
805 while (tmppit != boost::prior(selection.start.par())) {
806 setCursor(tmppit, 0);
808 ParagraphList::iterator pit = cursor.par();
809 ParagraphParameters & params = pit->params();
811 params.lineTop(line_top);
812 params.lineBottom(line_bottom);
813 params.pagebreakTop(pagebreak_top);
814 params.pagebreakBottom(pagebreak_bottom);
815 params.spaceTop(space_top);
816 params.spaceBottom(space_bottom);
817 params.spacing(spacing);
818 // does the layout allow the new alignment?
819 LyXLayout_ptr const & layout = pit->layout();
821 if (align == LYX_ALIGN_LAYOUT)
822 align = layout->align;
823 if (align & layout->alignpossible) {
824 if (align == layout->align)
825 params.align(LYX_ALIGN_LAYOUT);
829 pit->setLabelWidthString(labelwidthstring);
830 params.noindent(noindent);
831 tmppit = boost::prior(pit);
834 redoParagraphs(selection.start.par(), endpit);
837 setCursor(selection.start.par(), selection.start.pos());
838 selection.cursor = cursor;
839 setCursor(selection.end.par(), selection.end.pos());
841 setCursor(tmpcursor.par(), tmpcursor.pos());
843 bv()->updateInset(inset_owner);
847 // set the counter of a paragraph. This includes the labels
848 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
850 LyXTextClass const & textclass = buf.params.getLyXTextClass();
851 LyXLayout_ptr const & layout = pit->layout();
853 if (pit != ownerParagraphs().begin()) {
855 pit->params().appendix(boost::prior(pit)->params().appendix());
856 if (!pit->params().appendix() &&
857 pit->params().startOfAppendix()) {
858 pit->params().appendix(true);
859 textclass.counters().reset();
861 pit->enumdepth = boost::prior(pit)->enumdepth;
862 pit->itemdepth = boost::prior(pit)->itemdepth;
864 pit->params().appendix(pit->params().startOfAppendix());
869 // Maybe we have to increment the enumeration depth.
870 // BUT, enumeration in a footnote is considered in isolation from its
871 // surrounding paragraph so don't increment if this is the
872 // first line of the footnote
873 // AND, bibliographies can't have their depth changed ie. they
874 // are always of depth 0
875 if (pit != ownerParagraphs().begin()
876 && boost::prior(pit)->getDepth() < pit->getDepth()
877 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
878 && pit->enumdepth < 3
879 && layout->labeltype != LABEL_BIBLIO) {
883 // Maybe we have to decrement the enumeration depth, see note above
884 if (pit != ownerParagraphs().begin()
885 && boost::prior(pit)->getDepth() > pit->getDepth()
886 && layout->labeltype != LABEL_BIBLIO) {
887 pit->enumdepth = depthHook(pit, ownerParagraphs(),
888 pit->getDepth())->enumdepth;
891 if (!pit->params().labelString().empty()) {
892 pit->params().labelString(string());
895 if (layout->margintype == MARGIN_MANUAL) {
896 if (pit->params().labelWidthString().empty())
897 pit->setLabelWidthString(layout->labelstring());
899 pit->setLabelWidthString(string());
902 // is it a layout that has an automatic label?
903 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
904 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
908 if (i >= 0 && i <= buf.params.secnumdepth) {
912 textclass.counters().step(layout->latexname());
914 // Is there a label? Useful for Chapter layout
915 if (!pit->params().appendix()) {
916 s << buf.B_(layout->labelstring());
918 s << buf.B_(layout->labelstring_appendix());
921 // Use of an integer is here less than elegant. For now.
922 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
923 if (!pit->params().appendix()) {
924 numbertype = "sectioning";
926 numbertype = "appendix";
927 if (pit->isRightToLeftPar(buf.params))
934 << textclass.counters()
935 .numberLabel(layout->latexname(),
936 numbertype, langtype, head);
938 pit->params().labelString(STRCONV(s.str()));
940 // reset enum counters
941 textclass.counters().reset("enum");
942 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
943 textclass.counters().reset("enum");
944 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
946 // Yes I know this is a really, really! bad solution
948 string enumcounter("enum");
950 switch (pit->enumdepth) {
962 // not a valid enumdepth...
966 textclass.counters().step(enumcounter);
968 s << textclass.counters()
969 .numberLabel(enumcounter, "enumeration");
970 pit->params().labelString(STRCONV(s.str()));
972 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
973 textclass.counters().step("bibitem");
974 int number = textclass.counters().value("bibitem");
975 if (pit->bibitem()) {
976 pit->bibitem()->setCounter(number);
977 pit->params().labelString(layout->labelstring());
979 // In biblio should't be following counters but...
981 string s = buf.B_(layout->labelstring());
984 if (layout->labeltype == LABEL_SENSITIVE) {
985 ParagraphList::iterator end = ownerParagraphs().end();
986 ParagraphList::iterator tmppit = pit;
989 while (tmppit != end && tmppit->inInset()
990 // the single '=' is intended below
991 && (in = tmppit->inInset()->owner()))
993 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
994 in->lyxCode() == InsetOld::WRAP_CODE) {
998 Paragraph const * owner = &ownerPar(buf, in);
999 tmppit = ownerParagraphs().begin();
1000 for ( ; tmppit != end; ++tmppit)
1001 if (&*tmppit == owner)
1009 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1010 type = static_cast<InsetFloat*>(in)->params().type;
1011 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1012 type = static_cast<InsetWrap*>(in)->params().type;
1016 Floating const & fl = textclass.floats().getType(type);
1018 textclass.counters().step(fl.type());
1020 // Doesn't work... yet.
1021 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1023 // par->SetLayout(0);
1024 // s = layout->labelstring;
1025 s = _("Senseless: ");
1028 pit->params().labelString(s);
1030 // reset the enumeration counter. They are always reset
1031 // when there is any other layout between
1032 // Just fall-through between the cases so that all
1033 // enum counters deeper than enumdepth is also reset.
1034 switch (pit->enumdepth) {
1036 textclass.counters().reset("enumi");
1038 textclass.counters().reset("enumii");
1040 textclass.counters().reset("enumiii");
1042 textclass.counters().reset("enumiv");
1048 // Updates all counters. Paragraphs with changed label string will be rebroken
1049 void LyXText::updateCounters()
1052 bv()->buffer()->params.getLyXTextClass().counters().reset();
1054 ParagraphList::iterator beg = ownerParagraphs().begin();
1055 ParagraphList::iterator end = ownerParagraphs().end();
1056 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1057 string const oldLabel = pit->params().labelString();
1059 size_t maxdepth = 0;
1061 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1063 if (pit->params().depth() > maxdepth)
1064 pit->params().depth(maxdepth);
1066 // setCounter can potentially change the labelString.
1067 setCounter(*bv()->buffer(), pit);
1069 string const & newLabel = pit->params().labelString();
1071 if (oldLabel != newLabel)
1077 void LyXText::insertInset(InsetOld * inset)
1079 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1081 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1083 cursor.par()->insertInset(cursor.pos(), inset);
1084 // Just to rebreak and refresh correctly.
1085 // The character will not be inserted a second time
1086 insertChar(Paragraph::META_INSET);
1087 // If we enter a highly editable inset the cursor should be before
1088 // the inset. After an Undo LyX tries to call inset->edit(...)
1089 // and fails if the cursor is behind the inset and getInset
1090 // does not return the inset!
1091 if (isHighlyEditableInset(inset))
1097 void LyXText::cutSelection(bool doclear, bool realcut)
1099 // Stuff what we got on the clipboard. Even if there is no selection.
1101 // There is a problem with having the stuffing here in that the
1102 // larger the selection the slower LyX will get. This can be
1103 // solved by running the line below only when the selection has
1104 // finished. The solution used currently just works, to make it
1105 // faster we need to be more clever and probably also have more
1106 // calls to stuffClipboard. (Lgb)
1107 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1109 // This doesn't make sense, if there is no selection
1110 if (!selection.set())
1113 // OK, we have a selection. This is always between selection.start
1114 // and selection.end
1116 // make sure that the depth behind the selection are restored, too
1117 ParagraphList::iterator endpit = boost::next(selection.end.par());
1118 ParagraphList::iterator undoendpit = endpit;
1119 ParagraphList::iterator pars_end = ownerParagraphs().end();
1121 if (endpit != pars_end && endpit->getDepth()) {
1122 while (endpit != pars_end && endpit->getDepth()) {
1124 undoendpit = endpit;
1126 } else if (endpit != pars_end) {
1127 // because of parindents etc.
1131 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1132 boost::prior(undoendpit));
1134 endpit = selection.end.par();
1135 int endpos = selection.end.pos();
1137 boost::tie(endpit, endpos) = realcut ?
1138 CutAndPaste::cutSelection(bv()->buffer()->params,
1140 selection.start.par(), endpit,
1141 selection.start.pos(), endpos,
1142 bv()->buffer()->params.textclass,
1144 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1146 selection.start.par(), endpit,
1147 selection.start.pos(), endpos,
1149 // sometimes necessary
1151 selection.start.par()->stripLeadingSpaces();
1153 redoParagraphs(selection.start.par(), boost::next(endpit));
1154 // cutSelection can invalidate the cursor so we need to set
1156 // we prefer the end for when tracking changes
1160 // need a valid cursor. (Lgb)
1163 setCursor(cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1169 void LyXText::copySelection()
1171 // stuff the selection onto the X clipboard, from an explicit copy request
1172 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1174 // this doesnt make sense, if there is no selection
1175 if (!selection.set())
1178 // ok we have a selection. This is always between selection.start
1179 // and sel_end cursor
1181 // copy behind a space if there is one
1182 while (selection.start.par()->size() > selection.start.pos()
1183 && selection.start.par()->isLineSeparator(selection.start.pos())
1184 && (selection.start.par() != selection.end.par()
1185 || selection.start.pos() < selection.end.pos()))
1186 selection.start.pos(selection.start.pos() + 1);
1188 CutAndPaste::copySelection(selection.start.par(),
1189 selection.end.par(),
1190 selection.start.pos(), selection.end.pos(),
1191 bv()->buffer()->params.textclass);
1195 void LyXText::pasteSelection(size_t sel_index)
1197 // this does not make sense, if there is nothing to paste
1198 if (!CutAndPaste::checkPastePossible())
1201 recordUndo(bv(), Undo::INSERT, cursor.par());
1203 ParagraphList::iterator endpit;
1208 boost::tie(ppp, endpit) =
1209 CutAndPaste::pasteSelection(*bv()->buffer(),
1211 cursor.par(), cursor.pos(),
1212 bv()->buffer()->params.textclass,
1214 bufferErrors(*bv()->buffer(), el);
1215 bv()->showErrorList(_("Paste"));
1217 redoParagraphs(cursor.par(), endpit);
1219 setCursor(cursor.par(), cursor.pos());
1222 selection.cursor = cursor;
1223 setCursor(ppp.first, ppp.second);
1229 void LyXText::setSelectionRange(lyx::pos_type length)
1234 selection.cursor = cursor;
1241 // simple replacing. The font of the first selected character is used
1242 void LyXText::replaceSelectionWithString(string const & str)
1244 recordUndo(bv(), Undo::ATOMIC);
1247 if (!selection.set()) { // create a dummy selection
1248 selection.end = cursor;
1249 selection.start = cursor;
1252 // Get font setting before we cut
1253 pos_type pos = selection.end.pos();
1254 LyXFont const font = selection.start.par()
1255 ->getFontSettings(bv()->buffer()->params,
1256 selection.start.pos());
1258 // Insert the new string
1259 string::const_iterator cit = str.begin();
1260 string::const_iterator end = str.end();
1261 for (; cit != end; ++cit) {
1262 selection.end.par()->insertChar(pos, (*cit), font);
1266 // Cut the selection
1267 cutSelection(true, false);
1273 // needed to insert the selection
1274 void LyXText::insertStringAsLines(string const & str)
1276 ParagraphList::iterator pit = cursor.par();
1277 pos_type pos = cursor.pos();
1278 ParagraphList::iterator endpit = boost::next(cursor.par());
1280 recordUndo(bv(), Undo::ATOMIC);
1282 // only to be sure, should not be neccessary
1285 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1287 redoParagraphs(cursor.par(), endpit);
1288 setCursor(cursor.par(), cursor.pos());
1289 selection.cursor = cursor;
1290 setCursor(pit, pos);
1295 // turns double-CR to single CR, others where converted into one
1296 // blank. Then InsertStringAsLines is called
1297 void LyXText::insertStringAsParagraphs(string const & str)
1299 string linestr(str);
1300 bool newline_inserted = false;
1301 string::size_type const siz = linestr.length();
1303 for (string::size_type i = 0; i < siz; ++i) {
1304 if (linestr[i] == '\n') {
1305 if (newline_inserted) {
1306 // we know that \r will be ignored by
1307 // InsertStringA. Of course, it is a dirty
1308 // trick, but it works...
1309 linestr[i - 1] = '\r';
1313 newline_inserted = true;
1315 } else if (IsPrintable(linestr[i])) {
1316 newline_inserted = false;
1319 insertStringAsLines(linestr);
1323 bool LyXText::setCursor(ParagraphList::iterator pit,
1325 bool setfont, bool boundary)
1327 LyXCursor old_cursor = cursor;
1328 setCursorIntern(pit, pos, setfont, boundary);
1329 return deleteEmptyParagraphMechanism(old_cursor);
1333 void LyXText::redoCursor()
1335 #warning maybe the same for selections?
1336 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1340 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1341 pos_type pos, bool boundary)
1343 Assert(pit != ownerParagraphs().end());
1347 cur.boundary(boundary);
1351 // get the cursor y position in text
1353 RowList::iterator row = getRow(pit, pos);
1356 // y is now the beginning of the cursor row
1357 y += row->baseline();
1358 // y is now the cursor baseline
1361 pos_type last = lastPrintablePos(*pit, row);
1363 // None of these should happen, but we're scaredy-cats
1364 if (pos > pit->size()) {
1365 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1368 } else if (pos > last + 1) {
1369 lyxerr << "dont like 2 please report" << endl;
1370 // This shouldn't happen.
1373 } else if (pos < row->pos()) {
1374 lyxerr << "dont like 3 please report" << endl;
1379 // now get the cursors x position
1380 float x = getCursorX(pit, row, pos, last, boundary);
1386 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1387 pos_type pos, pos_type last, bool boundary) const
1389 pos_type cursor_vpos = 0;
1390 double x = rit->x();
1391 double fill_separator = rit->fill_separator();
1392 double fill_hfill = rit->fill_hfill();
1393 double fill_label_hfill = rit->fill_label_hfill();
1394 pos_type const rit_pos = rit->pos();
1397 cursor_vpos = rit_pos;
1398 else if (pos > last && !boundary)
1399 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1400 ? rit_pos : last + 1;
1401 else if (pos > rit_pos && (pos > last || boundary))
1402 // Place cursor after char at (logical) position pos - 1
1403 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1404 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1406 // Place cursor before char at (logical) position pos
1407 cursor_vpos = (bidi_level(pos) % 2 == 0)
1408 ? log2vis(pos) : log2vis(pos) + 1;
1410 pos_type body_pos = pit->beginningOfBody();
1412 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1415 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1416 pos_type pos = vis2log(vpos);
1417 if (body_pos > 0 && pos == body_pos - 1) {
1418 x += fill_label_hfill +
1419 font_metrics::width(
1420 pit->layout()->labelsep, getLabelFont(pit));
1421 if (pit->isLineSeparator(body_pos - 1))
1422 x -= singleWidth(pit, body_pos - 1);
1425 if (hfillExpansion(*pit, rit, pos)) {
1426 x += singleWidth(pit, pos);
1427 if (pos >= body_pos)
1430 x += fill_label_hfill;
1431 } else if (pit->isSeparator(pos)) {
1432 x += singleWidth(pit, pos);
1433 if (pos >= body_pos)
1434 x += fill_separator;
1436 x += singleWidth(pit, pos);
1442 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1443 pos_type pos, bool setfont, bool boundary)
1445 setCursor(cursor, pit, pos, boundary);
1451 void LyXText::setCurrentFont()
1453 pos_type pos = cursor.pos();
1454 ParagraphList::iterator pit = cursor.par();
1456 if (cursor.boundary() && pos > 0)
1460 if (pos == pit->size())
1462 else // potentional bug... BUG (Lgb)
1463 if (pit->isSeparator(pos)) {
1464 if (pos > cursorRow()->pos() &&
1465 bidi_level(pos) % 2 ==
1466 bidi_level(pos - 1) % 2)
1468 else if (pos + 1 < pit->size())
1473 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1474 real_current_font = getFont(pit, pos);
1476 if (cursor.pos() == pit->size() &&
1477 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1478 !cursor.boundary()) {
1479 Language const * lang =
1480 pit->getParLanguage(bv()->buffer()->params);
1481 current_font.setLanguage(lang);
1482 current_font.setNumber(LyXFont::OFF);
1483 real_current_font.setLanguage(lang);
1484 real_current_font.setNumber(LyXFont::OFF);
1489 // returns the column near the specified x-coordinate of the row
1490 // x is set to the real beginning of this column
1491 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1492 RowList::iterator rit, int & x, bool & boundary) const
1494 double tmpx = rit->x();
1495 double fill_separator = rit->fill_separator();
1496 double fill_hfill = rit->fill_hfill();
1497 double fill_label_hfill = rit->fill_label_hfill();
1499 pos_type vc = rit->pos();
1500 pos_type last = lastPrintablePos(*pit, rit);
1502 LyXLayout_ptr const & layout = pit->layout();
1504 bool left_side = false;
1506 pos_type body_pos = pit->beginningOfBody();
1507 double last_tmpx = tmpx;
1510 (body_pos - 1 > last ||
1511 !pit->isLineSeparator(body_pos - 1)))
1514 // check for empty row
1520 while (vc <= last && tmpx <= x) {
1523 if (body_pos > 0 && c == body_pos - 1) {
1524 tmpx += fill_label_hfill +
1525 font_metrics::width(layout->labelsep, getLabelFont(pit));
1526 if (pit->isLineSeparator(body_pos - 1))
1527 tmpx -= singleWidth(pit, body_pos - 1);
1530 if (hfillExpansion(*pit, rit, c)) {
1531 tmpx += singleWidth(pit, c);
1535 tmpx += fill_label_hfill;
1536 } else if (pit->isSeparator(c)) {
1537 tmpx += singleWidth(pit, c);
1539 tmpx += fill_separator;
1541 tmpx += singleWidth(pit, c);
1546 if ((tmpx + last_tmpx) / 2 > x) {
1551 if (vc > last + 1) // This shouldn't happen.
1555 // This (rtl_support test) is not needed, but gives
1556 // some speedup if rtl_support == false
1557 bool const lastrow = lyxrc.rtl_support
1558 && boost::next(rit) == pit->rows.end();
1560 // If lastrow is false, we don't need to compute
1561 // the value of rtl.
1562 bool const rtl = (lastrow)
1563 ? pit->isRightToLeftPar(bv()->buffer()->params)
1566 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1567 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1569 else if (vc == rit->pos()) {
1571 if (bidi_level(c) % 2 == 1)
1574 c = vis2log(vc - 1);
1575 bool const rtl = (bidi_level(c) % 2 == 1);
1576 if (left_side == rtl) {
1578 boundary = isBoundary(*bv()->buffer(), *pit, c);
1582 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1583 if (bidi_level(last) % 2 == 0)
1584 tmpx -= singleWidth(pit, last);
1586 tmpx += singleWidth(pit, last);
1596 void LyXText::setCursorFromCoordinates(int x, int y)
1598 LyXCursor old_cursor = cursor;
1599 setCursorFromCoordinates(cursor, x, y);
1601 deleteEmptyParagraphMechanism(old_cursor);
1605 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1607 // Get the row first.
1608 ParagraphList::iterator pit;
1609 RowList::iterator rit = getRowNearY(y, pit);
1613 pos_type const column = getColumnNearX(pit, rit, x, bound);
1615 cur.pos(rit->pos() + column);
1617 cur.y(y + rit->baseline());
1619 cur.boundary(bound);
1623 void LyXText::cursorLeft(bool internal)
1625 if (cursor.pos() > 0) {
1626 bool boundary = cursor.boundary();
1627 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1628 if (!internal && !boundary &&
1629 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1630 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1631 } else if (cursor.par() != ownerParagraphs().begin()) {
1632 // steps into the paragraph above
1633 ParagraphList::iterator pit = boost::prior(cursor.par());
1634 setCursor(pit, pit->size());
1639 void LyXText::cursorRight(bool internal)
1641 bool const at_end = (cursor.pos() == cursor.par()->size());
1642 bool const at_newline = !at_end &&
1643 cursor.par()->isNewline(cursor.pos());
1645 if (!internal && cursor.boundary() && !at_newline)
1646 setCursor(cursor.par(), cursor.pos(), true, false);
1648 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1650 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1651 setCursor(cursor.par(), cursor.pos(), true, true);
1652 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1653 setCursor(boost::next(cursor.par()), 0);
1657 void LyXText::cursorUp(bool selecting)
1660 int x = cursor.x_fix();
1661 int y = cursor.y() - cursorRow()->baseline() - 1;
1662 setCursorFromCoordinates(x, y);
1664 int topy = bv_owner->top_y();
1665 int y1 = cursor.y() - topy;
1668 InsetOld * inset_hit = checkInsetHit(x, y1);
1669 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1670 inset_hit->localDispatch(
1671 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1675 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1676 cursorRow()->baseline() << endl;
1677 setCursorFromCoordinates(cursor.x_fix(),
1678 cursor.y() - cursorRow()->baseline() - 1);
1683 void LyXText::cursorDown(bool selecting)
1686 int x = cursor.x_fix();
1687 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1688 setCursorFromCoordinates(x, y);
1689 if (!selecting && cursorRow() == cursorIRow()) {
1690 int topy = bv_owner->top_y();
1691 int y1 = cursor.y() - topy;
1694 InsetOld * inset_hit = checkInsetHit(x, y1);
1695 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1696 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1697 inset_hit->localDispatch(cmd);
1701 setCursorFromCoordinates(cursor.x_fix(),
1702 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1707 void LyXText::cursorUpParagraph()
1709 if (cursor.pos() > 0)
1710 setCursor(cursor.par(), 0);
1711 else if (cursor.par() != ownerParagraphs().begin())
1712 setCursor(boost::prior(cursor.par()), 0);
1716 void LyXText::cursorDownParagraph()
1718 ParagraphList::iterator par = cursor.par();
1719 ParagraphList::iterator next_par = boost::next(par);
1721 if (next_par != ownerParagraphs().end())
1722 setCursor(next_par, 0);
1724 setCursor(par, par->size());
1728 // fix the cursor `cur' after a characters has been deleted at `where'
1729 // position. Called by deleteEmptyParagraphMechanism
1730 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1732 // if cursor is not in the paragraph where the delete occured,
1734 if (cur.par() != where.par())
1737 // if cursor position is after the place where the delete occured,
1739 if (cur.pos() > where.pos())
1740 cur.pos(cur.pos()-1);
1742 // check also if we don't want to set the cursor on a spot behind the
1743 // pagragraph because we erased the last character.
1744 if (cur.pos() > cur.par()->size())
1745 cur.pos(cur.par()->size());
1747 // recompute row et al. for this cursor
1748 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1752 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1754 // Would be wrong to delete anything if we have a selection.
1755 if (selection.set())
1758 // We allow all kinds of "mumbo-jumbo" when freespacing.
1759 if (old_cursor.par()->isFreeSpacing())
1762 /* Ok I'll put some comments here about what is missing.
1763 I have fixed BackSpace (and thus Delete) to not delete
1764 double-spaces automagically. I have also changed Cut,
1765 Copy and Paste to hopefully do some sensible things.
1766 There are still some small problems that can lead to
1767 double spaces stored in the document file or space at
1768 the beginning of paragraphs. This happens if you have
1769 the cursor between to spaces and then save. Or if you
1770 cut and paste and the selection have a space at the
1771 beginning and then save right after the paste. I am
1772 sure none of these are very hard to fix, but I will
1773 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1774 that I can get some feedback. (Lgb)
1777 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1778 // delete the LineSeparator.
1781 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1782 // delete the LineSeparator.
1785 // If the pos around the old_cursor were spaces, delete one of them.
1786 if (old_cursor.par() != cursor.par()
1787 || old_cursor.pos() != cursor.pos()) {
1789 // Only if the cursor has really moved
1790 if (old_cursor.pos() > 0
1791 && old_cursor.pos() < old_cursor.par()->size()
1792 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1793 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1794 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1795 redoParagraph(old_cursor.par());
1799 #ifdef WITH_WARNINGS
1800 #warning This will not work anymore when we have multiple views of the same buffer
1801 // In this case, we will have to correct also the cursors held by
1802 // other bufferviews. It will probably be easier to do that in a more
1803 // automated way in LyXCursor code. (JMarc 26/09/2001)
1805 // correct all cursors held by the LyXText
1806 fixCursorAfterDelete(cursor, old_cursor);
1807 fixCursorAfterDelete(selection.cursor, old_cursor);
1808 fixCursorAfterDelete(selection.start, old_cursor);
1809 fixCursorAfterDelete(selection.end, old_cursor);
1814 // don't delete anything if this is the ONLY paragraph!
1815 if (ownerParagraphs().size() == 1)
1818 // Do not delete empty paragraphs with keepempty set.
1819 if (old_cursor.par()->allowEmpty())
1822 // only do our magic if we changed paragraph
1823 if (old_cursor.par() == cursor.par())
1826 // record if we have deleted a paragraph
1827 // we can't possibly have deleted a paragraph before this point
1828 bool deleted = false;
1830 if (old_cursor.par()->empty() ||
1831 (old_cursor.par()->size() == 1 &&
1832 old_cursor.par()->isLineSeparator(0))) {
1833 // ok, we will delete something
1834 LyXCursor tmpcursor;
1838 bool selection_position_was_oldcursor_position = (
1839 selection.cursor.par() == old_cursor.par()
1840 && selection.cursor.pos() == old_cursor.pos());
1843 cursor = old_cursor; // that undo can restore the right cursor position
1845 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1846 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1849 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1853 ownerParagraphs().erase(old_cursor.par());
1857 setCursorIntern(cursor.par(), cursor.pos());
1859 if (selection_position_was_oldcursor_position) {
1860 // correct selection
1861 selection.cursor = cursor;
1865 if (old_cursor.par()->stripLeadingSpaces()) {
1866 redoParagraph(old_cursor.par());
1868 setCursorIntern(cursor.par(), cursor.pos());
1869 selection.cursor = cursor;
1876 ParagraphList & LyXText::ownerParagraphs() const
1882 bool LyXText::isInInset() const
1884 // Sub-level has non-null bv owner and non-null inset owner.
1885 return inset_owner != 0;
1889 int defaultRowHeight()
1891 LyXFont const font(LyXFont::ALL_SANE);
1892 return int(font_metrics::maxAscent(font)
1893 + font_metrics::maxDescent(font) * 1.5);