3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
30 #include "CutAndPaste.h"
32 #include "errorlist.h"
34 #include "FloatList.h"
35 #include "funcrequest.h"
40 #include "lyxrow_funcs.h"
41 #include "metricsinfo.h"
42 #include "paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
45 #include "undo_funcs.h"
48 #include "frontends/font_metrics.h"
49 #include "frontends/LyXView.h"
51 #include "insets/insetbibitem.h"
52 #include "insets/insetenv.h"
53 #include "insets/insetfloat.h"
54 #include "insets/insetwrap.h"
56 #include "support/lstrings.h"
57 #include "support/textutils.h"
58 #include "support/tostr.h"
59 #include "support/std_sstream.h"
61 #include <boost/tuple/tuple.hpp>
64 using lyx::support::bformat;
67 using std::ostringstream;
70 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
71 ParagraphList & paragraphs)
72 : height(0), width(0), anchor_y_(0),
73 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
74 in_inset_(ininset), paragraphs_(paragraphs)
79 void LyXText::init(BufferView * bview)
83 ParagraphList::iterator const beg = ownerParagraphs().begin();
84 ParagraphList::iterator const end = ownerParagraphs().end();
85 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
93 current_font = getFont(beg, 0);
95 redoParagraphs(beg, end);
96 setCursorIntern(beg, 0);
97 selection.cursor = cursor;
103 // Gets the fully instantiated font at a given position in a paragraph
104 // Basically the same routine as Paragraph::getFont() in paragraph.C.
105 // The difference is that this one is used for displaying, and thus we
106 // are allowed to make cosmetic improvements. For instance make footnotes
108 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
110 BOOST_ASSERT(pos >= 0);
112 LyXLayout_ptr const & layout = pit->layout();
114 BufferParams const & params = bv()->buffer()->params();
116 // We specialize the 95% common case:
117 if (!pit->getDepth()) {
118 if (layout->labeltype == LABEL_MANUAL
119 && pos < pit->beginningOfBody()) {
121 LyXFont f = pit->getFontSettings(params, pos);
123 pit->inInset()->getDrawFont(f);
124 return f.realize(layout->reslabelfont);
126 LyXFont f = pit->getFontSettings(params, pos);
128 pit->inInset()->getDrawFont(f);
129 return f.realize(layout->resfont);
133 // The uncommon case need not be optimized as much
137 if (pos < pit->beginningOfBody()) {
139 layoutfont = layout->labelfont;
142 layoutfont = layout->font;
145 LyXFont tmpfont = pit->getFontSettings(params, pos);
146 tmpfont.realize(layoutfont);
149 pit->inInset()->getDrawFont(tmpfont);
151 // Realize with the fonts of lesser depth.
152 tmpfont.realize(outerFont(pit, ownerParagraphs()));
153 tmpfont.realize(defaultfont_);
159 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
161 LyXLayout_ptr const & layout = pit->layout();
163 if (!pit->getDepth())
164 return layout->resfont;
166 LyXFont font = layout->font;
167 // Realize with the fonts of lesser depth.
168 font.realize(outerFont(pit, ownerParagraphs()));
169 font.realize(defaultfont_);
175 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
177 LyXLayout_ptr const & layout = pit->layout();
179 if (!pit->getDepth())
180 return layout->reslabelfont;
182 LyXFont font = layout->labelfont;
183 // Realize with the fonts of lesser depth.
184 font.realize(outerFont(pit, ownerParagraphs()));
185 font.realize(defaultfont_);
191 void LyXText::setCharFont(ParagraphList::iterator pit,
192 pos_type pos, LyXFont const & fnt,
195 BufferParams const & params = bv()->buffer()->params();
196 LyXFont font = getFont(pit, pos);
197 font.update(fnt, params.language, toggleall);
198 // Let the insets convert their font
199 if (pit->isInset(pos)) {
200 InsetOld * inset = pit->getInset(pos);
201 if (isEditableInset(inset)) {
202 static_cast<UpdatableInset *>(inset)
203 ->setFont(bv(), fnt, toggleall, true);
207 // Plug through to version below:
208 setCharFont(pit, pos, font);
212 void LyXText::setCharFont(
213 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
216 LyXLayout_ptr const & layout = pit->layout();
218 // Get concrete layout font to reduce against
221 if (pos < pit->beginningOfBody())
222 layoutfont = layout->labelfont;
224 layoutfont = layout->font;
226 // Realize against environment font information
227 if (pit->getDepth()) {
228 ParagraphList::iterator tp = pit;
229 while (!layoutfont.resolved() &&
230 tp != ownerParagraphs().end() &&
232 tp = outerHook(tp, ownerParagraphs());
233 if (tp != ownerParagraphs().end())
234 layoutfont.realize(tp->layout()->font);
238 layoutfont.realize(defaultfont_);
240 // Now, reduce font against full layout font
241 font.reduce(layoutfont);
243 pit->setFont(pos, font);
247 InsetOld * LyXText::getInset() const
249 ParagraphList::iterator pit = cursor.par();
250 pos_type const pos = cursor.pos();
252 if (pos < pit->size() && pit->isInset(pos)) {
253 return pit->getInset(pos);
259 void LyXText::toggleInset()
261 InsetOld * inset = getInset();
262 // is there an editable inset at cursor position?
263 if (!isEditableInset(inset)) {
264 // No, try to see if we are inside a collapsable inset
265 if (inset_owner && inset_owner->owner()
266 && inset_owner->owner()->isOpen()) {
267 bv()->unlockInset(inset_owner->owner());
268 inset_owner->owner()->close(bv());
269 bv()->getLyXText()->cursorRight(bv());
273 //bv()->owner()->message(inset->editMessage());
275 // do we want to keep this?? (JMarc)
276 if (!isHighlyEditableInset(inset))
277 recordUndo(bv(), Undo::ATOMIC);
284 bv()->updateInset(inset);
288 /* used in setlayout */
289 // Asger is not sure we want to do this...
290 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
293 LyXLayout_ptr const & layout = par.layout();
294 pos_type const psize = par.size();
297 for (pos_type pos = 0; pos < psize; ++pos) {
298 if (pos < par.beginningOfBody())
299 layoutfont = layout->labelfont;
301 layoutfont = layout->font;
303 LyXFont tmpfont = par.getFontSettings(params, pos);
304 tmpfont.reduce(layoutfont);
305 par.setFont(pos, tmpfont);
310 ParagraphList::iterator
311 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
312 LyXCursor & send_cur,
313 string const & layout)
315 ParagraphList::iterator endpit = boost::next(send_cur.par());
316 ParagraphList::iterator undoendpit = endpit;
317 ParagraphList::iterator pars_end = ownerParagraphs().end();
319 if (endpit != pars_end && endpit->getDepth()) {
320 while (endpit != pars_end && endpit->getDepth()) {
324 } else if (endpit != pars_end) {
325 // because of parindents etc.
329 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
331 // ok we have a selection. This is always between sstart_cur
332 // and sel_end cursor
334 ParagraphList::iterator pit = sstart_cur.par();
335 ParagraphList::iterator epit = boost::next(send_cur.par());
337 BufferParams const & bufparams = bv()->buffer()->params();
338 LyXLayout_ptr const & lyxlayout =
339 bufparams.getLyXTextClass()[layout];
342 pit->applyLayout(lyxlayout);
343 makeFontEntriesLayoutSpecific(bufparams, *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 //BOOST_ASSERT(mi.base.textwidth);
632 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
635 dim.asc = firstRow()->ascent_of_text();
636 dim.des = height - dim.asc;
637 dim.wid = std::max(mi.base.textwidth, int(width));
641 // important for the screen
644 // the cursor set functions have a special mechanism. When they
645 // realize, that you left an empty paragraph, they will delete it.
646 // They also delete the corresponding row
648 // need the selection cursor:
649 void LyXText::setSelection()
651 TextCursor::setSelection();
656 void LyXText::clearSelection()
658 TextCursor::clearSelection();
660 // reset this in the bv_owner!
661 if (bv_owner && bv_owner->text)
662 bv_owner->text->xsel_cache.set(false);
666 void LyXText::cursorHome()
668 setCursor(cursor.par(), cursorRow()->pos());
672 void LyXText::cursorEnd()
674 setCursor(cursor.par(), cursorRow()->end() - 1);
678 void LyXText::cursorTop()
680 setCursor(ownerParagraphs().begin(), 0);
684 void LyXText::cursorBottom()
686 ParagraphList::iterator lastpit =
687 boost::prior(ownerParagraphs().end());
688 setCursor(lastpit, lastpit->size());
692 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
694 // If the mask is completely neutral, tell user
695 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
696 // Could only happen with user style
697 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
701 // Try implicit word selection
702 // If there is a change in the language the implicit word selection
704 LyXCursor resetCursor = cursor;
705 bool implicitSelection = (font.language() == ignore_language
706 && font.number() == LyXFont::IGNORE)
707 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
710 setFont(font, toggleall);
712 // Implicit selections are cleared afterwards
713 //and cursor is set to the original position.
714 if (implicitSelection) {
716 cursor = resetCursor;
717 setCursor(cursor.par(), cursor.pos());
718 selection.cursor = cursor;
723 string LyXText::getStringToIndex()
725 // Try implicit word selection
726 // If there is a change in the language the implicit word selection
728 LyXCursor const reset_cursor = cursor;
729 bool const implicitSelection =
730 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
733 if (!selection.set())
734 bv()->owner()->message(_("Nothing to index!"));
735 else if (selection.start.par() != selection.end.par())
736 bv()->owner()->message(_("Cannot index more than one paragraph!"));
738 idxstring = selectionAsString(*bv()->buffer(), false);
740 // Reset cursors to their original position.
741 cursor = reset_cursor;
742 setCursor(cursor.par(), cursor.pos());
743 selection.cursor = cursor;
745 // Clear the implicit selection.
746 if (implicitSelection)
753 // the DTP switches for paragraphs. LyX will store them in the first
754 // physical paragraph. When a paragraph is broken, the top settings rest,
755 // the bottom settings are given to the new one. So I can make sure,
756 // they do not duplicate themself and you cannnot make dirty things with
759 void LyXText::setParagraph(bool line_top, bool line_bottom,
760 bool pagebreak_top, bool pagebreak_bottom,
761 VSpace const & space_top,
762 VSpace const & space_bottom,
763 Spacing const & spacing,
765 string const & labelwidthstring,
768 LyXCursor tmpcursor = cursor;
769 if (!selection.set()) {
770 selection.start = cursor;
771 selection.end = cursor;
774 // make sure that the depth behind the selection are restored, too
775 ParagraphList::iterator endpit = boost::next(selection.end.par());
776 ParagraphList::iterator undoendpit = endpit;
777 ParagraphList::iterator pars_end = ownerParagraphs().end();
779 if (endpit != pars_end && endpit->getDepth()) {
780 while (endpit != pars_end && endpit->getDepth()) {
784 } else if (endpit != pars_end) {
785 // because of parindents etc.
789 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
790 boost::prior(undoendpit));
793 ParagraphList::iterator tmppit = selection.end.par();
795 while (tmppit != boost::prior(selection.start.par())) {
796 setCursor(tmppit, 0);
798 ParagraphList::iterator pit = cursor.par();
799 ParagraphParameters & params = pit->params();
801 params.lineTop(line_top);
802 params.lineBottom(line_bottom);
803 params.pagebreakTop(pagebreak_top);
804 params.pagebreakBottom(pagebreak_bottom);
805 params.spaceTop(space_top);
806 params.spaceBottom(space_bottom);
807 params.spacing(spacing);
808 // does the layout allow the new alignment?
809 LyXLayout_ptr const & layout = pit->layout();
811 if (align == LYX_ALIGN_LAYOUT)
812 align = layout->align;
813 if (align & layout->alignpossible) {
814 if (align == layout->align)
815 params.align(LYX_ALIGN_LAYOUT);
819 pit->setLabelWidthString(labelwidthstring);
820 params.noindent(noindent);
821 tmppit = boost::prior(pit);
824 redoParagraphs(selection.start.par(), endpit);
827 setCursor(selection.start.par(), selection.start.pos());
828 selection.cursor = cursor;
829 setCursor(selection.end.par(), selection.end.pos());
831 setCursor(tmpcursor.par(), tmpcursor.pos());
833 bv()->updateInset(inset_owner);
839 string expandLabel(LyXTextClass const & textclass,
840 LyXLayout_ptr const & layout, bool appendix)
842 string fmt = appendix ?
843 layout->labelstring_appendix() : layout->labelstring();
845 // handle 'inherited level parts' in 'fmt',
846 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
847 size_t const i = fmt.find('@', 0);
848 if (i != string::npos) {
849 size_t const j = fmt.find('@', i + 1);
850 if (j != string::npos) {
851 string parent(fmt, i + 1, j - i - 1);
852 string label = expandLabel(textclass, textclass[parent], appendix);
853 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
857 return textclass.counters().counterLabel(fmt);
863 // set the counter of a paragraph. This includes the labels
864 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
866 BufferParams const & bufparams = buf.params();
867 LyXTextClass const & textclass = bufparams.getLyXTextClass();
868 LyXLayout_ptr const & layout = pit->layout();
870 if (pit != ownerParagraphs().begin()) {
871 pit->params().appendix(boost::prior(pit)->params().appendix());
872 if (!pit->params().appendix() &&
873 pit->params().startOfAppendix()) {
874 pit->params().appendix(true);
875 textclass.counters().reset();
877 pit->enumdepth = boost::prior(pit)->enumdepth;
878 pit->itemdepth = boost::prior(pit)->itemdepth;
880 pit->params().appendix(pit->params().startOfAppendix());
885 // Maybe we have to increment the enumeration depth.
886 // Bibliographies can't have their depth changed ie. they
887 // are always of depth 0
888 if (pit != ownerParagraphs().begin()
889 && boost::prior(pit)->getDepth() < pit->getDepth()
890 && boost::prior(pit)->layout()->labeltype == LABEL_ENUMERATE
891 && pit->enumdepth < 3
892 && layout->labeltype != LABEL_BIBLIO) {
896 // Maybe we have to increment the enumeration depth.
897 // Bibliographies can't have their depth changed ie. they
898 // are always of depth 0
899 if (pit != ownerParagraphs().begin()
900 && boost::prior(pit)->getDepth() < pit->getDepth()
901 && boost::prior(pit)->layout()->labeltype == LABEL_ITEMIZE
902 && pit->itemdepth < 3
903 && layout->labeltype != LABEL_BIBLIO) {
907 // Maybe we have to decrement the enumeration depth, see note above
908 if (pit != ownerParagraphs().begin()
909 && boost::prior(pit)->getDepth() > pit->getDepth()
910 && layout->labeltype != LABEL_BIBLIO) {
911 pit->enumdepth = depthHook(pit, ownerParagraphs(),
912 pit->getDepth())->enumdepth;
915 // erase what was there before
916 pit->params().labelString(string());
918 if (layout->margintype == MARGIN_MANUAL) {
919 if (pit->params().labelWidthString().empty())
920 pit->setLabelWidthString(layout->labelstring());
922 pit->setLabelWidthString(string());
925 // is it a layout that has an automatic label?
926 if (layout->labeltype == LABEL_COUNTER) {
927 BufferParams const & bufparams = buf.params();
928 LyXTextClass const & textclass = bufparams.getLyXTextClass();
929 textclass.counters().step(layout->counter);
930 string label = expandLabel(textclass, layout, pit->params().appendix());
931 pit->params().labelString(label);
932 textclass.counters().reset("enum");
933 } else if (layout->labeltype == LABEL_ITEMIZE) {
934 // At some point of time we should do something more clever here,
936 // pit->params().labelString(
937 // bufparams.user_defined_bullet(pit->itemdepth).getText());
938 // for now, use a static label
939 pit->params().labelString("*");
940 textclass.counters().reset("enum");
941 } else if (layout->labeltype == LABEL_ENUMERATE) {
943 // Yes I know this is a really, really! bad solution
945 string enumcounter = "enum";
947 switch (pit->enumdepth) {
959 // not a valid enumdepth...
963 textclass.counters().step(enumcounter);
965 pit->params().labelString(textclass.counters().enumLabel(enumcounter));
966 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
967 textclass.counters().step("bibitem");
968 int number = textclass.counters().value("bibitem");
969 if (pit->bibitem()) {
970 pit->bibitem()->setCounter(number);
971 pit->params().labelString(layout->labelstring());
973 // In biblio should't be following counters but...
975 string s = buf.B_(layout->labelstring());
978 if (layout->labeltype == LABEL_SENSITIVE) {
979 ParagraphList::iterator end = ownerParagraphs().end();
980 ParagraphList::iterator tmppit = pit;
983 while (tmppit != end && tmppit->inInset()
984 // the single '=' is intended below
985 && (in = tmppit->inInset()->owner()))
987 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
988 in->lyxCode() == InsetOld::WRAP_CODE) {
992 Paragraph const * owner = &ownerPar(buf, in);
993 tmppit = ownerParagraphs().begin();
994 for ( ; tmppit != end; ++tmppit)
995 if (&*tmppit == owner)
1003 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1004 type = static_cast<InsetFloat*>(in)->params().type;
1005 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1006 type = static_cast<InsetWrap*>(in)->params().type;
1008 BOOST_ASSERT(false);
1010 Floating const & fl = textclass.floats().getType(type);
1012 textclass.counters().step(fl.type());
1014 // Doesn't work... yet.
1015 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1017 // par->SetLayout(0);
1018 // s = layout->labelstring;
1019 s = _("Senseless: ");
1022 pit->params().labelString(s);
1024 // reset the enumeration counter. They are always reset
1025 // when there is any other layout between
1026 // Just fall-through between the cases so that all
1027 // enum counters deeper than enumdepth is also reset.
1028 switch (pit->enumdepth) {
1030 textclass.counters().reset("enumi");
1032 textclass.counters().reset("enumii");
1034 textclass.counters().reset("enumiii");
1036 textclass.counters().reset("enumiv");
1042 // Updates all counters. Paragraphs with changed label string will be rebroken
1043 void LyXText::updateCounters()
1046 bv()->buffer()->params().getLyXTextClass().counters().reset();
1048 ParagraphList::iterator beg = ownerParagraphs().begin();
1049 ParagraphList::iterator end = ownerParagraphs().end();
1050 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1051 string const oldLabel = pit->params().labelString();
1053 size_t maxdepth = 0;
1055 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1057 if (pit->params().depth() > maxdepth)
1058 pit->params().depth(maxdepth);
1060 // setCounter can potentially change the labelString.
1061 setCounter(*bv()->buffer(), pit);
1063 string const & newLabel = pit->params().labelString();
1065 if (oldLabel != newLabel)
1071 void LyXText::insertInset(InsetOld * inset)
1073 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1075 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1077 cursor.par()->insertInset(cursor.pos(), inset);
1078 // Just to rebreak and refresh correctly.
1079 // The character will not be inserted a second time
1080 insertChar(Paragraph::META_INSET);
1081 // If we enter a highly editable inset the cursor should be before
1082 // the inset. After an Undo LyX tries to call inset->edit(...)
1083 // and fails if the cursor is behind the inset and getInset
1084 // does not return the inset!
1085 if (isHighlyEditableInset(inset))
1091 void LyXText::cutSelection(bool doclear, bool realcut)
1093 // Stuff what we got on the clipboard. Even if there is no selection.
1095 // There is a problem with having the stuffing here in that the
1096 // larger the selection the slower LyX will get. This can be
1097 // solved by running the line below only when the selection has
1098 // finished. The solution used currently just works, to make it
1099 // faster we need to be more clever and probably also have more
1100 // calls to stuffClipboard. (Lgb)
1101 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1103 // This doesn't make sense, if there is no selection
1104 if (!selection.set())
1107 // OK, we have a selection. This is always between selection.start
1108 // and selection.end
1110 // make sure that the depth behind the selection are restored, too
1111 ParagraphList::iterator endpit = boost::next(selection.end.par());
1112 ParagraphList::iterator undoendpit = endpit;
1113 ParagraphList::iterator pars_end = ownerParagraphs().end();
1115 if (endpit != pars_end && endpit->getDepth()) {
1116 while (endpit != pars_end && endpit->getDepth()) {
1118 undoendpit = endpit;
1120 } else if (endpit != pars_end) {
1121 // because of parindents etc.
1125 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1126 boost::prior(undoendpit));
1128 endpit = selection.end.par();
1129 int endpos = selection.end.pos();
1131 BufferParams const & bufparams = bv()->buffer()->params();
1132 boost::tie(endpit, endpos) = realcut ?
1133 CutAndPaste::cutSelection(bufparams,
1135 selection.start.par(), endpit,
1136 selection.start.pos(), endpos,
1137 bufparams.textclass,
1139 : CutAndPaste::eraseSelection(bufparams,
1141 selection.start.par(), endpit,
1142 selection.start.pos(), endpos,
1144 // sometimes necessary
1146 selection.start.par()->stripLeadingSpaces();
1148 redoParagraphs(selection.start.par(), boost::next(endpit));
1149 // cutSelection can invalidate the cursor so we need to set
1151 // we prefer the end for when tracking changes
1155 // need a valid cursor. (Lgb)
1158 setCursor(cursor.par(), cursor.pos());
1159 selection.cursor = cursor;
1164 void LyXText::copySelection()
1166 // stuff the selection onto the X clipboard, from an explicit copy request
1167 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1169 // this doesnt make sense, if there is no selection
1170 if (!selection.set())
1173 // ok we have a selection. This is always between selection.start
1174 // and sel_end cursor
1176 // copy behind a space if there is one
1177 while (selection.start.par()->size() > selection.start.pos()
1178 && selection.start.par()->isLineSeparator(selection.start.pos())
1179 && (selection.start.par() != selection.end.par()
1180 || selection.start.pos() < selection.end.pos()))
1181 selection.start.pos(selection.start.pos() + 1);
1183 CutAndPaste::copySelection(selection.start.par(),
1184 selection.end.par(),
1185 selection.start.pos(), selection.end.pos(),
1186 bv()->buffer()->params().textclass);
1190 void LyXText::pasteSelection(size_t sel_index)
1192 // this does not make sense, if there is nothing to paste
1193 if (!CutAndPaste::checkPastePossible())
1196 recordUndo(bv(), Undo::INSERT, cursor.par());
1198 ParagraphList::iterator endpit;
1203 boost::tie(ppp, endpit) =
1204 CutAndPaste::pasteSelection(*bv()->buffer(),
1206 cursor.par(), cursor.pos(),
1207 bv()->buffer()->params().textclass,
1209 bufferErrors(*bv()->buffer(), el);
1210 bv()->showErrorList(_("Paste"));
1212 redoParagraphs(cursor.par(), endpit);
1214 setCursor(cursor.par(), cursor.pos());
1217 selection.cursor = cursor;
1218 setCursor(ppp.first, ppp.second);
1224 void LyXText::setSelectionRange(lyx::pos_type length)
1229 selection.cursor = cursor;
1236 // simple replacing. The font of the first selected character is used
1237 void LyXText::replaceSelectionWithString(string const & str)
1239 recordUndo(bv(), Undo::ATOMIC);
1242 if (!selection.set()) { // create a dummy selection
1243 selection.end = cursor;
1244 selection.start = cursor;
1247 // Get font setting before we cut
1248 pos_type pos = selection.end.pos();
1249 LyXFont const font = selection.start.par()
1250 ->getFontSettings(bv()->buffer()->params(),
1251 selection.start.pos());
1253 // Insert the new string
1254 string::const_iterator cit = str.begin();
1255 string::const_iterator end = str.end();
1256 for (; cit != end; ++cit) {
1257 selection.end.par()->insertChar(pos, (*cit), font);
1261 // Cut the selection
1262 cutSelection(true, false);
1268 // needed to insert the selection
1269 void LyXText::insertStringAsLines(string const & str)
1271 ParagraphList::iterator pit = cursor.par();
1272 pos_type pos = cursor.pos();
1273 ParagraphList::iterator endpit = boost::next(cursor.par());
1275 recordUndo(bv(), Undo::ATOMIC);
1277 // only to be sure, should not be neccessary
1280 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1282 redoParagraphs(cursor.par(), endpit);
1283 setCursor(cursor.par(), cursor.pos());
1284 selection.cursor = cursor;
1285 setCursor(pit, pos);
1290 // turns double-CR to single CR, others where converted into one
1291 // blank. Then InsertStringAsLines is called
1292 void LyXText::insertStringAsParagraphs(string const & str)
1294 string linestr(str);
1295 bool newline_inserted = false;
1296 string::size_type const siz = linestr.length();
1298 for (string::size_type i = 0; i < siz; ++i) {
1299 if (linestr[i] == '\n') {
1300 if (newline_inserted) {
1301 // we know that \r will be ignored by
1302 // InsertStringA. Of course, it is a dirty
1303 // trick, but it works...
1304 linestr[i - 1] = '\r';
1308 newline_inserted = true;
1310 } else if (IsPrintable(linestr[i])) {
1311 newline_inserted = false;
1314 insertStringAsLines(linestr);
1318 bool LyXText::setCursor(ParagraphList::iterator pit,
1320 bool setfont, bool boundary)
1322 LyXCursor old_cursor = cursor;
1323 setCursorIntern(pit, pos, setfont, boundary);
1324 return deleteEmptyParagraphMechanism(old_cursor);
1328 void LyXText::redoCursor()
1330 #warning maybe the same for selections?
1331 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1335 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1336 pos_type pos, bool boundary)
1338 BOOST_ASSERT(pit != ownerParagraphs().end());
1342 cur.boundary(boundary);
1346 // get the cursor y position in text
1348 RowList::iterator row = getRow(pit, pos);
1351 // y is now the beginning of the cursor row
1352 y += row->baseline();
1353 // y is now the cursor baseline
1356 pos_type last = lastPos(*pit, row);
1358 // None of these should happen, but we're scaredy-cats
1359 if (pos > pit->size()) {
1360 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1363 } else if (pos > last + 1) {
1364 lyxerr << "dont like 2 please report" << endl;
1365 // This shouldn't happen.
1368 } else if (pos < row->pos()) {
1369 lyxerr << "dont like 3 please report" << endl;
1374 // now get the cursors x position
1375 float x = getCursorX(pit, row, pos, last, boundary);
1381 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1382 pos_type pos, pos_type last, bool boundary) const
1384 pos_type cursor_vpos = 0;
1385 double x = rit->x();
1386 double fill_separator = rit->fill_separator();
1387 double fill_hfill = rit->fill_hfill();
1388 double fill_label_hfill = rit->fill_label_hfill();
1389 pos_type const rit_pos = rit->pos();
1392 cursor_vpos = rit_pos;
1393 else if (pos > last && !boundary)
1394 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1395 ? rit_pos : last + 1;
1396 else if (pos > rit_pos && (pos > last || boundary))
1397 // Place cursor after char at (logical) position pos - 1
1398 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1399 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1401 // Place cursor before char at (logical) position pos
1402 cursor_vpos = (bidi_level(pos) % 2 == 0)
1403 ? log2vis(pos) : log2vis(pos) + 1;
1405 pos_type body_pos = pit->beginningOfBody();
1407 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1410 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1411 pos_type pos = vis2log(vpos);
1412 if (body_pos > 0 && pos == body_pos - 1) {
1413 x += fill_label_hfill +
1414 font_metrics::width(
1415 pit->layout()->labelsep, getLabelFont(pit));
1416 if (pit->isLineSeparator(body_pos - 1))
1417 x -= singleWidth(pit, body_pos - 1);
1420 if (hfillExpansion(*pit, rit, pos)) {
1421 x += singleWidth(pit, pos);
1422 if (pos >= body_pos)
1425 x += fill_label_hfill;
1426 } else if (pit->isSeparator(pos)) {
1427 x += singleWidth(pit, pos);
1428 if (pos >= body_pos)
1429 x += fill_separator;
1431 x += singleWidth(pit, pos);
1437 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1438 pos_type pos, bool setfont, bool boundary)
1440 setCursor(cursor, pit, pos, boundary);
1446 void LyXText::setCurrentFont()
1448 pos_type pos = cursor.pos();
1449 ParagraphList::iterator pit = cursor.par();
1451 if (cursor.boundary() && pos > 0)
1455 if (pos == pit->size())
1457 else // potentional bug... BUG (Lgb)
1458 if (pit->isSeparator(pos)) {
1459 if (pos > cursorRow()->pos() &&
1460 bidi_level(pos) % 2 ==
1461 bidi_level(pos - 1) % 2)
1463 else if (pos + 1 < pit->size())
1468 BufferParams const & bufparams = bv()->buffer()->params();
1469 current_font = pit->getFontSettings(bufparams, pos);
1470 real_current_font = getFont(pit, pos);
1472 if (cursor.pos() == pit->size() &&
1473 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1474 !cursor.boundary()) {
1475 Language const * lang =
1476 pit->getParLanguage(bufparams);
1477 current_font.setLanguage(lang);
1478 current_font.setNumber(LyXFont::OFF);
1479 real_current_font.setLanguage(lang);
1480 real_current_font.setNumber(LyXFont::OFF);
1485 // returns the column near the specified x-coordinate of the row
1486 // x is set to the real beginning of this column
1487 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1488 RowList::iterator rit, int & x, bool & boundary) const
1490 double tmpx = rit->x();
1491 double fill_separator = rit->fill_separator();
1492 double fill_hfill = rit->fill_hfill();
1493 double fill_label_hfill = rit->fill_label_hfill();
1495 pos_type vc = rit->pos();
1496 pos_type last = lastPos(*pit, rit);
1498 LyXLayout_ptr const & layout = pit->layout();
1500 bool left_side = false;
1502 pos_type body_pos = pit->beginningOfBody();
1503 double last_tmpx = tmpx;
1506 (body_pos - 1 > last ||
1507 !pit->isLineSeparator(body_pos - 1)))
1510 // check for empty row
1516 while (vc <= last && tmpx <= x) {
1519 if (body_pos > 0 && c == body_pos - 1) {
1520 tmpx += fill_label_hfill +
1521 font_metrics::width(layout->labelsep, getLabelFont(pit));
1522 if (pit->isLineSeparator(body_pos - 1))
1523 tmpx -= singleWidth(pit, body_pos - 1);
1526 if (hfillExpansion(*pit, rit, c)) {
1527 tmpx += singleWidth(pit, c);
1531 tmpx += fill_label_hfill;
1532 } else if (pit->isSeparator(c)) {
1533 tmpx += singleWidth(pit, c);
1535 tmpx += fill_separator;
1537 tmpx += singleWidth(pit, c);
1542 if ((tmpx + last_tmpx) / 2 > x) {
1547 if (vc > last + 1) // This shouldn't happen.
1551 // This (rtl_support test) is not needed, but gives
1552 // some speedup if rtl_support == false
1553 bool const lastrow = lyxrc.rtl_support
1554 && boost::next(rit) == pit->rows.end();
1556 // If lastrow is false, we don't need to compute
1557 // the value of rtl.
1558 bool const rtl = (lastrow)
1559 ? pit->isRightToLeftPar(bv()->buffer()->params())
1562 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1563 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1565 else if (vc == rit->pos()) {
1567 if (bidi_level(c) % 2 == 1)
1570 c = vis2log(vc - 1);
1571 bool const rtl = (bidi_level(c) % 2 == 1);
1572 if (left_side == rtl) {
1574 boundary = isBoundary(*bv()->buffer(), *pit, c);
1578 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1579 if (bidi_level(last) % 2 == 0)
1580 tmpx -= singleWidth(pit, last);
1582 tmpx += singleWidth(pit, last);
1592 void LyXText::setCursorFromCoordinates(int x, int y)
1594 LyXCursor old_cursor = cursor;
1595 setCursorFromCoordinates(cursor, x, y);
1597 deleteEmptyParagraphMechanism(old_cursor);
1601 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1603 // Get the row first.
1604 ParagraphList::iterator pit;
1605 RowList::iterator rit = getRowNearY(y, pit);
1609 pos_type const column = getColumnNearX(pit, rit, x, bound);
1611 cur.pos(rit->pos() + column);
1613 cur.y(y + rit->baseline());
1615 cur.boundary(bound);
1619 void LyXText::cursorLeft(bool internal)
1621 if (cursor.pos() > 0) {
1622 bool boundary = cursor.boundary();
1623 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1624 if (!internal && !boundary &&
1625 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1626 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1627 } else if (cursor.par() != ownerParagraphs().begin()) {
1628 // steps into the paragraph above
1629 ParagraphList::iterator pit = boost::prior(cursor.par());
1630 setCursor(pit, pit->size());
1635 void LyXText::cursorRight(bool internal)
1637 bool const at_end = (cursor.pos() == cursor.par()->size());
1638 bool const at_newline = !at_end &&
1639 cursor.par()->isNewline(cursor.pos());
1641 if (!internal && cursor.boundary() && !at_newline)
1642 setCursor(cursor.par(), cursor.pos(), true, false);
1644 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1646 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1647 setCursor(cursor.par(), cursor.pos(), true, true);
1648 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1649 setCursor(boost::next(cursor.par()), 0);
1653 void LyXText::cursorUp(bool selecting)
1656 int x = cursor.x_fix();
1657 int y = cursor.y() - cursorRow()->baseline() - 1;
1658 setCursorFromCoordinates(x, y);
1660 int topy = bv_owner->top_y();
1661 int y1 = cursor.y() - topy;
1664 InsetOld * inset_hit = checkInsetHit(x, y1);
1665 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1666 inset_hit->localDispatch(
1667 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1671 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1672 cursorRow()->baseline() << endl;
1673 setCursorFromCoordinates(cursor.x_fix(),
1674 cursor.y() - cursorRow()->baseline() - 1);
1679 void LyXText::cursorDown(bool selecting)
1682 int x = cursor.x_fix();
1683 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1684 setCursorFromCoordinates(x, y);
1685 if (!selecting && cursorRow() == cursorIRow()) {
1686 int topy = bv_owner->top_y();
1687 int y1 = cursor.y() - topy;
1690 InsetOld * inset_hit = checkInsetHit(x, y1);
1691 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1692 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1693 inset_hit->localDispatch(cmd);
1697 setCursorFromCoordinates(cursor.x_fix(),
1698 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1703 void LyXText::cursorUpParagraph()
1705 if (cursor.pos() > 0)
1706 setCursor(cursor.par(), 0);
1707 else if (cursor.par() != ownerParagraphs().begin())
1708 setCursor(boost::prior(cursor.par()), 0);
1712 void LyXText::cursorDownParagraph()
1714 ParagraphList::iterator par = cursor.par();
1715 ParagraphList::iterator next_par = boost::next(par);
1717 if (next_par != ownerParagraphs().end())
1718 setCursor(next_par, 0);
1720 setCursor(par, par->size());
1724 // fix the cursor `cur' after a characters has been deleted at `where'
1725 // position. Called by deleteEmptyParagraphMechanism
1726 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1728 // if cursor is not in the paragraph where the delete occured,
1730 if (cur.par() != where.par())
1733 // if cursor position is after the place where the delete occured,
1735 if (cur.pos() > where.pos())
1736 cur.pos(cur.pos()-1);
1738 // check also if we don't want to set the cursor on a spot behind the
1739 // pagragraph because we erased the last character.
1740 if (cur.pos() > cur.par()->size())
1741 cur.pos(cur.par()->size());
1743 // recompute row et al. for this cursor
1744 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1748 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1750 // Would be wrong to delete anything if we have a selection.
1751 if (selection.set())
1754 // We allow all kinds of "mumbo-jumbo" when freespacing.
1755 if (old_cursor.par()->isFreeSpacing())
1758 /* Ok I'll put some comments here about what is missing.
1759 I have fixed BackSpace (and thus Delete) to not delete
1760 double-spaces automagically. I have also changed Cut,
1761 Copy and Paste to hopefully do some sensible things.
1762 There are still some small problems that can lead to
1763 double spaces stored in the document file or space at
1764 the beginning of paragraphs. This happens if you have
1765 the cursor between to spaces and then save. Or if you
1766 cut and paste and the selection have a space at the
1767 beginning and then save right after the paste. I am
1768 sure none of these are very hard to fix, but I will
1769 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1770 that I can get some feedback. (Lgb)
1773 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1774 // delete the LineSeparator.
1777 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1778 // delete the LineSeparator.
1781 // If the pos around the old_cursor were spaces, delete one of them.
1782 if (old_cursor.par() != cursor.par()
1783 || old_cursor.pos() != cursor.pos()) {
1785 // Only if the cursor has really moved
1786 if (old_cursor.pos() > 0
1787 && old_cursor.pos() < old_cursor.par()->size()
1788 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1789 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1790 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1791 redoParagraph(old_cursor.par());
1795 #ifdef WITH_WARNINGS
1796 #warning This will not work anymore when we have multiple views of the same buffer
1797 // In this case, we will have to correct also the cursors held by
1798 // other bufferviews. It will probably be easier to do that in a more
1799 // automated way in LyXCursor code. (JMarc 26/09/2001)
1801 // correct all cursors held by the LyXText
1802 fixCursorAfterDelete(cursor, old_cursor);
1803 fixCursorAfterDelete(selection.cursor, old_cursor);
1804 fixCursorAfterDelete(selection.start, old_cursor);
1805 fixCursorAfterDelete(selection.end, old_cursor);
1810 // don't delete anything if this is the ONLY paragraph!
1811 if (ownerParagraphs().size() == 1)
1814 // Do not delete empty paragraphs with keepempty set.
1815 if (old_cursor.par()->allowEmpty())
1818 // only do our magic if we changed paragraph
1819 if (old_cursor.par() == cursor.par())
1822 // record if we have deleted a paragraph
1823 // we can't possibly have deleted a paragraph before this point
1824 bool deleted = false;
1826 if (old_cursor.par()->empty() ||
1827 (old_cursor.par()->size() == 1 &&
1828 old_cursor.par()->isLineSeparator(0))) {
1829 // ok, we will delete something
1830 LyXCursor tmpcursor;
1834 bool selection_position_was_oldcursor_position = (
1835 selection.cursor.par() == old_cursor.par()
1836 && selection.cursor.pos() == old_cursor.pos());
1839 cursor = old_cursor; // that undo can restore the right cursor position
1841 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1842 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1845 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1849 ownerParagraphs().erase(old_cursor.par());
1853 setCursorIntern(cursor.par(), cursor.pos());
1855 if (selection_position_was_oldcursor_position) {
1856 // correct selection
1857 selection.cursor = cursor;
1861 if (old_cursor.par()->stripLeadingSpaces()) {
1862 redoParagraph(old_cursor.par());
1864 setCursorIntern(cursor.par(), cursor.pos());
1865 selection.cursor = cursor;
1872 ParagraphList & LyXText::ownerParagraphs() const
1878 bool LyXText::isInInset() const
1880 // Sub-level has non-null bv owner and non-null inset owner.
1881 return inset_owner != 0;
1885 int defaultRowHeight()
1887 LyXFont const font(LyXFont::ALL_SANE);
1888 return int(font_metrics::maxAscent(font)
1889 + font_metrics::maxDescent(font) * 1.5);