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"
40 #include "FloatList.h"
42 #include "ParagraphParameters.h"
44 #include "lyxrow_funcs.h"
45 #include "metricsinfo.h"
46 #include "paragraph_funcs.h"
48 #include "insets/insetbibitem.h"
49 #include "insets/insetenv.h"
50 #include "insets/insetfloat.h"
51 #include "insets/insetwrap.h"
53 #include "support/LAssert.h"
54 #include "support/textutils.h"
55 #include "support/lstrings.h"
57 #include <boost/tuple/tuple.hpp>
61 using namespace lyx::support;
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);
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 if (cursor.par()->empty())
677 RowList::iterator rit = cursorRow();
678 RowList::iterator next_rit = boost::next(rit);
679 RowList::iterator end = boost::next(rit);
680 ParagraphList::iterator pit = cursor.par();
681 pos_type last_pos = lastPos(*pit, rit);
683 if (next_rit == end) {
687 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
692 setCursor(pit, last_pos);
696 void LyXText::cursorTop()
698 setCursor(ownerParagraphs().begin(), 0);
702 void LyXText::cursorBottom()
704 ParagraphList::iterator lastpit =
705 boost::prior(ownerParagraphs().end());
706 setCursor(lastpit, lastpit->size());
710 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
712 // If the mask is completely neutral, tell user
713 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
714 // Could only happen with user style
715 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
719 // Try implicit word selection
720 // If there is a change in the language the implicit word selection
722 LyXCursor resetCursor = cursor;
723 bool implicitSelection = (font.language() == ignore_language
724 && font.number() == LyXFont::IGNORE)
725 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
728 setFont(font, toggleall);
730 // Implicit selections are cleared afterwards
731 //and cursor is set to the original position.
732 if (implicitSelection) {
734 cursor = resetCursor;
735 setCursor(cursor.par(), cursor.pos());
736 selection.cursor = cursor;
741 string LyXText::getStringToIndex()
743 // Try implicit word selection
744 // If there is a change in the language the implicit word selection
746 LyXCursor const reset_cursor = cursor;
747 bool const implicitSelection =
748 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
751 if (!selection.set())
752 bv()->owner()->message(_("Nothing to index!"));
753 else if (selection.start.par() != selection.end.par())
754 bv()->owner()->message(_("Cannot index more than one paragraph!"));
756 idxstring = selectionAsString(bv()->buffer(), false);
758 // Reset cursors to their original position.
759 cursor = reset_cursor;
760 setCursor(cursor.par(), cursor.pos());
761 selection.cursor = cursor;
763 // Clear the implicit selection.
764 if (implicitSelection)
771 // the DTP switches for paragraphs. LyX will store them in the first
772 // physical paragraph. When a paragraph is broken, the top settings rest,
773 // the bottom settings are given to the new one. So I can make sure,
774 // they do not duplicate themself and you cannnot make dirty things with
777 void LyXText::setParagraph(bool line_top, bool line_bottom,
778 bool pagebreak_top, bool pagebreak_bottom,
779 VSpace const & space_top,
780 VSpace const & space_bottom,
781 Spacing const & spacing,
783 string const & labelwidthstring,
786 LyXCursor tmpcursor = cursor;
787 if (!selection.set()) {
788 selection.start = cursor;
789 selection.end = cursor;
792 // make sure that the depth behind the selection are restored, too
793 ParagraphList::iterator endpit = boost::next(selection.end.par());
794 ParagraphList::iterator undoendpit = endpit;
795 ParagraphList::iterator pars_end = ownerParagraphs().end();
797 if (endpit != pars_end && endpit->getDepth()) {
798 while (endpit != pars_end && endpit->getDepth()) {
802 } else if (endpit != pars_end) {
803 // because of parindents etc.
807 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
808 boost::prior(undoendpit));
811 ParagraphList::iterator tmppit = selection.end.par();
813 while (tmppit != boost::prior(selection.start.par())) {
814 setCursor(tmppit, 0);
816 ParagraphList::iterator pit = cursor.par();
817 ParagraphParameters & params = pit->params();
819 params.lineTop(line_top);
820 params.lineBottom(line_bottom);
821 params.pagebreakTop(pagebreak_top);
822 params.pagebreakBottom(pagebreak_bottom);
823 params.spaceTop(space_top);
824 params.spaceBottom(space_bottom);
825 params.spacing(spacing);
826 // does the layout allow the new alignment?
827 LyXLayout_ptr const & layout = pit->layout();
829 if (align == LYX_ALIGN_LAYOUT)
830 align = layout->align;
831 if (align & layout->alignpossible) {
832 if (align == layout->align)
833 params.align(LYX_ALIGN_LAYOUT);
837 pit->setLabelWidthString(labelwidthstring);
838 params.noindent(noindent);
839 tmppit = boost::prior(pit);
842 redoParagraphs(selection.start.par(), endpit);
845 setCursor(selection.start.par(), selection.start.pos());
846 selection.cursor = cursor;
847 setCursor(selection.end.par(), selection.end.pos());
849 setCursor(tmpcursor.par(), tmpcursor.pos());
855 // set the counter of a paragraph. This includes the labels
856 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
858 LyXTextClass const & textclass = buf->params.getLyXTextClass();
859 LyXLayout_ptr const & layout = pit->layout();
861 if (pit != ownerParagraphs().begin()) {
863 pit->params().appendix(boost::prior(pit)->params().appendix());
864 if (!pit->params().appendix() &&
865 pit->params().startOfAppendix()) {
866 pit->params().appendix(true);
867 textclass.counters().reset();
869 pit->enumdepth = boost::prior(pit)->enumdepth;
870 pit->itemdepth = boost::prior(pit)->itemdepth;
872 pit->params().appendix(pit->params().startOfAppendix());
877 // Maybe we have to increment the enumeration depth.
878 // BUT, enumeration in a footnote is considered in isolation from its
879 // surrounding paragraph so don't increment if this is the
880 // first line of the footnote
881 // AND, bibliographies can't have their depth changed ie. they
882 // are always of depth 0
883 if (pit != ownerParagraphs().begin()
884 && boost::prior(pit)->getDepth() < pit->getDepth()
885 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
886 && pit->enumdepth < 3
887 && layout->labeltype != LABEL_BIBLIO) {
891 // Maybe we have to decrement the enumeration depth, see note above
892 if (pit != ownerParagraphs().begin()
893 && boost::prior(pit)->getDepth() > pit->getDepth()
894 && layout->labeltype != LABEL_BIBLIO) {
895 pit->enumdepth = depthHook(pit, ownerParagraphs(),
896 pit->getDepth())->enumdepth;
899 if (!pit->params().labelString().empty()) {
900 pit->params().labelString(string());
903 if (layout->margintype == MARGIN_MANUAL) {
904 if (pit->params().labelWidthString().empty())
905 pit->setLabelWidthString(layout->labelstring());
907 pit->setLabelWidthString(string());
910 // is it a layout that has an automatic label?
911 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
912 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
916 if (i >= 0 && i <= buf->params.secnumdepth) {
920 textclass.counters().step(layout->latexname());
922 // Is there a label? Useful for Chapter layout
923 if (!pit->params().appendix()) {
924 s << buf->B_(layout->labelstring());
926 s << buf->B_(layout->labelstring_appendix());
929 // Use of an integer is here less than elegant. For now.
930 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
931 if (!pit->params().appendix()) {
932 numbertype = "sectioning";
934 numbertype = "appendix";
935 if (pit->isRightToLeftPar(buf->params))
942 << textclass.counters()
943 .numberLabel(layout->latexname(),
944 numbertype, langtype, head);
946 pit->params().labelString(STRCONV(s.str()));
948 // reset enum counters
949 textclass.counters().reset("enum");
950 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
951 textclass.counters().reset("enum");
952 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
954 // Yes I know this is a really, really! bad solution
956 string enumcounter("enum");
958 switch (pit->enumdepth) {
970 // not a valid enumdepth...
974 textclass.counters().step(enumcounter);
976 s << textclass.counters()
977 .numberLabel(enumcounter, "enumeration");
978 pit->params().labelString(STRCONV(s.str()));
980 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
981 textclass.counters().step("bibitem");
982 int number = textclass.counters().value("bibitem");
983 if (pit->bibitem()) {
984 pit->bibitem()->setCounter(number);
985 pit->params().labelString(layout->labelstring());
987 // In biblio should't be following counters but...
989 string s = buf->B_(layout->labelstring());
992 if (layout->labeltype == LABEL_SENSITIVE) {
993 ParagraphList::iterator end = ownerParagraphs().end();
994 ParagraphList::iterator tmppit = pit;
997 while (tmppit != end && tmppit->inInset()
998 // the single '=' is intended below
999 && (in = tmppit->inInset()->owner()))
1001 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1002 in->lyxCode() == InsetOld::WRAP_CODE) {
1006 tmppit = ownerParagraphs().begin();
1007 for ( ; tmppit != end; ++tmppit)
1008 if (&*tmppit == in->parOwner())
1016 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1017 type = static_cast<InsetFloat*>(in)->params().type;
1018 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1019 type = static_cast<InsetWrap*>(in)->params().type;
1023 Floating const & fl = textclass.floats().getType(type);
1025 textclass.counters().step(fl.type());
1027 // Doesn't work... yet.
1028 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1030 // par->SetLayout(0);
1031 // s = layout->labelstring;
1032 s = _("Senseless: ");
1035 pit->params().labelString(s);
1037 // reset the enumeration counter. They are always reset
1038 // when there is any other layout between
1039 // Just fall-through between the cases so that all
1040 // enum counters deeper than enumdepth is also reset.
1041 switch (pit->enumdepth) {
1043 textclass.counters().reset("enumi");
1045 textclass.counters().reset("enumii");
1047 textclass.counters().reset("enumiii");
1049 textclass.counters().reset("enumiv");
1055 // Updates all counters. Paragraphs with changed label string will be rebroken
1056 void LyXText::updateCounters()
1059 bv()->buffer()->params.getLyXTextClass().counters().reset();
1061 ParagraphList::iterator beg = ownerParagraphs().begin();
1062 ParagraphList::iterator end = ownerParagraphs().end();
1063 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1064 string const oldLabel = pit->params().labelString();
1066 size_t maxdepth = 0;
1068 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1070 if (pit->params().depth() > maxdepth)
1071 pit->params().depth(maxdepth);
1073 // setCounter can potentially change the labelString.
1074 setCounter(bv()->buffer(), pit);
1076 string const & newLabel = pit->params().labelString();
1078 if (oldLabel != newLabel)
1084 void LyXText::insertInset(InsetOld * inset)
1086 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1088 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1090 cursor.par()->insertInset(cursor.pos(), inset);
1091 // Just to rebreak and refresh correctly.
1092 // The character will not be inserted a second time
1093 insertChar(Paragraph::META_INSET);
1094 // If we enter a highly editable inset the cursor should be before
1095 // the inset. After an Undo LyX tries to call inset->edit(...)
1096 // and fails if the cursor is behind the inset and getInset
1097 // does not return the inset!
1098 if (isHighlyEditableInset(inset))
1104 void LyXText::cutSelection(bool doclear, bool realcut)
1106 // Stuff what we got on the clipboard. Even if there is no selection.
1108 // There is a problem with having the stuffing here in that the
1109 // larger the selection the slower LyX will get. This can be
1110 // solved by running the line below only when the selection has
1111 // finished. The solution used currently just works, to make it
1112 // faster we need to be more clever and probably also have more
1113 // calls to stuffClipboard. (Lgb)
1114 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1116 // This doesn't make sense, if there is no selection
1117 if (!selection.set())
1120 // OK, we have a selection. This is always between selection.start
1121 // and selection.end
1123 // make sure that the depth behind the selection are restored, too
1124 ParagraphList::iterator endpit = boost::next(selection.end.par());
1125 ParagraphList::iterator undoendpit = endpit;
1126 ParagraphList::iterator pars_end = ownerParagraphs().end();
1128 if (endpit != pars_end && endpit->getDepth()) {
1129 while (endpit != pars_end && endpit->getDepth()) {
1131 undoendpit = endpit;
1133 } else if (endpit != pars_end) {
1134 // because of parindents etc.
1138 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1139 boost::prior(undoendpit));
1141 endpit = selection.end.par();
1142 int endpos = selection.end.pos();
1144 boost::tie(endpit, endpos) = realcut ?
1145 CutAndPaste::cutSelection(bv()->buffer()->params,
1147 selection.start.par(), endpit,
1148 selection.start.pos(), endpos,
1149 bv()->buffer()->params.textclass,
1151 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1153 selection.start.par(), endpit,
1154 selection.start.pos(), endpos,
1156 // sometimes necessary
1158 selection.start.par()->stripLeadingSpaces();
1160 redoParagraphs(selection.start.par(), boost::next(endpit));
1161 // cutSelection can invalidate the cursor so we need to set
1163 // we prefer the end for when tracking changes
1167 // need a valid cursor. (Lgb)
1170 setCursor(cursor.par(), cursor.pos());
1171 selection.cursor = cursor;
1176 void LyXText::copySelection()
1178 // stuff the selection onto the X clipboard, from an explicit copy request
1179 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1181 // this doesnt make sense, if there is no selection
1182 if (!selection.set())
1185 // ok we have a selection. This is always between selection.start
1186 // and sel_end cursor
1188 // copy behind a space if there is one
1189 while (selection.start.par()->size() > selection.start.pos()
1190 && selection.start.par()->isLineSeparator(selection.start.pos())
1191 && (selection.start.par() != selection.end.par()
1192 || selection.start.pos() < selection.end.pos()))
1193 selection.start.pos(selection.start.pos() + 1);
1195 CutAndPaste::copySelection(selection.start.par(),
1196 selection.end.par(),
1197 selection.start.pos(), selection.end.pos(),
1198 bv()->buffer()->params.textclass);
1202 void LyXText::pasteSelection(size_t sel_index)
1204 // this does not make sense, if there is nothing to paste
1205 if (!CutAndPaste::checkPastePossible())
1208 recordUndo(bv(), Undo::INSERT, cursor.par());
1210 ParagraphList::iterator endpit;
1215 boost::tie(ppp, endpit) =
1216 CutAndPaste::pasteSelection(*bv()->buffer(),
1218 cursor.par(), cursor.pos(),
1219 bv()->buffer()->params.textclass,
1221 bufferErrors(*bv()->buffer(), el);
1222 bv()->showErrorList(_("Paste"));
1224 redoParagraphs(cursor.par(), endpit);
1226 setCursor(cursor.par(), cursor.pos());
1229 selection.cursor = cursor;
1230 setCursor(ppp.first, ppp.second);
1236 void LyXText::setSelectionRange(lyx::pos_type length)
1241 selection.cursor = cursor;
1248 // simple replacing. The font of the first selected character is used
1249 void LyXText::replaceSelectionWithString(string const & str)
1251 recordUndo(bv(), Undo::ATOMIC);
1254 if (!selection.set()) { // create a dummy selection
1255 selection.end = cursor;
1256 selection.start = cursor;
1259 // Get font setting before we cut
1260 pos_type pos = selection.end.pos();
1261 LyXFont const font = selection.start.par()
1262 ->getFontSettings(bv()->buffer()->params,
1263 selection.start.pos());
1265 // Insert the new string
1266 string::const_iterator cit = str.begin();
1267 string::const_iterator end = str.end();
1268 for (; cit != end; ++cit) {
1269 selection.end.par()->insertChar(pos, (*cit), font);
1273 // Cut the selection
1274 cutSelection(true, false);
1280 // needed to insert the selection
1281 void LyXText::insertStringAsLines(string const & str)
1283 ParagraphList::iterator pit = cursor.par();
1284 pos_type pos = cursor.pos();
1285 ParagraphList::iterator endpit = boost::next(cursor.par());
1287 recordUndo(bv(), Undo::ATOMIC);
1289 // only to be sure, should not be neccessary
1292 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1294 redoParagraphs(cursor.par(), endpit);
1295 setCursor(cursor.par(), cursor.pos());
1296 selection.cursor = cursor;
1297 setCursor(pit, pos);
1302 // turns double-CR to single CR, others where converted into one
1303 // blank. Then InsertStringAsLines is called
1304 void LyXText::insertStringAsParagraphs(string const & str)
1306 string linestr(str);
1307 bool newline_inserted = false;
1308 string::size_type const siz = linestr.length();
1310 for (string::size_type i = 0; i < siz; ++i) {
1311 if (linestr[i] == '\n') {
1312 if (newline_inserted) {
1313 // we know that \r will be ignored by
1314 // InsertStringA. Of course, it is a dirty
1315 // trick, but it works...
1316 linestr[i - 1] = '\r';
1320 newline_inserted = true;
1322 } else if (IsPrintable(linestr[i])) {
1323 newline_inserted = false;
1326 insertStringAsLines(linestr);
1330 bool LyXText::setCursor(ParagraphList::iterator pit,
1332 bool setfont, bool boundary)
1334 LyXCursor old_cursor = cursor;
1335 setCursorIntern(pit, pos, setfont, boundary);
1336 return deleteEmptyParagraphMechanism(old_cursor);
1340 void LyXText::redoCursor()
1342 #warning maybe the same for selections?
1343 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1347 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1348 pos_type pos, bool boundary)
1350 Assert(pit != ownerParagraphs().end());
1354 cur.boundary(boundary);
1358 // get the cursor y position in text
1360 RowList::iterator row = getRow(pit, pos);
1363 RowList::iterator old_row = row;
1364 // if we are before the first char of this row and are still in the
1365 // same paragraph and there is a previous row then put the cursor on
1366 // the end of the previous row
1367 cur.iy(y + row->baseline());
1369 // y is now the beginning of the cursor row
1370 y += row->baseline();
1371 // y is now the cursor baseline
1374 pos_type last = lastPrintablePos(*pit, old_row);
1376 // None of these should happen, but we're scaredy-cats
1377 if (pos > pit->size()) {
1378 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1381 } else if (pos > last + 1) {
1382 lyxerr << "dont like 2 please report" << endl;
1383 // This shouldn't happen.
1386 } else if (pos < row->pos()) {
1387 lyxerr << "dont like 3 please report" << endl;
1392 // now get the cursors x position
1393 float x = getCursorX(pit, row, pos, last, boundary);
1396 if (old_row != row) {
1397 x = getCursorX(pit, old_row, pos, last, boundary);
1401 /* We take out this for the time being because 1) the redraw code is not
1402 prepared to this yet and 2) because some good policy has yet to be decided
1403 while editting: for instance how to act on rows being created/deleted
1407 //if the cursor is in a visible row, anchor to it
1409 if (topy < y && y < topy + bv()->workHeight())
1415 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1416 pos_type pos, pos_type last, bool boundary) const
1418 pos_type cursor_vpos = 0;
1419 double x = rit->x();
1420 double fill_separator = rit->fill_separator();
1421 double fill_hfill = rit->fill_hfill();
1422 double fill_label_hfill = rit->fill_label_hfill();
1423 pos_type const rit_pos = rit->pos();
1426 cursor_vpos = rit_pos;
1427 else if (pos > last && !boundary)
1428 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1429 ? rit_pos : last + 1;
1430 else if (pos > rit_pos && (pos > last || boundary))
1431 // Place cursor after char at (logical) position pos - 1
1432 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1433 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1435 // Place cursor before char at (logical) position pos
1436 cursor_vpos = (bidi_level(pos) % 2 == 0)
1437 ? log2vis(pos) : log2vis(pos) + 1;
1439 pos_type body_pos = pit->beginningOfBody();
1441 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1444 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1445 pos_type pos = vis2log(vpos);
1446 if (body_pos > 0 && pos == body_pos - 1) {
1447 x += fill_label_hfill +
1448 font_metrics::width(
1449 pit->layout()->labelsep, getLabelFont(pit));
1450 if (pit->isLineSeparator(body_pos - 1))
1451 x -= singleWidth(pit, body_pos - 1);
1454 if (hfillExpansion(*pit, rit, pos)) {
1455 x += singleWidth(pit, pos);
1456 if (pos >= body_pos)
1459 x += fill_label_hfill;
1460 } else if (pit->isSeparator(pos)) {
1461 x += singleWidth(pit, pos);
1462 if (pos >= body_pos)
1463 x += fill_separator;
1465 x += singleWidth(pit, pos);
1471 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1472 pos_type pos, bool setfont, bool boundary)
1474 setCursor(cursor, pit, pos, boundary);
1480 void LyXText::setCurrentFont()
1482 pos_type pos = cursor.pos();
1483 ParagraphList::iterator pit = cursor.par();
1485 if (cursor.boundary() && pos > 0)
1489 if (pos == pit->size())
1491 else // potentional bug... BUG (Lgb)
1492 if (pit->isSeparator(pos)) {
1493 if (pos > cursorRow()->pos() &&
1494 bidi_level(pos) % 2 ==
1495 bidi_level(pos - 1) % 2)
1497 else if (pos + 1 < pit->size())
1502 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1503 real_current_font = getFont(pit, pos);
1505 if (cursor.pos() == pit->size() &&
1506 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1507 !cursor.boundary()) {
1508 Language const * lang =
1509 pit->getParLanguage(bv()->buffer()->params);
1510 current_font.setLanguage(lang);
1511 current_font.setNumber(LyXFont::OFF);
1512 real_current_font.setLanguage(lang);
1513 real_current_font.setNumber(LyXFont::OFF);
1518 // returns the column near the specified x-coordinate of the row
1519 // x is set to the real beginning of this column
1520 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1521 RowList::iterator rit, int & x, bool & boundary) const
1523 double tmpx = rit->x();
1524 double fill_separator = rit->fill_separator();
1525 double fill_hfill = rit->fill_hfill();
1526 double fill_label_hfill = rit->fill_label_hfill();
1528 pos_type vc = rit->pos();
1529 pos_type last = lastPrintablePos(*pit, rit);
1531 LyXLayout_ptr const & layout = pit->layout();
1533 bool left_side = false;
1535 pos_type body_pos = pit->beginningOfBody();
1536 double last_tmpx = tmpx;
1539 (body_pos - 1 > last ||
1540 !pit->isLineSeparator(body_pos - 1)))
1543 // check for empty row
1549 while (vc <= last && tmpx <= x) {
1552 if (body_pos > 0 && c == body_pos - 1) {
1553 tmpx += fill_label_hfill +
1554 font_metrics::width(layout->labelsep, getLabelFont(pit));
1555 if (pit->isLineSeparator(body_pos - 1))
1556 tmpx -= singleWidth(pit, body_pos - 1);
1559 if (hfillExpansion(*pit, rit, c)) {
1560 tmpx += singleWidth(pit, c);
1564 tmpx += fill_label_hfill;
1565 } else if (pit->isSeparator(c)) {
1566 tmpx += singleWidth(pit, c);
1568 tmpx += fill_separator;
1570 tmpx += singleWidth(pit, c);
1575 if ((tmpx + last_tmpx) / 2 > x) {
1580 if (vc > last + 1) // This shouldn't happen.
1584 // This (rtl_support test) is not needed, but gives
1585 // some speedup if rtl_support == false
1586 bool const lastrow = lyxrc.rtl_support
1587 && boost::next(rit) == pit->rows.end();
1589 // If lastrow is false, we don't need to compute
1590 // the value of rtl.
1591 bool const rtl = (lastrow)
1592 ? pit->isRightToLeftPar(bv()->buffer()->params)
1595 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1596 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1598 else if (vc == rit->pos()) {
1600 if (bidi_level(c) % 2 == 1)
1603 c = vis2log(vc - 1);
1604 bool const rtl = (bidi_level(c) % 2 == 1);
1605 if (left_side == rtl) {
1607 boundary = isBoundary(bv()->buffer(), *pit, c);
1611 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1612 if (bidi_level(last) % 2 == 0)
1613 tmpx -= singleWidth(pit, last);
1615 tmpx += singleWidth(pit, last);
1625 void LyXText::setCursorFromCoordinates(int x, int y)
1627 LyXCursor old_cursor = cursor;
1628 setCursorFromCoordinates(cursor, x, y);
1630 deleteEmptyParagraphMechanism(old_cursor);
1634 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1636 // Get the row first.
1637 ParagraphList::iterator pit;
1638 RowList::iterator rit = getRowNearY(y, pit);
1642 pos_type const column = getColumnNearX(pit, rit, x, bound);
1644 cur.pos(rit->pos() + column);
1646 cur.y(y + rit->baseline());
1651 cur.boundary(bound);
1655 void LyXText::cursorLeft(bool internal)
1657 if (cursor.pos() > 0) {
1658 bool boundary = cursor.boundary();
1659 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1660 if (!internal && !boundary &&
1661 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1662 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1663 } else if (cursor.par() != ownerParagraphs().begin()) {
1664 // steps into the paragraph above
1665 ParagraphList::iterator pit = boost::prior(cursor.par());
1666 setCursor(pit, pit->size());
1671 void LyXText::cursorRight(bool internal)
1673 bool const at_end = (cursor.pos() == cursor.par()->size());
1674 bool const at_newline = !at_end &&
1675 cursor.par()->isNewline(cursor.pos());
1677 if (!internal && cursor.boundary() && !at_newline)
1678 setCursor(cursor.par(), cursor.pos(), true, false);
1680 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1682 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1683 setCursor(cursor.par(), cursor.pos(), true, true);
1684 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1685 setCursor(boost::next(cursor.par()), 0);
1689 void LyXText::cursorUp(bool selecting)
1692 int x = cursor.x_fix();
1693 int y = cursor.y() - cursorRow()->baseline() - 1;
1694 setCursorFromCoordinates(x, y);
1697 int y1 = cursor.iy() - topy;
1700 InsetOld * inset_hit = checkInsetHit(x, y1);
1701 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1702 inset_hit->localDispatch(
1703 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1707 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1708 cursorRow()->baseline() << endl;
1709 setCursorFromCoordinates(cursor.x_fix(),
1710 cursor.y() - cursorRow()->baseline() - 1);
1715 void LyXText::cursorDown(bool selecting)
1718 int x = cursor.x_fix();
1719 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1720 setCursorFromCoordinates(x, y);
1721 if (!selecting && cursorRow() == cursorIRow()) {
1723 int y1 = cursor.iy() - topy;
1726 InsetOld * inset_hit = checkInsetHit(x, y1);
1727 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1728 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1729 inset_hit->localDispatch(cmd);
1733 setCursorFromCoordinates(cursor.x_fix(),
1734 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1739 void LyXText::cursorUpParagraph()
1741 if (cursor.pos() > 0)
1742 setCursor(cursor.par(), 0);
1743 else if (cursor.par() != ownerParagraphs().begin())
1744 setCursor(boost::prior(cursor.par()), 0);
1748 void LyXText::cursorDownParagraph()
1750 ParagraphList::iterator par = cursor.par();
1751 ParagraphList::iterator next_par = boost::next(par);
1753 if (next_par != ownerParagraphs().end())
1754 setCursor(next_par, 0);
1756 setCursor(par, par->size());
1760 // fix the cursor `cur' after a characters has been deleted at `where'
1761 // position. Called by deleteEmptyParagraphMechanism
1762 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1764 // if cursor is not in the paragraph where the delete occured,
1766 if (cur.par() != where.par())
1769 // if cursor position is after the place where the delete occured,
1771 if (cur.pos() > where.pos())
1772 cur.pos(cur.pos()-1);
1774 // check also if we don't want to set the cursor on a spot behind the
1775 // pagragraph because we erased the last character.
1776 if (cur.pos() > cur.par()->size())
1777 cur.pos(cur.par()->size());
1779 // recompute row et al. for this cursor
1780 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1784 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1786 // Would be wrong to delete anything if we have a selection.
1787 if (selection.set())
1790 // We allow all kinds of "mumbo-jumbo" when freespacing.
1791 if (old_cursor.par()->isFreeSpacing())
1794 /* Ok I'll put some comments here about what is missing.
1795 I have fixed BackSpace (and thus Delete) to not delete
1796 double-spaces automagically. I have also changed Cut,
1797 Copy and Paste to hopefully do some sensible things.
1798 There are still some small problems that can lead to
1799 double spaces stored in the document file or space at
1800 the beginning of paragraphs. This happens if you have
1801 the cursor between to spaces and then save. Or if you
1802 cut and paste and the selection have a space at the
1803 beginning and then save right after the paste. I am
1804 sure none of these are very hard to fix, but I will
1805 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1806 that I can get some feedback. (Lgb)
1809 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1810 // delete the LineSeparator.
1813 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1814 // delete the LineSeparator.
1817 // If the pos around the old_cursor were spaces, delete one of them.
1818 if (old_cursor.par() != cursor.par()
1819 || old_cursor.pos() != cursor.pos()) {
1821 // Only if the cursor has really moved
1822 if (old_cursor.pos() > 0
1823 && old_cursor.pos() < old_cursor.par()->size()
1824 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1825 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1826 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1827 redoParagraph(old_cursor.par());
1831 #ifdef WITH_WARNINGS
1832 #warning This will not work anymore when we have multiple views of the same buffer
1833 // In this case, we will have to correct also the cursors held by
1834 // other bufferviews. It will probably be easier to do that in a more
1835 // automated way in LyXCursor code. (JMarc 26/09/2001)
1837 // correct all cursors held by the LyXText
1838 fixCursorAfterDelete(cursor, old_cursor);
1839 fixCursorAfterDelete(selection.cursor, old_cursor);
1840 fixCursorAfterDelete(selection.start, old_cursor);
1841 fixCursorAfterDelete(selection.end, old_cursor);
1846 // don't delete anything if this is the ONLY paragraph!
1847 if (ownerParagraphs().size() == 1)
1850 // Do not delete empty paragraphs with keepempty set.
1851 if (old_cursor.par()->allowEmpty())
1854 // only do our magic if we changed paragraph
1855 if (old_cursor.par() == cursor.par())
1858 // record if we have deleted a paragraph
1859 // we can't possibly have deleted a paragraph before this point
1860 bool deleted = false;
1862 if (old_cursor.par()->empty() ||
1863 (old_cursor.par()->size() == 1 &&
1864 old_cursor.par()->isLineSeparator(0))) {
1865 // ok, we will delete something
1866 LyXCursor tmpcursor;
1870 bool selection_position_was_oldcursor_position = (
1871 selection.cursor.par() == old_cursor.par()
1872 && selection.cursor.pos() == old_cursor.pos());
1875 cursor = old_cursor; // that undo can restore the right cursor position
1877 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1878 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1881 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1885 ownerParagraphs().erase(old_cursor.par());
1889 setCursorIntern(cursor.par(), cursor.pos());
1891 if (selection_position_was_oldcursor_position) {
1892 // correct selection
1893 selection.cursor = cursor;
1897 if (old_cursor.par()->stripLeadingSpaces()) {
1898 redoParagraph(old_cursor.par());
1900 setCursorIntern(cursor.par(), cursor.pos());
1901 selection.cursor = cursor;
1908 ParagraphList & LyXText::ownerParagraphs() const
1914 bool LyXText::isInInset() const
1916 // Sub-level has non-null bv owner and non-null inset owner.
1917 return inset_owner != 0;
1921 int defaultRowHeight()
1923 LyXFont const font(LyXFont::ALL_SANE);
1924 return int(font_metrics::maxAscent(font)
1925 + font_metrics::maxDescent(font) * 1.5);