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"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
34 #include "errorlist.h"
36 #include "FloatList.h"
37 #include "funcrequest.h"
42 #include "lyxrow_funcs.h"
43 #include "paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
49 #include "frontends/font_metrics.h"
50 #include "frontends/LyXView.h"
52 #include "insets/insetbibitem.h"
53 #include "insets/insetenv.h"
54 #include "insets/insetfloat.h"
55 #include "insets/insetwrap.h"
57 #include "support/lstrings.h"
58 #include "support/textutils.h"
59 #include "support/tostr.h"
60 #include "support/std_sstream.h"
62 #include <boost/tuple/tuple.hpp>
65 using lyx::paroffset_type;
66 using lyx::support::bformat;
69 using std::ostringstream;
73 LyXText::LyXText(BufferView * bv, InsetText * inset, bool in_inset,
74 ParagraphList & paragraphs)
75 : height(0), width(0), textwidth_(bv ? bv->workWidth() : 100),
76 inset_owner(inset), bv_owner(bv),
77 in_inset_(in_inset), paragraphs_(¶graphs), xo_(0), yo_(0)
81 void LyXText::init(BufferView * bview)
85 ParagraphList::iterator const beg = ownerParagraphs().begin();
86 ParagraphList::iterator const end = ownerParagraphs().end();
87 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
93 current_font = getFont(beg, 0);
95 redoParagraphs(beg, end);
96 setCursorIntern(0, 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();
115 pos_type const body_pos = pit->beginOfBody();
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 LyXFont f = pit->getFontSettings(params, pos);
121 pit->inInset()->getDrawFont(f);
122 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
123 return f.realize(layout->reslabelfont);
125 return f.realize(layout->resfont);
128 // The uncommon case need not be optimized as much
131 layoutfont = layout->labelfont;
133 layoutfont = layout->font;
135 LyXFont font = pit->getFontSettings(params, pos);
136 font.realize(layoutfont);
139 pit->inInset()->getDrawFont(font);
141 // Realize with the fonts of lesser depth.
142 //font.realize(outerFont(pit, ownerParagraphs()));
143 font.realize(defaultfont_);
149 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
151 LyXLayout_ptr const & layout = pit->layout();
153 if (!pit->getDepth())
154 return layout->resfont;
156 LyXFont font = layout->font;
157 // Realize with the fonts of lesser depth.
158 //font.realize(outerFont(pit, ownerParagraphs()));
159 font.realize(defaultfont_);
165 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
167 LyXLayout_ptr const & layout = pit->layout();
169 if (!pit->getDepth())
170 return layout->reslabelfont;
172 LyXFont font = layout->labelfont;
173 // Realize with the fonts of lesser depth.
174 font.realize(outerFont(pit, ownerParagraphs()));
175 font.realize(defaultfont_);
181 void LyXText::setCharFont(ParagraphList::iterator pit,
182 pos_type pos, LyXFont const & fnt,
185 BufferParams const & params = bv()->buffer()->params();
186 LyXFont font = getFont(pit, pos);
187 font.update(fnt, params.language, toggleall);
188 // Let the insets convert their font
189 if (pit->isInset(pos)) {
190 InsetOld * inset = pit->getInset(pos);
191 if (isEditableInset(inset)) {
192 static_cast<UpdatableInset *>(inset)
193 ->setFont(bv(), fnt, toggleall, true);
197 // Plug through to version below:
198 setCharFont(pit, pos, font);
202 void LyXText::setCharFont(
203 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
206 LyXLayout_ptr const & layout = pit->layout();
208 // Get concrete layout font to reduce against
211 if (pos < pit->beginOfBody())
212 layoutfont = layout->labelfont;
214 layoutfont = layout->font;
216 // Realize against environment font information
217 if (pit->getDepth()) {
218 ParagraphList::iterator tp = pit;
219 while (!layoutfont.resolved() &&
220 tp != ownerParagraphs().end() &&
222 tp = outerHook(tp, ownerParagraphs());
223 if (tp != ownerParagraphs().end())
224 layoutfont.realize(tp->layout()->font);
228 layoutfont.realize(defaultfont_);
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 pit->setFont(pos, font);
237 InsetOld * LyXText::getInset() const
239 ParagraphList::iterator pit = cursorPar();
240 pos_type const pos = cursor.pos();
242 if (pos < pit->size() && pit->isInset(pos)) {
243 return pit->getInset(pos);
249 void LyXText::toggleInset()
251 InsetOld * inset = getInset();
252 // is there an editable inset at cursor position?
253 if (!isEditableInset(inset)) {
254 // No, try to see if we are inside a collapsable inset
255 if (inset_owner && inset_owner->owner()
256 && inset_owner->owner()->isOpen()) {
258 inset_owner->owner()->close();
259 bv()->getLyXText()->cursorRight(true);
260 bv()->updateParagraphDialog();
264 //bv()->owner()->message(inset->editMessage());
266 // do we want to keep this?? (JMarc)
267 if (!isHighlyEditableInset(inset))
268 recUndo(cursor.par());
278 // Asger is not sure we want to do this...
279 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
282 LyXLayout_ptr const & layout = par.layout();
283 pos_type const psize = par.size();
286 for (pos_type pos = 0; pos < psize; ++pos) {
287 if (pos < par.beginOfBody())
288 layoutfont = layout->labelfont;
290 layoutfont = layout->font;
292 LyXFont tmpfont = par.getFontSettings(params, pos);
293 tmpfont.reduce(layoutfont);
294 par.setFont(pos, tmpfont);
299 ParagraphList::iterator
300 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
301 LyXCursor & send_cur,
302 string const & layout)
304 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
305 ParagraphList::iterator undoendpit = endpit;
306 ParagraphList::iterator pars_end = ownerParagraphs().end();
308 if (endpit != pars_end && endpit->getDepth()) {
309 while (endpit != pars_end && endpit->getDepth()) {
313 } else if (endpit != pars_end) {
314 // because of parindents etc.
318 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
320 // ok we have a selection. This is always between sstart_cur
321 // and sel_end cursor
323 ParagraphList::iterator pit = getPar(sstart_cur);
324 ParagraphList::iterator epit = boost::next(getPar(send_cur));
326 BufferParams const & bufparams = bv()->buffer()->params();
327 LyXLayout_ptr const & lyxlayout =
328 bufparams.getLyXTextClass()[layout];
331 pit->applyLayout(lyxlayout);
332 makeFontEntriesLayoutSpecific(bufparams, *pit);
333 pit->params().spaceTop(lyxlayout->fill_top ?
334 VSpace(VSpace::VFILL)
335 : VSpace(VSpace::NONE));
336 pit->params().spaceBottom(lyxlayout->fill_bottom ?
337 VSpace(VSpace::VFILL)
338 : VSpace(VSpace::NONE));
339 if (lyxlayout->margintype == MARGIN_MANUAL)
340 pit->setLabelWidthString(lyxlayout->labelstring());
341 cur.par(std::distance(ownerParagraphs().begin(), pit));
343 } while (pit != epit);
349 // set layout over selection and make a total rebreak of those paragraphs
350 void LyXText::setLayout(string const & layout)
352 LyXCursor tmpcursor = cursor; // store the current cursor
354 // if there is no selection just set the layout
355 // of the current paragraph
356 if (!selection.set()) {
357 selection.start = cursor; // dummy selection
358 selection.end = cursor;
361 // special handling of new environment insets
362 BufferParams const & params = bv()->buffer()->params();
363 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
364 if (lyxlayout->is_environment) {
365 // move everything in a new environment inset
366 lyxerr << "setting layout " << layout << endl;
367 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
368 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
369 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
370 InsetOld * inset = new InsetEnvironment(params, layout);
371 if (bv()->insertInset(inset)) {
373 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
380 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
381 selection.end, layout);
382 redoParagraphs(getPar(selection.start), endpit);
384 // we have to reset the selection, because the
385 // geometry could have changed
386 setCursor(selection.start.par(), selection.start.pos(), false);
387 selection.cursor = cursor;
388 setCursor(selection.end.par(), selection.end.pos(), false);
392 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
396 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
398 ParagraphList::iterator pit = cursorPar();
399 ParagraphList::iterator end = pit;
400 ParagraphList::iterator start = pit;
402 if (selection.set()) {
403 pit = getPar(selection.start);
404 end = getPar(selection.end);
409 recUndo(parOffset(start), parOffset(end));
411 bool changed = false;
413 int prev_after_depth = 0;
414 #warning parlist ... could be nicer ?
415 if (start != ownerParagraphs().begin()) {
416 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
420 int const depth = pit->params().depth();
421 if (type == bv_funcs::INC_DEPTH) {
422 if (depth < prev_after_depth
423 && pit->layout()->labeltype != LABEL_BIBLIO) {
426 pit->params().depth(depth + 1);
431 pit->params().depth(depth - 1);
434 prev_after_depth = pit->getMaxDepthAfter();
436 #warning SERIOUS: Uahh... does this mean we access end->getMaxDepthAfter?
447 redoParagraphs(start, boost::next(end));
449 // We need to actually move the text->cursor. I don't
450 // understand why ...
451 LyXCursor tmpcursor = cursor;
453 // we have to reset the visual selection because the
454 // geometry could have changed
455 if (selection.set()) {
456 setCursor(selection.start.par(), selection.start.pos());
457 selection.cursor = cursor;
458 setCursor(selection.end.par(), selection.end.pos());
461 // this handles the counter labels, and also fixes up
462 // depth values for follow-on (child) paragraphs
466 setCursor(tmpcursor.par(), tmpcursor.pos());
472 // set font over selection and make a total rebreak of those paragraphs
473 void LyXText::setFont(LyXFont const & font, bool toggleall)
475 // if there is no selection just set the current_font
476 if (!selection.set()) {
477 // Determine basis font
479 if (cursor.pos() < cursorPar()->beginOfBody()) {
480 layoutfont = getLabelFont(cursorPar());
482 layoutfont = getLayoutFont(cursorPar());
484 // Update current font
485 real_current_font.update(font,
486 bv()->buffer()->params().language,
489 // Reduce to implicit settings
490 current_font = real_current_font;
491 current_font.reduce(layoutfont);
492 // And resolve it completely
493 real_current_font.realize(layoutfont);
498 LyXCursor tmpcursor = cursor; // store the current cursor
500 // ok we have a selection. This is always between sel_start_cursor
501 // and sel_end cursor
503 recUndo(selection.start.par(), selection.end.par());
505 cursor = selection.start;
506 while (cursor.par() != selection.end.par() ||
507 cursor.pos() < selection.end.pos())
509 if (cursor.pos() < cursorPar()->size()) {
510 // an open footnote should behave like a closed one
511 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
512 cursor.pos(cursor.pos() + 1);
515 cursor.par(cursor.par() + 1);
520 redoParagraph(getPar(selection.start));
522 // we have to reset the selection, because the
523 // geometry could have changed, but we keep
524 // it for user convenience
525 setCursor(selection.start.par(), selection.start.pos());
526 selection.cursor = cursor;
527 setCursor(selection.end.par(), selection.end.pos());
529 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
530 tmpcursor.boundary());
534 // important for the screen
537 // the cursor set functions have a special mechanism. When they
538 // realize, that you left an empty paragraph, they will delete it.
540 // need the selection cursor:
541 void LyXText::setSelection()
543 TextCursor::setSelection();
547 void LyXText::clearSelection()
549 TextCursor::clearSelection();
551 // reset this in the bv()!
552 if (bv() && bv()->text)
553 bv()->text->xsel_cache.set(false);
557 void LyXText::cursorHome()
559 ParagraphList::iterator cpit = cursorPar();
560 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
564 void LyXText::cursorEnd()
566 ParagraphList::iterator cpit = cursorPar();
567 pos_type end = cpit->getRow(cursor.pos())->endpos();
568 // if not on the last row of the par, put the cursor before
570 setCursor(cpit, end == cpit->size() ? end : end - 1);
574 void LyXText::cursorTop()
576 setCursor(ownerParagraphs().begin(), 0);
580 void LyXText::cursorBottom()
582 ParagraphList::iterator lastpit =
583 boost::prior(ownerParagraphs().end());
584 setCursor(lastpit, lastpit->size());
588 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
590 // If the mask is completely neutral, tell user
591 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
592 // Could only happen with user style
593 bv()->owner()->message(_("No font change defined. "
594 "Use Character under the Layout menu to define font change."));
598 // Try implicit word selection
599 // If there is a change in the language the implicit word selection
601 LyXCursor resetCursor = cursor;
602 bool implicitSelection =
603 font.language() == ignore_language
604 && font.number() == LyXFont::IGNORE
605 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
608 setFont(font, toggleall);
610 // Implicit selections are cleared afterwards
611 //and cursor is set to the original position.
612 if (implicitSelection) {
614 cursor = resetCursor;
615 setCursor(cursorPar(), cursor.pos());
616 selection.cursor = cursor;
621 string LyXText::getStringToIndex()
623 // Try implicit word selection
624 // If there is a change in the language the implicit word selection
626 LyXCursor const reset_cursor = cursor;
627 bool const implicitSelection =
628 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
631 if (!selection.set())
632 bv()->owner()->message(_("Nothing to index!"));
633 else if (selection.start.par() != selection.end.par())
634 bv()->owner()->message(_("Cannot index more than one paragraph!"));
636 idxstring = selectionAsString(*bv()->buffer(), false);
638 // Reset cursors to their original position.
639 cursor = reset_cursor;
640 setCursor(cursorPar(), cursor.pos());
641 selection.cursor = cursor;
643 // Clear the implicit selection.
644 if (implicitSelection)
651 // the DTP switches for paragraphs. LyX will store them in the first
652 // physical paragraph. When a paragraph is broken, the top settings rest,
653 // the bottom settings are given to the new one. So I can make sure,
654 // they do not duplicate themself and you cannot play dirty tricks with
657 void LyXText::setParagraph(
658 VSpace const & space_top,
659 VSpace const & space_bottom,
660 Spacing const & spacing,
662 string const & labelwidthstring,
665 LyXCursor tmpcursor = cursor;
666 if (!selection.set()) {
667 selection.start = cursor;
668 selection.end = cursor;
671 // make sure that the depth behind the selection are restored, too
672 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
673 ParagraphList::iterator undoendpit = endpit;
674 ParagraphList::iterator pars_end = ownerParagraphs().end();
676 if (endpit != pars_end && endpit->getDepth()) {
677 while (endpit != pars_end && endpit->getDepth()) {
681 } else if (endpit != pars_end) {
682 // because of parindents etc.
686 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
688 int tmppit = selection.end.par();
690 while (tmppit != selection.start.par() - 1) {
691 setCursor(tmppit, 0);
693 ParagraphList::iterator const pit = cursorPar();
694 ParagraphParameters & params = pit->params();
695 params.spaceTop(space_top);
696 params.spaceBottom(space_bottom);
697 params.spacing(spacing);
699 // does the layout allow the new alignment?
700 LyXLayout_ptr const & layout = pit->layout();
702 if (align == LYX_ALIGN_LAYOUT)
703 align = layout->align;
704 if (align & layout->alignpossible) {
705 if (align == layout->align)
706 params.align(LYX_ALIGN_LAYOUT);
710 pit->setLabelWidthString(labelwidthstring);
711 params.noindent(noindent);
715 redoParagraphs(getPar(selection.start), endpit);
718 setCursor(selection.start.par(), selection.start.pos());
719 selection.cursor = cursor;
720 setCursor(selection.end.par(), selection.end.pos());
722 setCursor(tmpcursor.par(), tmpcursor.pos());
729 string expandLabel(LyXTextClass const & textclass,
730 LyXLayout_ptr const & layout, bool appendix)
732 string fmt = appendix ?
733 layout->labelstring_appendix() : layout->labelstring();
735 // handle 'inherited level parts' in 'fmt',
736 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
737 size_t const i = fmt.find('@', 0);
738 if (i != string::npos) {
739 size_t const j = fmt.find('@', i + 1);
740 if (j != string::npos) {
741 string parent(fmt, i + 1, j - i - 1);
742 string label = expandLabel(textclass, textclass[parent], appendix);
743 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
747 return textclass.counters().counterLabel(fmt);
751 void incrementItemDepth(ParagraphList::iterator pit,
752 ParagraphList::iterator first_pit)
754 int const cur_labeltype = pit->layout()->labeltype;
756 if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
759 int const cur_depth = pit->getDepth();
761 ParagraphList::iterator prev_pit = boost::prior(pit);
763 int const prev_depth = prev_pit->getDepth();
764 int const prev_labeltype = prev_pit->layout()->labeltype;
765 if (prev_depth == 0 && cur_depth > 0) {
766 if (prev_labeltype == cur_labeltype) {
767 pit->itemdepth = prev_pit->itemdepth + 1;
770 } else if (prev_depth < cur_depth) {
771 if (prev_labeltype == cur_labeltype) {
772 pit->itemdepth = prev_pit->itemdepth + 1;
775 } else if (prev_depth == cur_depth) {
776 if (prev_labeltype == cur_labeltype) {
777 pit->itemdepth = prev_pit->itemdepth;
781 if (prev_pit == first_pit)
789 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
790 ParagraphList::iterator firstpit,
796 int const cur_depth = pit->getDepth();
797 ParagraphList::iterator prev_pit = boost::prior(pit);
799 int const prev_depth = prev_pit->getDepth();
800 int const prev_labeltype = prev_pit->layout()->labeltype;
801 if (prev_depth <= cur_depth) {
802 if (prev_labeltype != LABEL_ENUMERATE) {
803 switch (pit->itemdepth) {
805 counters.reset("enumi");
807 counters.reset("enumii");
809 counters.reset("enumiii");
811 counters.reset("enumiv");
817 if (prev_pit == firstpit)
827 // set the counter of a paragraph. This includes the labels
828 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
830 BufferParams const & bufparams = buf.params();
831 LyXTextClass const & textclass = bufparams.getLyXTextClass();
832 LyXLayout_ptr const & layout = pit->layout();
833 ParagraphList::iterator first_pit = ownerParagraphs().begin();
834 Counters & counters = textclass.counters();
839 if (pit == first_pit) {
840 pit->params().appendix(pit->params().startOfAppendix());
842 pit->params().appendix(boost::prior(pit)->params().appendix());
843 if (!pit->params().appendix() &&
844 pit->params().startOfAppendix()) {
845 pit->params().appendix(true);
846 textclass.counters().reset();
849 // Maybe we have to increment the item depth.
850 incrementItemDepth(pit, first_pit);
853 // erase what was there before
854 pit->params().labelString(string());
856 if (layout->margintype == MARGIN_MANUAL) {
857 if (pit->params().labelWidthString().empty())
858 pit->setLabelWidthString(layout->labelstring());
860 pit->setLabelWidthString(string());
863 // is it a layout that has an automatic label?
864 if (layout->labeltype == LABEL_COUNTER) {
865 BufferParams const & bufparams = buf.params();
866 LyXTextClass const & textclass = bufparams.getLyXTextClass();
867 counters.step(layout->counter);
868 string label = expandLabel(textclass, layout, pit->params().appendix());
869 pit->params().labelString(label);
870 } else if (layout->labeltype == LABEL_ITEMIZE) {
871 // At some point of time we should do something more
872 // clever here, like:
873 // pit->params().labelString(
874 // bufparams.user_defined_bullet(pit->itemdepth).getText());
875 // for now, use a simple hardcoded label
877 switch (pit->itemdepth) {
892 pit->params().labelString(itemlabel);
893 } else if (layout->labeltype == LABEL_ENUMERATE) {
894 // Maybe we have to reset the enumeration counter.
895 resetEnumCounterIfNeeded(pit, first_pit, counters);
898 // Yes I know this is a really, really! bad solution
900 string enumcounter = "enum";
902 switch (pit->itemdepth) {
914 // not a valid enumdepth...
918 counters.step(enumcounter);
920 pit->params().labelString(counters.enumLabel(enumcounter));
921 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
922 counters.step("bibitem");
923 int number = counters.value("bibitem");
924 if (pit->bibitem()) {
925 pit->bibitem()->setCounter(number);
926 pit->params().labelString(layout->labelstring());
928 // In biblio should't be following counters but...
930 string s = buf.B_(layout->labelstring());
933 if (layout->labeltype == LABEL_SENSITIVE) {
934 ParagraphList::iterator end = ownerParagraphs().end();
935 ParagraphList::iterator tmppit = pit;
938 while (tmppit != end && tmppit->inInset()
939 // the single '=' is intended below
940 && (in = tmppit->inInset()->owner()))
942 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
943 in->lyxCode() == InsetOld::WRAP_CODE) {
947 Paragraph const * owner = &ownerPar(buf, in);
949 for ( ; tmppit != end; ++tmppit)
950 if (&*tmppit == owner)
958 if (in->lyxCode() == InsetOld::FLOAT_CODE)
959 type = static_cast<InsetFloat*>(in)->params().type;
960 else if (in->lyxCode() == InsetOld::WRAP_CODE)
961 type = static_cast<InsetWrap*>(in)->params().type;
965 Floating const & fl = textclass.floats().getType(type);
967 counters.step(fl.type());
969 // Doesn't work... yet.
970 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
972 // par->SetLayout(0);
973 // s = layout->labelstring;
974 s = _("Senseless: ");
977 pit->params().labelString(s);
983 // Updates all counters. Paragraphs with changed label string will be
984 // not be rebroken as this is too expensive. The next round will get it
986 void LyXText::updateCounters()
989 bv()->buffer()->params().getLyXTextClass().counters().reset();
991 ParagraphList::iterator beg = ownerParagraphs().begin();
992 ParagraphList::iterator end = ownerParagraphs().end();
993 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
996 maxdepth = boost::prior(pit)->getMaxDepthAfter();
998 if (pit->params().depth() > maxdepth)
999 pit->params().depth(maxdepth);
1001 // setCounter can potentially change the labelString.
1002 setCounter(*bv()->buffer(), pit);
1007 void LyXText::insertInset(InsetOld * inset)
1009 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1012 recUndo(cursor.par());
1014 cursorPar()->insertInset(cursor.pos(), inset);
1015 // Just to rebreak and refresh correctly.
1016 // The character will not be inserted a second time
1017 insertChar(Paragraph::META_INSET);
1018 // If we enter a highly editable inset the cursor should be before
1019 // the inset. After an undo LyX tries to call inset->edit(...)
1020 // and fails if the cursor is behind the inset and getInset
1021 // does not return the inset!
1022 if (isHighlyEditableInset(inset))
1029 void LyXText::cutSelection(bool doclear, bool realcut)
1031 // Stuff what we got on the clipboard. Even if there is no selection.
1033 // There is a problem with having the stuffing here in that the
1034 // larger the selection the slower LyX will get. This can be
1035 // solved by running the line below only when the selection has
1036 // finished. The solution used currently just works, to make it
1037 // faster we need to be more clever and probably also have more
1038 // calls to stuffClipboard. (Lgb)
1039 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1041 // This doesn't make sense, if there is no selection
1042 if (!selection.set())
1045 // OK, we have a selection. This is always between selection.start
1046 // and selection.end
1048 // make sure that the depth behind the selection are restored, too
1049 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1050 ParagraphList::iterator undoendpit = endpit;
1051 ParagraphList::iterator pars_end = ownerParagraphs().end();
1053 if (endpit != pars_end && endpit->getDepth()) {
1054 while (endpit != pars_end && endpit->getDepth()) {
1056 undoendpit = endpit;
1058 } else if (endpit != pars_end) {
1059 // because of parindents etc.
1063 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1065 endpit = getPar(selection.end.par());
1066 int endpos = selection.end.pos();
1068 BufferParams const & bufparams = bv()->buffer()->params();
1069 boost::tie(endpit, endpos) = realcut ?
1070 CutAndPaste::cutSelection(bufparams,
1072 getPar(selection.start.par()), endpit,
1073 selection.start.pos(), endpos,
1074 bufparams.textclass,
1076 : CutAndPaste::eraseSelection(bufparams,
1078 getPar(selection.start.par()), endpit,
1079 selection.start.pos(), endpos,
1081 // sometimes necessary
1083 getPar(selection.start.par())->stripLeadingSpaces();
1085 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1086 // cutSelection can invalidate the cursor so we need to set
1088 // we prefer the end for when tracking changes
1090 cursor.par(parOffset(endpit));
1092 // need a valid cursor. (Lgb)
1095 setCursor(cursorPar(), cursor.pos());
1096 selection.cursor = cursor;
1101 void LyXText::copySelection()
1103 // stuff the selection onto the X clipboard, from an explicit copy request
1104 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1106 // this doesnt make sense, if there is no selection
1107 if (!selection.set())
1110 // ok we have a selection. This is always between selection.start
1111 // and sel_end cursor
1113 // copy behind a space if there is one
1114 while (getPar(selection.start)->size() > selection.start.pos()
1115 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1116 && (selection.start.par() != selection.end.par()
1117 || selection.start.pos() < selection.end.pos()))
1118 selection.start.pos(selection.start.pos() + 1);
1120 CutAndPaste::copySelection(getPar(selection.start.par()),
1121 getPar(selection.end.par()),
1122 selection.start.pos(), selection.end.pos(),
1123 bv()->buffer()->params().textclass);
1127 void LyXText::pasteSelection(size_t sel_index)
1129 // this does not make sense, if there is nothing to paste
1130 if (!CutAndPaste::checkPastePossible())
1133 recUndo(cursor.par());
1135 ParagraphList::iterator endpit;
1140 boost::tie(ppp, endpit) =
1141 CutAndPaste::pasteSelection(*bv()->buffer(),
1143 cursorPar(), cursor.pos(),
1144 bv()->buffer()->params().textclass,
1146 bufferErrors(*bv()->buffer(), el);
1147 bv()->showErrorList(_("Paste"));
1149 redoParagraphs(cursorPar(), endpit);
1151 setCursor(cursor.par(), cursor.pos());
1154 selection.cursor = cursor;
1155 setCursor(ppp.first, ppp.second);
1161 void LyXText::setSelectionRange(lyx::pos_type length)
1166 selection.cursor = cursor;
1173 // simple replacing. The font of the first selected character is used
1174 void LyXText::replaceSelectionWithString(string const & str)
1176 recUndo(cursor.par());
1179 if (!selection.set()) { // create a dummy selection
1180 selection.end = cursor;
1181 selection.start = cursor;
1184 // Get font setting before we cut
1185 pos_type pos = selection.end.pos();
1186 LyXFont const font = getPar(selection.start)
1187 ->getFontSettings(bv()->buffer()->params(),
1188 selection.start.pos());
1190 // Insert the new string
1191 string::const_iterator cit = str.begin();
1192 string::const_iterator end = str.end();
1193 for (; cit != end; ++cit) {
1194 getPar(selection.end)->insertChar(pos, (*cit), font);
1198 // Cut the selection
1199 cutSelection(true, false);
1205 // needed to insert the selection
1206 void LyXText::insertStringAsLines(string const & str)
1208 ParagraphList::iterator pit = cursorPar();
1209 pos_type pos = cursor.pos();
1210 ParagraphList::iterator endpit = boost::next(cursorPar());
1212 recUndo(cursor.par());
1214 // only to be sure, should not be neccessary
1217 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1219 redoParagraphs(cursorPar(), endpit);
1220 setCursor(cursorPar(), cursor.pos());
1221 selection.cursor = cursor;
1222 setCursor(pit, pos);
1227 // turns double-CR to single CR, others where converted into one
1228 // blank. Then InsertStringAsLines is called
1229 void LyXText::insertStringAsParagraphs(string const & str)
1231 string linestr(str);
1232 bool newline_inserted = false;
1233 string::size_type const siz = linestr.length();
1235 for (string::size_type i = 0; i < siz; ++i) {
1236 if (linestr[i] == '\n') {
1237 if (newline_inserted) {
1238 // we know that \r will be ignored by
1239 // InsertStringA. Of course, it is a dirty
1240 // trick, but it works...
1241 linestr[i - 1] = '\r';
1245 newline_inserted = true;
1247 } else if (IsPrintable(linestr[i])) {
1248 newline_inserted = false;
1251 insertStringAsLines(linestr);
1255 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1257 setCursor(parOffset(pit), pos);
1261 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1263 LyXCursor old_cursor = cursor;
1264 setCursorIntern(par, pos, setfont, boundary);
1265 return deleteEmptyParagraphMechanism(old_cursor);
1269 void LyXText::redoCursor()
1271 #warning maybe the same for selections?
1272 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1276 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1277 pos_type pos, bool boundary)
1279 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1283 cur.boundary(boundary);
1285 // no rows, no fun...
1286 if (ownerParagraphs().begin()->rows.empty())
1289 // get the cursor y position in text
1291 ParagraphList::iterator pit = getPar(par);
1292 Row const & row = *pit->getRow(pos);
1294 int y = pit->y + row.y_offset();
1296 // y is now the beginning of the cursor row
1297 y += row.baseline();
1298 // y is now the cursor baseline
1301 pos_type const end = row.endpos();
1303 // None of these should happen, but we're scaredy-cats
1305 lyxerr << "dont like -1" << endl;
1308 BOOST_ASSERT(false);
1309 } else if (pos > pit->size()) {
1310 lyxerr << "dont like 1, pos: " << pos
1311 << " size: " << pit->size()
1312 << " row.pos():" << row.pos()
1313 << " paroffset: " << par << endl;
1316 BOOST_ASSERT(false);
1317 } else if (pos > end) {
1318 lyxerr << "dont like 2 please report" << endl;
1319 // This shouldn't happen.
1322 BOOST_ASSERT(false);
1323 } else if (pos < row.pos()) {
1324 lyxerr << "dont like 3 please report pos:" << pos
1325 << " size: " << pit->size()
1326 << " row.pos():" << row.pos()
1327 << " paroffset: " << par << endl;
1330 BOOST_ASSERT(false);
1332 // now get the cursors x position
1333 cur.x(int(getCursorX(pit, row, pos, boundary)));
1337 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1338 pos_type pos, bool boundary) const
1340 pos_type cursor_vpos = 0;
1342 double fill_separator = row.fill_separator();
1343 double fill_hfill = row.fill_hfill();
1344 double fill_label_hfill = row.fill_label_hfill();
1345 pos_type const row_pos = row.pos();
1346 pos_type const end = row.endpos();
1349 cursor_vpos = row_pos;
1350 else if (pos >= end && !boundary)
1351 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1353 else if (pos > row_pos && (pos >= end || boundary))
1354 // Place cursor after char at (logical) position pos - 1
1355 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1356 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1358 // Place cursor before char at (logical) position pos
1359 cursor_vpos = (bidi.level(pos) % 2 == 0)
1360 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1362 pos_type body_pos = pit->beginOfBody();
1364 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1367 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1368 pos_type pos = bidi.vis2log(vpos);
1369 if (body_pos > 0 && pos == body_pos - 1) {
1370 x += fill_label_hfill
1371 + font_metrics::width(pit->layout()->labelsep,
1373 if (pit->isLineSeparator(body_pos - 1))
1374 x -= singleWidth(pit, body_pos - 1);
1377 if (hfillExpansion(*pit, row, pos)) {
1378 x += singleWidth(pit, pos);
1379 if (pos >= body_pos)
1382 x += fill_label_hfill;
1383 } else if (pit->isSeparator(pos)) {
1384 x += singleWidth(pit, pos);
1385 if (pos >= body_pos)
1386 x += fill_separator;
1388 x += singleWidth(pit, pos);
1394 void LyXText::setCursorIntern(paroffset_type par,
1395 pos_type pos, bool setfont, bool boundary)
1397 setCursor(cursor, par, pos, boundary);
1398 bv()->x_target(cursor.x() + xo_);
1404 void LyXText::setCurrentFont()
1406 pos_type pos = cursor.pos();
1407 ParagraphList::iterator pit = cursorPar();
1409 if (cursor.boundary() && pos > 0)
1413 if (pos == pit->size())
1415 else // potentional bug... BUG (Lgb)
1416 if (pit->isSeparator(pos)) {
1417 if (pos > pit->getRow(pos)->pos() &&
1418 bidi.level(pos) % 2 ==
1419 bidi.level(pos - 1) % 2)
1421 else if (pos + 1 < pit->size())
1426 BufferParams const & bufparams = bv()->buffer()->params();
1427 current_font = pit->getFontSettings(bufparams, pos);
1428 real_current_font = getFont(pit, pos);
1430 if (cursor.pos() == pit->size() &&
1431 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1432 !cursor.boundary()) {
1433 Language const * lang =
1434 pit->getParLanguage(bufparams);
1435 current_font.setLanguage(lang);
1436 current_font.setNumber(LyXFont::OFF);
1437 real_current_font.setLanguage(lang);
1438 real_current_font.setNumber(LyXFont::OFF);
1443 // returns the column near the specified x-coordinate of the row
1444 // x is set to the real beginning of this column
1445 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1446 Row const & row, int & x, bool & boundary) const
1448 double tmpx = row.x();
1449 double fill_separator = row.fill_separator();
1450 double fill_hfill = row.fill_hfill();
1451 double fill_label_hfill = row.fill_label_hfill();
1453 pos_type vc = row.pos();
1454 pos_type end = row.endpos();
1456 LyXLayout_ptr const & layout = pit->layout();
1458 bool left_side = false;
1460 pos_type body_pos = pit->beginOfBody();
1461 double last_tmpx = tmpx;
1465 !pit->isLineSeparator(body_pos - 1)))
1468 // check for empty row
1474 while (vc < end && tmpx <= x) {
1475 c = bidi.vis2log(vc);
1477 if (body_pos > 0 && c == body_pos - 1) {
1478 tmpx += fill_label_hfill +
1479 font_metrics::width(layout->labelsep, getLabelFont(pit));
1480 if (pit->isLineSeparator(body_pos - 1))
1481 tmpx -= singleWidth(pit, body_pos - 1);
1484 if (hfillExpansion(*pit, row, c)) {
1485 tmpx += singleWidth(pit, c);
1489 tmpx += fill_label_hfill;
1490 } else if (pit->isSeparator(c)) {
1491 tmpx += singleWidth(pit, c);
1493 tmpx += fill_separator;
1495 tmpx += singleWidth(pit, c);
1500 if ((tmpx + last_tmpx) / 2 > x) {
1505 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1508 // This (rtl_support test) is not needed, but gives
1509 // some speedup if rtl_support == false
1510 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1512 // If lastrow is false, we don't need to compute
1513 // the value of rtl.
1514 bool const rtl = (lastrow)
1515 ? pit->isRightToLeftPar(bv()->buffer()->params())
1518 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1519 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1521 else if (vc == row.pos()) {
1522 c = bidi.vis2log(vc);
1523 if (bidi.level(c) % 2 == 1)
1526 c = bidi.vis2log(vc - 1);
1527 bool const rtl = (bidi.level(c) % 2 == 1);
1528 if (left_side == rtl) {
1530 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1534 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1535 if (bidi.level(end -1) % 2 == 0)
1536 tmpx -= singleWidth(pit, end - 1);
1538 tmpx += singleWidth(pit, end - 1);
1548 void LyXText::setCursorFromCoordinates(int x, int y)
1550 LyXCursor old_cursor = cursor;
1551 setCursorFromCoordinates(cursor, x, y);
1553 deleteEmptyParagraphMechanism(old_cursor);
1556 // x,y are coordinates relative to this LyXText
1557 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1559 // Get the row first.
1560 ParagraphList::iterator pit;
1561 Row const & row = *getRowNearY(y, pit);
1562 y = pit->y + row.y_offset();
1565 pos_type const column = getColumnNearX(pit, row, x, bound);
1566 cur.par(parOffset(pit));
1567 cur.pos(row.pos() + column);
1569 cur.y(y + row.baseline());
1571 cur.boundary(bound);
1575 bool LyXText::checkAndActivateInset(bool front)
1577 if (cursor.pos() == cursorPar()->size())
1579 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1580 if (!isHighlyEditableInset(inset))
1582 inset->edit(bv(), front);
1587 DispatchResult LyXText::moveRight()
1589 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1590 return moveLeftIntern(false, true, false);
1592 return moveRightIntern(true, true, false);
1596 DispatchResult LyXText::moveLeft()
1598 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1599 return moveRightIntern(true, true, false);
1601 return moveLeftIntern(false, true, false);
1605 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1607 ParagraphList::iterator c_par = cursorPar();
1608 if (boost::next(c_par) == ownerParagraphs().end()
1609 && cursor.pos() >= c_par->size())
1610 return DispatchResult(false, FINISHED_RIGHT);
1611 if (activate_inset && checkAndActivateInset(front))
1612 return DispatchResult(true, true);
1616 return DispatchResult(true);
1620 DispatchResult LyXText::moveLeftIntern(bool front,
1621 bool activate_inset, bool selecting)
1623 if (cursor.par() == 0 && cursor.pos() <= 0)
1624 return DispatchResult(false, FINISHED);
1628 if (activate_inset && checkAndActivateInset(front))
1629 return DispatchResult(true, true);
1630 return DispatchResult(true);
1634 DispatchResult LyXText::moveUp()
1636 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1637 return DispatchResult(false, FINISHED_UP);
1640 return DispatchResult(true);
1644 DispatchResult LyXText::moveDown()
1646 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1647 return DispatchResult(false, FINISHED_DOWN);
1650 return DispatchResult(true);
1654 bool LyXText::cursorLeft(bool internal)
1656 if (cursor.pos() > 0) {
1657 bool boundary = cursor.boundary();
1658 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1659 if (!internal && !boundary &&
1660 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1661 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1665 if (cursor.par() != 0) {
1666 // steps into the paragraph above
1667 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1675 bool LyXText::cursorRight(bool internal)
1677 if (!internal && cursor.boundary()) {
1678 setCursor(cursor.par(), cursor.pos(), true, false);
1682 if (cursor.pos() != cursorPar()->size()) {
1683 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1684 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1686 setCursor(cursor.par(), cursor.pos(), true, true);
1690 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1691 setCursor(cursor.par() + 1, 0);
1699 void LyXText::cursorUp(bool selecting)
1701 Row const & row = *cursorRow();
1702 int x = bv()->x_target() - xo_;
1703 int y = cursor.y() - row.baseline() - 1;
1704 setCursorFromCoordinates(x, y);
1707 int y_abs = y + yo_ - bv()->top_y();
1708 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1709 if (inset_hit && isHighlyEditableInset(inset_hit))
1710 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1715 void LyXText::cursorDown(bool selecting)
1717 Row const & row = *cursorRow();
1718 int x = bv()->x_target() - xo_;
1719 int y = cursor.y() - row.baseline() + row.height() + 1;
1720 setCursorFromCoordinates(x, y);
1723 int y_abs = y + yo_ - bv()->top_y();
1724 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1725 if (inset_hit && isHighlyEditableInset(inset_hit))
1726 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1731 void LyXText::cursorUpParagraph()
1733 ParagraphList::iterator cpit = cursorPar();
1734 if (cursor.pos() > 0)
1736 else if (cpit != ownerParagraphs().begin())
1737 setCursor(boost::prior(cpit), 0);
1741 void LyXText::cursorDownParagraph()
1743 ParagraphList::iterator pit = cursorPar();
1744 ParagraphList::iterator next_pit = boost::next(pit);
1746 if (next_pit != ownerParagraphs().end())
1747 setCursor(next_pit, 0);
1749 setCursor(pit, pit->size());
1753 // fix the cursor `cur' after a characters has been deleted at `where'
1754 // position. Called by deleteEmptyParagraphMechanism
1755 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1757 // if cursor is not in the paragraph where the delete occured,
1759 if (cur.par() != where.par())
1762 // if cursor position is after the place where the delete occured,
1764 if (cur.pos() > where.pos())
1765 cur.pos(cur.pos()-1);
1767 // check also if we don't want to set the cursor on a spot behind the
1768 // pagragraph because we erased the last character.
1769 if (cur.pos() > getPar(cur)->size())
1770 cur.pos(getPar(cur)->size());
1772 // recompute row et al. for this cursor
1773 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1777 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1779 // Would be wrong to delete anything if we have a selection.
1780 if (selection.set())
1783 // Don't do anything if the cursor is invalid
1784 if (old_cursor.par() == -1)
1787 // We allow all kinds of "mumbo-jumbo" when freespacing.
1788 ParagraphList::iterator const old_pit = getPar(old_cursor);
1789 if (old_pit->isFreeSpacing())
1792 /* Ok I'll put some comments here about what is missing.
1793 I have fixed BackSpace (and thus Delete) to not delete
1794 double-spaces automagically. I have also changed Cut,
1795 Copy and Paste to hopefully do some sensible things.
1796 There are still some small problems that can lead to
1797 double spaces stored in the document file or space at
1798 the beginning of paragraphs. This happens if you have
1799 the cursor between to spaces and then save. Or if you
1800 cut and paste and the selection have a space at the
1801 beginning and then save right after the paste. I am
1802 sure none of these are very hard to fix, but I will
1803 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1804 that I can get some feedback. (Lgb)
1807 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1808 // delete the LineSeparator.
1811 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1812 // delete the LineSeparator.
1815 // If the pos around the old_cursor were spaces, delete one of them.
1816 if (old_cursor.par() != cursor.par()
1817 || old_cursor.pos() != cursor.pos()) {
1819 // Only if the cursor has really moved
1820 if (old_cursor.pos() > 0
1821 && old_cursor.pos() < old_pit->size()
1822 && old_pit->isLineSeparator(old_cursor.pos())
1823 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1824 bool erased = old_pit->erase(old_cursor.pos() - 1);
1825 redoParagraph(old_pit);
1829 #ifdef WITH_WARNINGS
1830 #warning This will not work anymore when we have multiple views of the same buffer
1831 // In this case, we will have to correct also the cursors held by
1832 // other bufferviews. It will probably be easier to do that in a more
1833 // automated way in LyXCursor code. (JMarc 26/09/2001)
1835 // correct all cursors held by the LyXText
1836 fixCursorAfterDelete(cursor, old_cursor);
1837 fixCursorAfterDelete(selection.cursor, old_cursor);
1838 fixCursorAfterDelete(selection.start, old_cursor);
1839 fixCursorAfterDelete(selection.end, old_cursor);
1844 // don't delete anything if this is the ONLY paragraph!
1845 if (ownerParagraphs().size() == 1)
1848 // Do not delete empty paragraphs with keepempty set.
1849 if (old_pit->allowEmpty())
1852 // only do our magic if we changed paragraph
1853 if (old_cursor.par() == cursor.par())
1856 // record if we have deleted a paragraph
1857 // we can't possibly have deleted a paragraph before this point
1858 bool deleted = false;
1860 if (old_pit->empty()
1861 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1862 // ok, we will delete something
1863 LyXCursor tmpcursor;
1867 bool selection_position_was_oldcursor_position =
1868 selection.cursor.par() == old_cursor.par()
1869 && selection.cursor.pos() == old_cursor.pos();
1872 cursor = old_cursor; // that undo can restore the right cursor position
1874 ParagraphList::iterator endpit = boost::next(old_pit);
1875 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1878 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1882 ParagraphList::iterator tmppit = cursorPar();
1884 ownerParagraphs().erase(old_pit);
1885 // update cursor par offset
1886 cursor.par(parOffset(tmppit));
1890 setCursorIntern(cursor.par(), cursor.pos());
1892 if (selection_position_was_oldcursor_position) {
1893 // correct selection
1894 selection.cursor = cursor;
1901 if (old_pit->stripLeadingSpaces()) {
1902 redoParagraph(old_pit);
1904 setCursorIntern(cursor.par(), cursor.pos());
1905 selection.cursor = cursor;
1911 ParagraphList & LyXText::ownerParagraphs() const
1913 return *paragraphs_;
1917 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1919 recordUndo(Undo::ATOMIC, this, first, last);
1923 void LyXText::recUndo(lyx::paroffset_type par) const
1925 recordUndo(Undo::ATOMIC, this, par, par);
1929 bool LyXText::isInInset() const
1931 // Sub-level has non-null bv owner and non-null inset owner.
1932 return inset_owner != 0;
1936 int defaultRowHeight()
1938 LyXFont const font(LyXFont::ALL_SANE);
1939 return int(font_metrics::maxHeight(font) * 1.2);