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 ininset,
74 ParagraphList & paragraphs)
75 : height(0), width(0), inset_owner(inset), bv_owner(bv),
76 in_inset_(ininset), paragraphs_(¶graphs),
77 cache_pos_(-1), 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)
94 current_font = getFont(beg, 0);
96 redoParagraphs(beg, end);
97 setCursorIntern(0, 0);
98 selection.cursor = cursor;
104 // Gets the fully instantiated font at a given position in a paragraph
105 // Basically the same routine as Paragraph::getFont() in paragraph.C.
106 // The difference is that this one is used for displaying, and thus we
107 // are allowed to make cosmetic improvements. For instance make footnotes
109 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
111 BOOST_ASSERT(pos >= 0);
113 LyXLayout_ptr const & layout = pit->layout();
115 BufferParams const & params = bv()->buffer()->params();
116 pos_type const body_pos = pit->beginOfBody();
118 // We specialize the 95% common case:
119 if (!pit->getDepth()) {
120 LyXFont f = pit->getFontSettings(params, pos);
122 pit->inInset()->getDrawFont(f);
123 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
124 return f.realize(layout->reslabelfont);
126 return f.realize(layout->resfont);
129 // The uncommon case need not be optimized as much
132 layoutfont = layout->labelfont;
134 layoutfont = layout->font;
136 LyXFont font = pit->getFontSettings(params, pos);
137 font.realize(layoutfont);
140 pit->inInset()->getDrawFont(font);
142 // Realize with the fonts of lesser depth.
143 //font.realize(outerFont(pit, ownerParagraphs()));
144 font.realize(defaultfont_);
150 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
152 LyXLayout_ptr const & layout = pit->layout();
154 if (!pit->getDepth())
155 return layout->resfont;
157 LyXFont font = layout->font;
158 // Realize with the fonts of lesser depth.
159 //font.realize(outerFont(pit, ownerParagraphs()));
160 font.realize(defaultfont_);
166 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
168 LyXLayout_ptr const & layout = pit->layout();
170 if (!pit->getDepth())
171 return layout->reslabelfont;
173 LyXFont font = layout->labelfont;
174 // Realize with the fonts of lesser depth.
175 font.realize(outerFont(pit, ownerParagraphs()));
176 font.realize(defaultfont_);
182 void LyXText::setCharFont(ParagraphList::iterator pit,
183 pos_type pos, LyXFont const & fnt,
186 BufferParams const & params = bv()->buffer()->params();
187 LyXFont font = getFont(pit, pos);
188 font.update(fnt, params.language, toggleall);
189 // Let the insets convert their font
190 if (pit->isInset(pos)) {
191 InsetOld * inset = pit->getInset(pos);
192 if (isEditableInset(inset)) {
193 static_cast<UpdatableInset *>(inset)
194 ->setFont(bv(), fnt, toggleall, true);
198 // Plug through to version below:
199 setCharFont(pit, pos, font);
203 void LyXText::setCharFont(
204 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
207 LyXLayout_ptr const & layout = pit->layout();
209 // Get concrete layout font to reduce against
212 if (pos < pit->beginOfBody())
213 layoutfont = layout->labelfont;
215 layoutfont = layout->font;
217 // Realize against environment font information
218 if (pit->getDepth()) {
219 ParagraphList::iterator tp = pit;
220 while (!layoutfont.resolved() &&
221 tp != ownerParagraphs().end() &&
223 tp = outerHook(tp, ownerParagraphs());
224 if (tp != ownerParagraphs().end())
225 layoutfont.realize(tp->layout()->font);
229 layoutfont.realize(defaultfont_);
231 // Now, reduce font against full layout font
232 font.reduce(layoutfont);
234 pit->setFont(pos, font);
238 InsetOld * LyXText::getInset() const
240 ParagraphList::iterator pit = cursorPar();
241 pos_type const pos = cursor.pos();
243 if (pos < pit->size() && pit->isInset(pos)) {
244 return pit->getInset(pos);
250 void LyXText::toggleInset()
252 InsetOld * inset = getInset();
253 // is there an editable inset at cursor position?
254 if (!isEditableInset(inset)) {
255 // No, try to see if we are inside a collapsable inset
256 if (inset_owner && inset_owner->owner()
257 && inset_owner->owner()->isOpen()) {
259 inset_owner->owner()->close();
260 bv()->getLyXText()->cursorRight(true);
261 bv()->updateParagraphDialog();
265 //bv()->owner()->message(inset->editMessage());
267 // do we want to keep this?? (JMarc)
268 if (!isHighlyEditableInset(inset))
269 recUndo(cursor.par());
279 // Asger is not sure we want to do this...
280 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
283 LyXLayout_ptr const & layout = par.layout();
284 pos_type const psize = par.size();
287 for (pos_type pos = 0; pos < psize; ++pos) {
288 if (pos < par.beginOfBody())
289 layoutfont = layout->labelfont;
291 layoutfont = layout->font;
293 LyXFont tmpfont = par.getFontSettings(params, pos);
294 tmpfont.reduce(layoutfont);
295 par.setFont(pos, tmpfont);
300 ParagraphList::iterator
301 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
302 LyXCursor & send_cur,
303 string const & layout)
305 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
306 ParagraphList::iterator undoendpit = endpit;
307 ParagraphList::iterator pars_end = ownerParagraphs().end();
309 if (endpit != pars_end && endpit->getDepth()) {
310 while (endpit != pars_end && endpit->getDepth()) {
314 } else if (endpit != pars_end) {
315 // because of parindents etc.
319 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
321 // ok we have a selection. This is always between sstart_cur
322 // and sel_end cursor
324 ParagraphList::iterator pit = getPar(sstart_cur);
325 ParagraphList::iterator epit = boost::next(getPar(send_cur));
327 BufferParams const & bufparams = bv()->buffer()->params();
328 LyXLayout_ptr const & lyxlayout =
329 bufparams.getLyXTextClass()[layout];
332 pit->applyLayout(lyxlayout);
333 makeFontEntriesLayoutSpecific(bufparams, *pit);
334 pit->params().spaceTop(lyxlayout->fill_top ?
335 VSpace(VSpace::VFILL)
336 : VSpace(VSpace::NONE));
337 pit->params().spaceBottom(lyxlayout->fill_bottom ?
338 VSpace(VSpace::VFILL)
339 : VSpace(VSpace::NONE));
340 if (lyxlayout->margintype == MARGIN_MANUAL)
341 pit->setLabelWidthString(lyxlayout->labelstring());
342 cur.par(std::distance(ownerParagraphs().begin(), pit));
344 } while (pit != epit);
350 // set layout over selection and make a total rebreak of those paragraphs
351 void LyXText::setLayout(string const & layout)
353 LyXCursor tmpcursor = cursor; // store the current cursor
355 // if there is no selection just set the layout
356 // of the current paragraph
357 if (!selection.set()) {
358 selection.start = cursor; // dummy selection
359 selection.end = cursor;
362 // special handling of new environment insets
363 BufferParams const & params = bv()->buffer()->params();
364 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
365 if (lyxlayout->is_environment) {
366 // move everything in a new environment inset
367 lyxerr << "setting layout " << layout << endl;
368 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
369 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
370 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
371 InsetOld * inset = new InsetEnvironment(params, layout);
372 if (bv()->insertInset(inset)) {
374 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
381 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
382 selection.end, layout);
383 redoParagraphs(getPar(selection.start), endpit);
385 // we have to reset the selection, because the
386 // geometry could have changed
387 setCursor(selection.start.par(), selection.start.pos(), false);
388 selection.cursor = cursor;
389 setCursor(selection.end.par(), selection.end.pos(), false);
393 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
397 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
399 ParagraphList::iterator pit = cursorPar();
400 ParagraphList::iterator end = pit;
401 ParagraphList::iterator start = pit;
403 if (selection.set()) {
404 pit = getPar(selection.start);
405 end = getPar(selection.end);
409 ParagraphList::iterator pastend = boost::next(end);
412 recUndo(parOffset(start), parOffset(end));
414 bool changed = false;
416 int prev_after_depth = 0;
417 #warning parlist ... could be nicer ?
418 if (start != ownerParagraphs().begin()) {
419 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
423 int const depth = pit->params().depth();
424 if (type == bv_funcs::INC_DEPTH) {
425 if (depth < prev_after_depth
426 && pit->layout()->labeltype != LABEL_BIBLIO) {
429 pit->params().depth(depth + 1);
434 pit->params().depth(depth - 1);
437 prev_after_depth = pit->getMaxDepthAfter();
439 #warning SERIOUS: Uahh... does this mean we access end->getMaxDepthAfter?
450 redoParagraphs(start, pastend);
452 // We need to actually move the text->cursor. I don't
453 // understand why ...
454 LyXCursor tmpcursor = cursor;
456 // we have to reset the visual selection because the
457 // geometry could have changed
458 if (selection.set()) {
459 setCursor(selection.start.par(), selection.start.pos());
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos());
464 // this handles the counter labels, and also fixes up
465 // depth values for follow-on (child) paragraphs
469 setCursor(tmpcursor.par(), tmpcursor.pos());
475 // set font over selection and make a total rebreak of those paragraphs
476 void LyXText::setFont(LyXFont const & font, bool toggleall)
478 // if there is no selection just set the current_font
479 if (!selection.set()) {
480 // Determine basis font
482 if (cursor.pos() < cursorPar()->beginOfBody()) {
483 layoutfont = getLabelFont(cursorPar());
485 layoutfont = getLayoutFont(cursorPar());
487 // Update current font
488 real_current_font.update(font,
489 bv()->buffer()->params().language,
492 // Reduce to implicit settings
493 current_font = real_current_font;
494 current_font.reduce(layoutfont);
495 // And resolve it completely
496 real_current_font.realize(layoutfont);
501 LyXCursor tmpcursor = cursor; // store the current cursor
503 // ok we have a selection. This is always between sel_start_cursor
504 // and sel_end cursor
506 recUndo(selection.start.par(), selection.end.par());
508 cursor = selection.start;
509 while (cursor.par() != selection.end.par() ||
510 cursor.pos() < selection.end.pos())
512 if (cursor.pos() < cursorPar()->size()) {
513 // an open footnote should behave like a closed one
514 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
515 cursor.pos(cursor.pos() + 1);
518 cursor.par(cursor.par() + 1);
523 redoParagraph(getPar(selection.start));
525 // we have to reset the selection, because the
526 // geometry could have changed, but we keep
527 // it for user convenience
528 setCursor(selection.start.par(), selection.start.pos());
529 selection.cursor = cursor;
530 setCursor(selection.end.par(), selection.end.pos());
532 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
533 tmpcursor.boundary());
537 // important for the screen
540 // the cursor set functions have a special mechanism. When they
541 // realize, that you left an empty paragraph, they will delete it.
543 // need the selection cursor:
544 void LyXText::setSelection()
546 TextCursor::setSelection();
550 void LyXText::clearSelection()
552 TextCursor::clearSelection();
554 // reset this in the bv()!
555 if (bv() && bv()->text)
556 bv()->text->xsel_cache.set(false);
560 void LyXText::cursorHome()
562 ParagraphList::iterator cpit = cursorPar();
563 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
567 void LyXText::cursorEnd()
569 ParagraphList::iterator cpit = cursorPar();
570 pos_type end = cpit->getRow(cursor.pos())->endpos();
571 // if not on the last row of the par, put the cursor before
573 setCursor(cpit, end == cpit->size() ? end : end - 1);
577 void LyXText::cursorTop()
579 setCursor(ownerParagraphs().begin(), 0);
583 void LyXText::cursorBottom()
585 ParagraphList::iterator lastpit =
586 boost::prior(ownerParagraphs().end());
587 setCursor(lastpit, lastpit->size());
591 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
593 // If the mask is completely neutral, tell user
594 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
595 // Could only happen with user style
596 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
600 // Try implicit word selection
601 // If there is a change in the language the implicit word selection
603 LyXCursor resetCursor = cursor;
604 bool implicitSelection =
605 font.language() == ignore_language
606 && font.number() == LyXFont::IGNORE
607 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
610 setFont(font, toggleall);
612 // Implicit selections are cleared afterwards
613 //and cursor is set to the original position.
614 if (implicitSelection) {
616 cursor = resetCursor;
617 setCursor(cursorPar(), cursor.pos());
618 selection.cursor = cursor;
623 string LyXText::getStringToIndex()
625 // Try implicit word selection
626 // If there is a change in the language the implicit word selection
628 LyXCursor const reset_cursor = cursor;
629 bool const implicitSelection =
630 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
633 if (!selection.set())
634 bv()->owner()->message(_("Nothing to index!"));
635 else if (selection.start.par() != selection.end.par())
636 bv()->owner()->message(_("Cannot index more than one paragraph!"));
638 idxstring = selectionAsString(*bv()->buffer(), false);
640 // Reset cursors to their original position.
641 cursor = reset_cursor;
642 setCursor(cursorPar(), cursor.pos());
643 selection.cursor = cursor;
645 // Clear the implicit selection.
646 if (implicitSelection)
653 // the DTP switches for paragraphs. LyX will store them in the first
654 // physical paragraph. When a paragraph is broken, the top settings rest,
655 // the bottom settings are given to the new one. So I can make sure,
656 // they do not duplicate themself and you cannot play dirty tricks with
659 void LyXText::setParagraph(
660 VSpace const & space_top,
661 VSpace const & space_bottom,
662 Spacing const & spacing,
664 string const & labelwidthstring,
667 LyXCursor tmpcursor = cursor;
668 if (!selection.set()) {
669 selection.start = cursor;
670 selection.end = cursor;
673 // make sure that the depth behind the selection are restored, too
674 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
675 ParagraphList::iterator undoendpit = endpit;
676 ParagraphList::iterator pars_end = ownerParagraphs().end();
678 if (endpit != pars_end && endpit->getDepth()) {
679 while (endpit != pars_end && endpit->getDepth()) {
683 } else if (endpit != pars_end) {
684 // because of parindents etc.
688 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
690 int tmppit = selection.end.par();
692 while (tmppit != selection.start.par() - 1) {
693 setCursor(tmppit, 0);
695 ParagraphList::iterator const pit = cursorPar();
696 ParagraphParameters & params = pit->params();
697 params.spaceTop(space_top);
698 params.spaceBottom(space_bottom);
699 params.spacing(spacing);
701 // does the layout allow the new alignment?
702 LyXLayout_ptr const & layout = pit->layout();
704 if (align == LYX_ALIGN_LAYOUT)
705 align = layout->align;
706 if (align & layout->alignpossible) {
707 if (align == layout->align)
708 params.align(LYX_ALIGN_LAYOUT);
712 pit->setLabelWidthString(labelwidthstring);
713 params.noindent(noindent);
717 redoParagraphs(getPar(selection.start), endpit);
720 setCursor(selection.start.par(), selection.start.pos());
721 selection.cursor = cursor;
722 setCursor(selection.end.par(), selection.end.pos());
724 setCursor(tmpcursor.par(), tmpcursor.pos());
731 string expandLabel(LyXTextClass const & textclass,
732 LyXLayout_ptr const & layout, bool appendix)
734 string fmt = appendix ?
735 layout->labelstring_appendix() : layout->labelstring();
737 // handle 'inherited level parts' in 'fmt',
738 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
739 size_t const i = fmt.find('@', 0);
740 if (i != string::npos) {
741 size_t const j = fmt.find('@', i + 1);
742 if (j != string::npos) {
743 string parent(fmt, i + 1, j - i - 1);
744 string label = expandLabel(textclass, textclass[parent], appendix);
745 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
749 return textclass.counters().counterLabel(fmt);
753 void incrementItemDepth(ParagraphList::iterator pit,
754 ParagraphList::iterator first_pit)
756 int const cur_labeltype = pit->layout()->labeltype;
758 if (cur_labeltype != LABEL_ENUMERATE &&
759 cur_labeltype != LABEL_ITEMIZE)
762 int const cur_depth = pit->getDepth();
764 ParagraphList::iterator prev_pit = boost::prior(pit);
766 int const prev_depth = prev_pit->getDepth();
767 int const prev_labeltype = prev_pit->layout()->labeltype;
768 if (prev_depth == 0 && cur_depth > 0) {
769 if (prev_labeltype == cur_labeltype) {
770 pit->itemdepth = prev_pit->itemdepth + 1;
773 } else if (prev_depth < cur_depth) {
774 if (prev_labeltype == cur_labeltype) {
775 pit->itemdepth = prev_pit->itemdepth + 1;
778 } else if (prev_depth == cur_depth) {
779 if (prev_labeltype == cur_labeltype) {
780 pit->itemdepth = prev_pit->itemdepth;
784 if (prev_pit == first_pit)
792 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
793 ParagraphList::iterator firstpit,
799 int const cur_depth = pit->getDepth();
800 ParagraphList::iterator prev_pit = boost::prior(pit);
802 int const prev_depth = prev_pit->getDepth();
803 int const prev_labeltype = prev_pit->layout()->labeltype;
804 if (prev_depth <= cur_depth) {
805 if (prev_labeltype != LABEL_ENUMERATE) {
806 switch (pit->itemdepth) {
808 counters.reset("enumi");
810 counters.reset("enumii");
812 counters.reset("enumiii");
814 counters.reset("enumiv");
820 if (prev_pit == firstpit)
830 // set the counter of a paragraph. This includes the labels
831 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
833 BufferParams const & bufparams = buf.params();
834 LyXTextClass const & textclass = bufparams.getLyXTextClass();
835 LyXLayout_ptr const & layout = pit->layout();
836 ParagraphList::iterator first_pit = ownerParagraphs().begin();
837 Counters & counters = textclass.counters();
842 if (pit == first_pit) {
843 pit->params().appendix(pit->params().startOfAppendix());
845 pit->params().appendix(boost::prior(pit)->params().appendix());
846 if (!pit->params().appendix() &&
847 pit->params().startOfAppendix()) {
848 pit->params().appendix(true);
849 textclass.counters().reset();
852 // Maybe we have to increment the item depth.
853 incrementItemDepth(pit, first_pit);
856 // erase what was there before
857 pit->params().labelString(string());
859 if (layout->margintype == MARGIN_MANUAL) {
860 if (pit->params().labelWidthString().empty())
861 pit->setLabelWidthString(layout->labelstring());
863 pit->setLabelWidthString(string());
866 // is it a layout that has an automatic label?
867 if (layout->labeltype == LABEL_COUNTER) {
868 BufferParams const & bufparams = buf.params();
869 LyXTextClass const & textclass = bufparams.getLyXTextClass();
870 counters.step(layout->counter);
871 string label = expandLabel(textclass, layout, pit->params().appendix());
872 pit->params().labelString(label);
873 } else if (layout->labeltype == LABEL_ITEMIZE) {
874 // At some point of time we should do something more
875 // clever here, like:
876 // pit->params().labelString(
877 // bufparams.user_defined_bullet(pit->itemdepth).getText());
878 // for now, use a simple hardcoded label
880 switch (pit->itemdepth) {
895 pit->params().labelString(itemlabel);
896 } else if (layout->labeltype == LABEL_ENUMERATE) {
897 // Maybe we have to reset the enumeration counter.
898 resetEnumCounterIfNeeded(pit, first_pit, counters);
901 // Yes I know this is a really, really! bad solution
903 string enumcounter = "enum";
905 switch (pit->itemdepth) {
917 // not a valid enumdepth...
921 counters.step(enumcounter);
923 pit->params().labelString(counters.enumLabel(enumcounter));
924 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
925 counters.step("bibitem");
926 int number = counters.value("bibitem");
927 if (pit->bibitem()) {
928 pit->bibitem()->setCounter(number);
929 pit->params().labelString(layout->labelstring());
931 // In biblio should't be following counters but...
933 string s = buf.B_(layout->labelstring());
936 if (layout->labeltype == LABEL_SENSITIVE) {
937 ParagraphList::iterator end = ownerParagraphs().end();
938 ParagraphList::iterator tmppit = pit;
941 while (tmppit != end && tmppit->inInset()
942 // the single '=' is intended below
943 && (in = tmppit->inInset()->owner()))
945 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
946 in->lyxCode() == InsetOld::WRAP_CODE) {
950 Paragraph const * owner = &ownerPar(buf, in);
952 for ( ; tmppit != end; ++tmppit)
953 if (&*tmppit == owner)
961 if (in->lyxCode() == InsetOld::FLOAT_CODE)
962 type = static_cast<InsetFloat*>(in)->params().type;
963 else if (in->lyxCode() == InsetOld::WRAP_CODE)
964 type = static_cast<InsetWrap*>(in)->params().type;
968 Floating const & fl = textclass.floats().getType(type);
970 counters.step(fl.type());
972 // Doesn't work... yet.
973 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
975 // par->SetLayout(0);
976 // s = layout->labelstring;
977 s = _("Senseless: ");
980 pit->params().labelString(s);
986 // Updates all counters. Paragraphs with changed label string will be rebroken
987 void LyXText::updateCounters()
990 bv()->buffer()->params().getLyXTextClass().counters().reset();
992 ParagraphList::iterator beg = ownerParagraphs().begin();
993 ParagraphList::iterator end = ownerParagraphs().end();
994 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
995 string const oldLabel = pit->params().labelString();
999 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1001 if (pit->params().depth() > maxdepth)
1002 pit->params().depth(maxdepth);
1004 // setCounter can potentially change the labelString.
1005 setCounter(*bv()->buffer(), pit);
1007 string const & newLabel = pit->params().labelString();
1009 if (oldLabel != newLabel)
1015 void LyXText::insertInset(InsetOld * inset)
1017 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1020 recUndo(cursor.par());
1022 cursorPar()->insertInset(cursor.pos(), inset);
1023 // Just to rebreak and refresh correctly.
1024 // The character will not be inserted a second time
1025 insertChar(Paragraph::META_INSET);
1026 // If we enter a highly editable inset the cursor should be before
1027 // the inset. After an Undo LyX tries to call inset->edit(...)
1028 // and fails if the cursor is behind the inset and getInset
1029 // does not return the inset!
1030 if (isHighlyEditableInset(inset))
1037 void LyXText::cutSelection(bool doclear, bool realcut)
1039 // Stuff what we got on the clipboard. Even if there is no selection.
1041 // There is a problem with having the stuffing here in that the
1042 // larger the selection the slower LyX will get. This can be
1043 // solved by running the line below only when the selection has
1044 // finished. The solution used currently just works, to make it
1045 // faster we need to be more clever and probably also have more
1046 // calls to stuffClipboard. (Lgb)
1047 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1049 // This doesn't make sense, if there is no selection
1050 if (!selection.set())
1053 // OK, we have a selection. This is always between selection.start
1054 // and selection.end
1056 // make sure that the depth behind the selection are restored, too
1057 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1058 ParagraphList::iterator undoendpit = endpit;
1059 ParagraphList::iterator pars_end = ownerParagraphs().end();
1061 if (endpit != pars_end && endpit->getDepth()) {
1062 while (endpit != pars_end && endpit->getDepth()) {
1064 undoendpit = endpit;
1066 } else if (endpit != pars_end) {
1067 // because of parindents etc.
1071 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1073 endpit = getPar(selection.end.par());
1074 int endpos = selection.end.pos();
1076 BufferParams const & bufparams = bv()->buffer()->params();
1077 boost::tie(endpit, endpos) = realcut ?
1078 CutAndPaste::cutSelection(bufparams,
1080 getPar(selection.start.par()), endpit,
1081 selection.start.pos(), endpos,
1082 bufparams.textclass,
1084 : CutAndPaste::eraseSelection(bufparams,
1086 getPar(selection.start.par()), endpit,
1087 selection.start.pos(), endpos,
1089 // sometimes necessary
1091 getPar(selection.start.par())->stripLeadingSpaces();
1093 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1094 // cutSelection can invalidate the cursor so we need to set
1096 // we prefer the end for when tracking changes
1098 cursor.par(parOffset(endpit));
1100 // need a valid cursor. (Lgb)
1103 setCursor(cursorPar(), cursor.pos());
1104 selection.cursor = cursor;
1109 void LyXText::copySelection()
1111 // stuff the selection onto the X clipboard, from an explicit copy request
1112 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1114 // this doesnt make sense, if there is no selection
1115 if (!selection.set())
1118 // ok we have a selection. This is always between selection.start
1119 // and sel_end cursor
1121 // copy behind a space if there is one
1122 while (getPar(selection.start)->size() > selection.start.pos()
1123 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1124 && (selection.start.par() != selection.end.par()
1125 || selection.start.pos() < selection.end.pos()))
1126 selection.start.pos(selection.start.pos() + 1);
1128 CutAndPaste::copySelection(getPar(selection.start.par()),
1129 getPar(selection.end.par()),
1130 selection.start.pos(), selection.end.pos(),
1131 bv()->buffer()->params().textclass);
1135 void LyXText::pasteSelection(size_t sel_index)
1137 // this does not make sense, if there is nothing to paste
1138 if (!CutAndPaste::checkPastePossible())
1141 recUndo(cursor.par());
1143 ParagraphList::iterator endpit;
1148 boost::tie(ppp, endpit) =
1149 CutAndPaste::pasteSelection(*bv()->buffer(),
1151 cursorPar(), cursor.pos(),
1152 bv()->buffer()->params().textclass,
1154 bufferErrors(*bv()->buffer(), el);
1155 bv()->showErrorList(_("Paste"));
1157 redoParagraphs(cursorPar(), endpit);
1159 setCursor(cursor.par(), cursor.pos());
1162 selection.cursor = cursor;
1163 setCursor(ppp.first, ppp.second);
1169 void LyXText::setSelectionRange(lyx::pos_type length)
1174 selection.cursor = cursor;
1181 // simple replacing. The font of the first selected character is used
1182 void LyXText::replaceSelectionWithString(string const & str)
1184 recUndo(cursor.par());
1187 if (!selection.set()) { // create a dummy selection
1188 selection.end = cursor;
1189 selection.start = cursor;
1192 // Get font setting before we cut
1193 pos_type pos = selection.end.pos();
1194 LyXFont const font = getPar(selection.start)
1195 ->getFontSettings(bv()->buffer()->params(),
1196 selection.start.pos());
1198 // Insert the new string
1199 string::const_iterator cit = str.begin();
1200 string::const_iterator end = str.end();
1201 for (; cit != end; ++cit) {
1202 getPar(selection.end)->insertChar(pos, (*cit), font);
1206 // Cut the selection
1207 cutSelection(true, false);
1213 // needed to insert the selection
1214 void LyXText::insertStringAsLines(string const & str)
1216 ParagraphList::iterator pit = cursorPar();
1217 pos_type pos = cursor.pos();
1218 ParagraphList::iterator endpit = boost::next(cursorPar());
1220 recUndo(cursor.par());
1222 // only to be sure, should not be neccessary
1225 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1227 redoParagraphs(cursorPar(), endpit);
1228 setCursor(cursorPar(), cursor.pos());
1229 selection.cursor = cursor;
1230 setCursor(pit, pos);
1235 // turns double-CR to single CR, others where converted into one
1236 // blank. Then InsertStringAsLines is called
1237 void LyXText::insertStringAsParagraphs(string const & str)
1239 string linestr(str);
1240 bool newline_inserted = false;
1241 string::size_type const siz = linestr.length();
1243 for (string::size_type i = 0; i < siz; ++i) {
1244 if (linestr[i] == '\n') {
1245 if (newline_inserted) {
1246 // we know that \r will be ignored by
1247 // InsertStringA. Of course, it is a dirty
1248 // trick, but it works...
1249 linestr[i - 1] = '\r';
1253 newline_inserted = true;
1255 } else if (IsPrintable(linestr[i])) {
1256 newline_inserted = false;
1259 insertStringAsLines(linestr);
1263 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1265 setCursor(parOffset(pit), pos);
1269 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1271 LyXCursor old_cursor = cursor;
1272 setCursorIntern(par, pos, setfont, boundary);
1273 return deleteEmptyParagraphMechanism(old_cursor);
1277 void LyXText::redoCursor()
1279 #warning maybe the same for selections?
1280 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1284 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1285 pos_type pos, bool boundary)
1287 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1291 cur.boundary(boundary);
1293 // no rows, no fun...
1294 if (ownerParagraphs().begin()->rows.empty())
1297 // get the cursor y position in text
1299 ParagraphList::iterator pit = getPar(par);
1300 Row const & row = *pit->getRow(pos);
1302 int y = pit->y + row.y_offset();
1304 // y is now the beginning of the cursor row
1305 y += row.baseline();
1306 // y is now the cursor baseline
1309 pos_type const end = row.endpos();
1311 // None of these should happen, but we're scaredy-cats
1313 lyxerr << "dont like -1" << endl;
1316 BOOST_ASSERT(false);
1317 } else if (pos > pit->size()) {
1318 lyxerr << "dont like 1, pos: " << pos
1319 << " size: " << pit->size()
1320 << " row.pos():" << row.pos()
1321 << " paroffset: " << par << endl;
1324 BOOST_ASSERT(false);
1325 } else if (pos > end) {
1326 lyxerr << "dont like 2 please report" << endl;
1327 // This shouldn't happen.
1330 BOOST_ASSERT(false);
1331 } else if (pos < row.pos()) {
1332 lyxerr << "dont like 3 please report pos:" << pos
1333 << " size: " << pit->size()
1334 << " row.pos():" << row.pos()
1335 << " paroffset: " << par << endl;
1338 BOOST_ASSERT(false);
1340 // now get the cursors x position
1341 cur.x(int(getCursorX(pit, row, pos, boundary)));
1345 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1346 pos_type pos, bool boundary) const
1348 pos_type cursor_vpos = 0;
1350 double fill_separator = row.fill_separator();
1351 double fill_hfill = row.fill_hfill();
1352 double fill_label_hfill = row.fill_label_hfill();
1353 pos_type const row_pos = row.pos();
1354 pos_type const end = row.endpos();
1357 cursor_vpos = row_pos;
1358 else if (pos >= end && !boundary)
1359 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1361 else if (pos > row_pos && (pos >= end || boundary))
1362 // Place cursor after char at (logical) position pos - 1
1363 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1364 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1366 // Place cursor before char at (logical) position pos
1367 cursor_vpos = (bidi.level(pos) % 2 == 0)
1368 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1370 pos_type body_pos = pit->beginOfBody();
1372 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1375 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1376 pos_type pos = bidi.vis2log(vpos);
1377 if (body_pos > 0 && pos == body_pos - 1) {
1378 x += fill_label_hfill
1379 + font_metrics::width(pit->layout()->labelsep,
1381 if (pit->isLineSeparator(body_pos - 1))
1382 x -= singleWidth(pit, body_pos - 1);
1385 if (hfillExpansion(*pit, row, pos)) {
1386 x += singleWidth(pit, pos);
1387 if (pos >= body_pos)
1390 x += fill_label_hfill;
1391 } else if (pit->isSeparator(pos)) {
1392 x += singleWidth(pit, pos);
1393 if (pos >= body_pos)
1394 x += fill_separator;
1396 x += singleWidth(pit, pos);
1402 void LyXText::setCursorIntern(paroffset_type par,
1403 pos_type pos, bool setfont, bool boundary)
1405 setCursor(cursor, par, pos, boundary);
1406 bv()->x_target(cursor.x() + xo_);
1412 void LyXText::setCurrentFont()
1414 pos_type pos = cursor.pos();
1415 ParagraphList::iterator pit = cursorPar();
1417 if (cursor.boundary() && pos > 0)
1421 if (pos == pit->size())
1423 else // potentional bug... BUG (Lgb)
1424 if (pit->isSeparator(pos)) {
1425 if (pos > pit->getRow(pos)->pos() &&
1426 bidi.level(pos) % 2 ==
1427 bidi.level(pos - 1) % 2)
1429 else if (pos + 1 < pit->size())
1434 BufferParams const & bufparams = bv()->buffer()->params();
1435 current_font = pit->getFontSettings(bufparams, pos);
1436 real_current_font = getFont(pit, pos);
1438 if (cursor.pos() == pit->size() &&
1439 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1440 !cursor.boundary()) {
1441 Language const * lang =
1442 pit->getParLanguage(bufparams);
1443 current_font.setLanguage(lang);
1444 current_font.setNumber(LyXFont::OFF);
1445 real_current_font.setLanguage(lang);
1446 real_current_font.setNumber(LyXFont::OFF);
1451 // returns the column near the specified x-coordinate of the row
1452 // x is set to the real beginning of this column
1453 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1454 Row const & row, int & x, bool & boundary) const
1456 double tmpx = row.x();
1457 double fill_separator = row.fill_separator();
1458 double fill_hfill = row.fill_hfill();
1459 double fill_label_hfill = row.fill_label_hfill();
1461 pos_type vc = row.pos();
1462 pos_type end = row.endpos();
1464 LyXLayout_ptr const & layout = pit->layout();
1466 bool left_side = false;
1468 pos_type body_pos = pit->beginOfBody();
1469 double last_tmpx = tmpx;
1473 !pit->isLineSeparator(body_pos - 1)))
1476 // check for empty row
1482 while (vc < end && tmpx <= x) {
1483 c = bidi.vis2log(vc);
1485 if (body_pos > 0 && c == body_pos - 1) {
1486 tmpx += fill_label_hfill +
1487 font_metrics::width(layout->labelsep, getLabelFont(pit));
1488 if (pit->isLineSeparator(body_pos - 1))
1489 tmpx -= singleWidth(pit, body_pos - 1);
1492 if (hfillExpansion(*pit, row, c)) {
1493 tmpx += singleWidth(pit, c);
1497 tmpx += fill_label_hfill;
1498 } else if (pit->isSeparator(c)) {
1499 tmpx += singleWidth(pit, c);
1501 tmpx += fill_separator;
1503 tmpx += singleWidth(pit, c);
1508 if ((tmpx + last_tmpx) / 2 > x) {
1513 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1516 // This (rtl_support test) is not needed, but gives
1517 // some speedup if rtl_support == false
1518 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1520 // If lastrow is false, we don't need to compute
1521 // the value of rtl.
1522 bool const rtl = (lastrow)
1523 ? pit->isRightToLeftPar(bv()->buffer()->params())
1526 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1527 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1529 else if (vc == row.pos()) {
1530 c = bidi.vis2log(vc);
1531 if (bidi.level(c) % 2 == 1)
1534 c = bidi.vis2log(vc - 1);
1535 bool const rtl = (bidi.level(c) % 2 == 1);
1536 if (left_side == rtl) {
1538 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1542 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1543 if (bidi.level(end -1) % 2 == 0)
1544 tmpx -= singleWidth(pit, end - 1);
1546 tmpx += singleWidth(pit, end - 1);
1556 void LyXText::setCursorFromCoordinates(int x, int y)
1558 LyXCursor old_cursor = cursor;
1559 setCursorFromCoordinates(cursor, x, y);
1561 deleteEmptyParagraphMechanism(old_cursor);
1564 //x,y are coordinates relative to this LyXText
1565 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1567 // Get the row first.
1568 ParagraphList::iterator pit;
1569 Row const & row = *getRowNearY(y, pit);
1570 y = pit->y + row.y_offset();
1573 pos_type const column = getColumnNearX(pit, row, x, bound);
1574 cur.par(parOffset(pit));
1575 cur.pos(row.pos() + column);
1577 cur.y(y + row.baseline());
1579 cur.boundary(bound);
1584 bool LyXText::checkAndActivateInset(bool front)
1586 if (cursor.pos() == cursorPar()->size())
1588 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1589 if (!isHighlyEditableInset(inset))
1591 inset->edit(bv(), front);
1596 DispatchResult LyXText::moveRight()
1598 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1599 return moveLeftIntern(false, true, false);
1601 return moveRightIntern(true, true, false);
1605 DispatchResult LyXText::moveLeft()
1607 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1608 return moveRightIntern(true, true, false);
1610 return moveLeftIntern(false, true, false);
1614 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1616 ParagraphList::iterator c_par = cursorPar();
1617 if (boost::next(c_par) == ownerParagraphs().end()
1618 && cursor.pos() >= c_par->size())
1619 return DispatchResult(false, FINISHED_RIGHT);
1620 if (activate_inset && checkAndActivateInset(front))
1621 return DispatchResult(true, true);
1625 return DispatchResult(true);
1629 DispatchResult LyXText::moveLeftIntern(bool front,
1630 bool activate_inset, bool selecting)
1632 if (cursor.par() == 0 && cursor.pos() <= 0)
1633 return DispatchResult(false, FINISHED);
1637 if (activate_inset && checkAndActivateInset(front))
1638 return DispatchResult(true, true);
1639 return DispatchResult(true);
1643 DispatchResult LyXText::moveUp()
1645 if (cursorRow() == firstRow())
1646 return DispatchResult(false, FINISHED_UP);
1649 return DispatchResult(true);
1653 DispatchResult LyXText::moveDown()
1655 if (cursorRow() == lastRow())
1656 return DispatchResult(false, FINISHED_DOWN);
1659 return DispatchResult(true);
1663 bool LyXText::cursorLeft(bool internal)
1665 if (cursor.pos() > 0) {
1666 bool boundary = cursor.boundary();
1667 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1668 if (!internal && !boundary &&
1669 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1670 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1674 if (cursor.par() != 0) {
1675 // steps into the paragraph above
1676 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1684 bool LyXText::cursorRight(bool internal)
1686 if (!internal && cursor.boundary()) {
1687 setCursor(cursor.par(), cursor.pos(), true, false);
1691 if (cursor.pos() != cursorPar()->size()) {
1692 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1693 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1695 setCursor(cursor.par(), cursor.pos(), true, true);
1699 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1700 setCursor(cursor.par() + 1, 0);
1708 void LyXText::cursorUp(bool selecting)
1710 Row const & row = *cursorRow();
1711 int x = bv()->x_target() - xo_;
1712 int y = cursor.y() - row.baseline() - 1;
1713 setCursorFromCoordinates(x, y);
1716 int y_abs = y + yo_ - bv()->top_y();
1717 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1718 if (inset_hit && isHighlyEditableInset(inset_hit))
1719 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1724 void LyXText::cursorDown(bool selecting)
1726 Row const & row = *cursorRow();
1727 int x = bv()->x_target() - xo_;
1728 int y = cursor.y() - row.baseline() + row.height() + 1;
1729 setCursorFromCoordinates(x, y);
1732 int y_abs = y + yo_ - bv()->top_y();
1733 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1734 if (inset_hit && isHighlyEditableInset(inset_hit))
1735 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1740 void LyXText::cursorUpParagraph()
1742 ParagraphList::iterator cpit = cursorPar();
1743 if (cursor.pos() > 0)
1745 else if (cpit != ownerParagraphs().begin())
1746 setCursor(boost::prior(cpit), 0);
1750 void LyXText::cursorDownParagraph()
1752 ParagraphList::iterator pit = cursorPar();
1753 ParagraphList::iterator next_pit = boost::next(pit);
1755 if (next_pit != ownerParagraphs().end())
1756 setCursor(next_pit, 0);
1758 setCursor(pit, pit->size());
1762 // fix the cursor `cur' after a characters has been deleted at `where'
1763 // position. Called by deleteEmptyParagraphMechanism
1764 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1766 // if cursor is not in the paragraph where the delete occured,
1768 if (cur.par() != where.par())
1771 // if cursor position is after the place where the delete occured,
1773 if (cur.pos() > where.pos())
1774 cur.pos(cur.pos()-1);
1776 // check also if we don't want to set the cursor on a spot behind the
1777 // pagragraph because we erased the last character.
1778 if (cur.pos() > getPar(cur)->size())
1779 cur.pos(getPar(cur)->size());
1781 // recompute row et al. for this cursor
1782 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1786 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1788 // Would be wrong to delete anything if we have a selection.
1789 if (selection.set())
1792 // Don't do anything if the cursor is invalid
1793 if (old_cursor.par() == -1)
1796 // We allow all kinds of "mumbo-jumbo" when freespacing.
1797 ParagraphList::iterator const old_pit = getPar(old_cursor);
1798 if (old_pit->isFreeSpacing())
1801 /* Ok I'll put some comments here about what is missing.
1802 I have fixed BackSpace (and thus Delete) to not delete
1803 double-spaces automagically. I have also changed Cut,
1804 Copy and Paste to hopefully do some sensible things.
1805 There are still some small problems that can lead to
1806 double spaces stored in the document file or space at
1807 the beginning of paragraphs. This happens if you have
1808 the cursor between to spaces and then save. Or if you
1809 cut and paste and the selection have a space at the
1810 beginning and then save right after the paste. I am
1811 sure none of these are very hard to fix, but I will
1812 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1813 that I can get some feedback. (Lgb)
1816 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1817 // delete the LineSeparator.
1820 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1821 // delete the LineSeparator.
1824 // If the pos around the old_cursor were spaces, delete one of them.
1825 if (old_cursor.par() != cursor.par()
1826 || old_cursor.pos() != cursor.pos()) {
1828 // Only if the cursor has really moved
1829 if (old_cursor.pos() > 0
1830 && old_cursor.pos() < old_pit->size()
1831 && old_pit->isLineSeparator(old_cursor.pos())
1832 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1833 bool erased = old_pit->erase(old_cursor.pos() - 1);
1834 redoParagraph(old_pit);
1838 #ifdef WITH_WARNINGS
1839 #warning This will not work anymore when we have multiple views of the same buffer
1840 // In this case, we will have to correct also the cursors held by
1841 // other bufferviews. It will probably be easier to do that in a more
1842 // automated way in LyXCursor code. (JMarc 26/09/2001)
1844 // correct all cursors held by the LyXText
1845 fixCursorAfterDelete(cursor, old_cursor);
1846 fixCursorAfterDelete(selection.cursor, old_cursor);
1847 fixCursorAfterDelete(selection.start, old_cursor);
1848 fixCursorAfterDelete(selection.end, old_cursor);
1853 // don't delete anything if this is the ONLY paragraph!
1854 if (ownerParagraphs().size() == 1)
1857 // Do not delete empty paragraphs with keepempty set.
1858 if (old_pit->allowEmpty())
1861 // only do our magic if we changed paragraph
1862 if (old_cursor.par() == cursor.par())
1865 // record if we have deleted a paragraph
1866 // we can't possibly have deleted a paragraph before this point
1867 bool deleted = false;
1869 if (old_pit->empty()
1870 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1871 // ok, we will delete something
1872 LyXCursor tmpcursor;
1876 bool selection_position_was_oldcursor_position =
1877 selection.cursor.par() == old_cursor.par()
1878 && selection.cursor.pos() == old_cursor.pos();
1881 cursor = old_cursor; // that undo can restore the right cursor position
1883 ParagraphList::iterator endpit = boost::next(old_pit);
1884 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1887 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1891 ParagraphList::iterator tmppit = cursorPar();
1893 ownerParagraphs().erase(old_pit);
1894 // update cursor par offset
1895 cursor.par(parOffset(tmppit));
1899 setCursorIntern(cursor.par(), cursor.pos());
1901 if (selection_position_was_oldcursor_position) {
1902 // correct selection
1903 selection.cursor = cursor;
1910 if (old_pit->stripLeadingSpaces()) {
1911 redoParagraph(old_pit);
1913 setCursorIntern(cursor.par(), cursor.pos());
1914 selection.cursor = cursor;
1920 ParagraphList & LyXText::ownerParagraphs() const
1922 return *paragraphs_;
1926 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1928 recordUndo(Undo::ATOMIC, this, first, last);
1932 void LyXText::recUndo(lyx::paroffset_type par) const
1934 recordUndo(Undo::ATOMIC, this, par, par);
1938 bool LyXText::isInInset() const
1940 // Sub-level has non-null bv owner and non-null inset owner.
1941 return inset_owner != 0;
1945 int defaultRowHeight()
1947 LyXFont const font(LyXFont::ALL_SANE);
1948 return int(font_metrics::maxHeight(font) * 1.2);