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(bv());
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());
281 // Asger is not sure we want to do this...
282 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
285 LyXLayout_ptr const & layout = par.layout();
286 pos_type const psize = par.size();
289 for (pos_type pos = 0; pos < psize; ++pos) {
290 if (pos < par.beginOfBody())
291 layoutfont = layout->labelfont;
293 layoutfont = layout->font;
295 LyXFont tmpfont = par.getFontSettings(params, pos);
296 tmpfont.reduce(layoutfont);
297 par.setFont(pos, tmpfont);
302 ParagraphList::iterator
303 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
304 LyXCursor & send_cur,
305 string const & layout)
307 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
308 ParagraphList::iterator undoendpit = endpit;
309 ParagraphList::iterator pars_end = ownerParagraphs().end();
311 if (endpit != pars_end && endpit->getDepth()) {
312 while (endpit != pars_end && endpit->getDepth()) {
316 } else if (endpit != pars_end) {
317 // because of parindents etc.
321 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
323 // ok we have a selection. This is always between sstart_cur
324 // and sel_end cursor
326 ParagraphList::iterator pit = getPar(sstart_cur);
327 ParagraphList::iterator epit = boost::next(getPar(send_cur));
329 BufferParams const & bufparams = bv()->buffer()->params();
330 LyXLayout_ptr const & lyxlayout =
331 bufparams.getLyXTextClass()[layout];
334 pit->applyLayout(lyxlayout);
335 makeFontEntriesLayoutSpecific(bufparams, *pit);
336 pit->params().spaceTop(lyxlayout->fill_top ?
337 VSpace(VSpace::VFILL)
338 : VSpace(VSpace::NONE));
339 pit->params().spaceBottom(lyxlayout->fill_bottom ?
340 VSpace(VSpace::VFILL)
341 : VSpace(VSpace::NONE));
342 if (lyxlayout->margintype == MARGIN_MANUAL)
343 pit->setLabelWidthString(lyxlayout->labelstring());
344 cur.par(std::distance(ownerParagraphs().begin(), pit));
346 } while (pit != epit);
352 // set layout over selection and make a total rebreak of those paragraphs
353 void LyXText::setLayout(string const & layout)
355 LyXCursor tmpcursor = cursor; // store the current cursor
357 // if there is no selection just set the layout
358 // of the current paragraph
359 if (!selection.set()) {
360 selection.start = cursor; // dummy selection
361 selection.end = cursor;
364 // special handling of new environment insets
365 BufferParams const & params = bv()->buffer()->params();
366 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
367 if (lyxlayout->is_environment) {
368 // move everything in a new environment inset
369 lyxerr << "setting layout " << layout << endl;
370 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
371 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
372 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
373 InsetOld * inset = new InsetEnvironment(params, layout);
374 if (bv()->insertInset(inset)) {
376 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
383 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
384 selection.end, layout);
385 redoParagraphs(getPar(selection.start), endpit);
387 // we have to reset the selection, because the
388 // geometry could have changed
389 setCursor(selection.start.par(), selection.start.pos(), false);
390 selection.cursor = cursor;
391 setCursor(selection.end.par(), selection.end.pos(), false);
395 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
399 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
401 ParagraphList::iterator pit = cursorPar();
402 ParagraphList::iterator end = pit;
403 ParagraphList::iterator start = pit;
405 if (selection.set()) {
406 pit = getPar(selection.start);
407 end = getPar(selection.end);
411 ParagraphList::iterator pastend = boost::next(end);
414 recUndo(parOffset(start), parOffset(end));
416 bool changed = false;
418 int prev_after_depth = 0;
419 #warning parlist ... could be nicer ?
420 if (start != ownerParagraphs().begin()) {
421 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
425 int const depth = pit->params().depth();
426 if (type == bv_funcs::INC_DEPTH) {
427 if (depth < prev_after_depth
428 && pit->layout()->labeltype != LABEL_BIBLIO) {
431 pit->params().depth(depth + 1);
436 pit->params().depth(depth - 1);
439 prev_after_depth = pit->getMaxDepthAfter();
441 #warning SERIOUS: Uahh... does this mean we access end->getMaxDepthAfter?
452 redoParagraphs(start, pastend);
454 // We need to actually move the text->cursor. I don't
455 // understand why ...
456 LyXCursor tmpcursor = cursor;
458 // we have to reset the visual selection because the
459 // geometry could have changed
460 if (selection.set()) {
461 setCursor(selection.start.par(), selection.start.pos());
462 selection.cursor = cursor;
463 setCursor(selection.end.par(), selection.end.pos());
466 // this handles the counter labels, and also fixes up
467 // depth values for follow-on (child) paragraphs
471 setCursor(tmpcursor.par(), tmpcursor.pos());
477 // set font over selection and make a total rebreak of those paragraphs
478 void LyXText::setFont(LyXFont const & font, bool toggleall)
480 // if there is no selection just set the current_font
481 if (!selection.set()) {
482 // Determine basis font
484 if (cursor.pos() < cursorPar()->beginOfBody()) {
485 layoutfont = getLabelFont(cursorPar());
487 layoutfont = getLayoutFont(cursorPar());
489 // Update current font
490 real_current_font.update(font,
491 bv()->buffer()->params().language,
494 // Reduce to implicit settings
495 current_font = real_current_font;
496 current_font.reduce(layoutfont);
497 // And resolve it completely
498 real_current_font.realize(layoutfont);
503 LyXCursor tmpcursor = cursor; // store the current cursor
505 // ok we have a selection. This is always between sel_start_cursor
506 // and sel_end cursor
508 recUndo(selection.start.par(), selection.end.par());
510 cursor = selection.start;
511 while (cursor.par() != selection.end.par() ||
512 cursor.pos() < selection.end.pos())
514 if (cursor.pos() < cursorPar()->size()) {
515 // an open footnote should behave like a closed one
516 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
517 cursor.pos(cursor.pos() + 1);
520 cursor.par(cursor.par() + 1);
525 redoParagraph(getPar(selection.start));
527 // we have to reset the selection, because the
528 // geometry could have changed, but we keep
529 // it for user convenience
530 setCursor(selection.start.par(), selection.start.pos());
531 selection.cursor = cursor;
532 setCursor(selection.end.par(), selection.end.pos());
534 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
535 tmpcursor.boundary());
539 // important for the screen
542 // the cursor set functions have a special mechanism. When they
543 // realize, that you left an empty paragraph, they will delete it.
545 // need the selection cursor:
546 void LyXText::setSelection()
548 TextCursor::setSelection();
552 void LyXText::clearSelection()
554 TextCursor::clearSelection();
556 // reset this in the bv()!
557 if (bv() && bv()->text)
558 bv()->text->xsel_cache.set(false);
562 void LyXText::cursorHome()
564 ParagraphList::iterator cpit = cursorPar();
565 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
569 void LyXText::cursorEnd()
571 ParagraphList::iterator cpit = cursorPar();
572 pos_type end = cpit->getRow(cursor.pos())->endpos();
573 // if not on the last row of the par, put the cursor before
575 setCursor(cpit, end == cpit->size() ? end : end - 1);
579 void LyXText::cursorTop()
581 setCursor(ownerParagraphs().begin(), 0);
585 void LyXText::cursorBottom()
587 ParagraphList::iterator lastpit =
588 boost::prior(ownerParagraphs().end());
589 setCursor(lastpit, lastpit->size());
593 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
595 // If the mask is completely neutral, tell user
596 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
597 // Could only happen with user style
598 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
602 // Try implicit word selection
603 // If there is a change in the language the implicit word selection
605 LyXCursor resetCursor = cursor;
606 bool implicitSelection =
607 font.language() == ignore_language
608 && font.number() == LyXFont::IGNORE
609 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
612 setFont(font, toggleall);
614 // Implicit selections are cleared afterwards
615 //and cursor is set to the original position.
616 if (implicitSelection) {
618 cursor = resetCursor;
619 setCursor(cursorPar(), cursor.pos());
620 selection.cursor = cursor;
625 string LyXText::getStringToIndex()
627 // Try implicit word selection
628 // If there is a change in the language the implicit word selection
630 LyXCursor const reset_cursor = cursor;
631 bool const implicitSelection =
632 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
635 if (!selection.set())
636 bv()->owner()->message(_("Nothing to index!"));
637 else if (selection.start.par() != selection.end.par())
638 bv()->owner()->message(_("Cannot index more than one paragraph!"));
640 idxstring = selectionAsString(*bv()->buffer(), false);
642 // Reset cursors to their original position.
643 cursor = reset_cursor;
644 setCursor(cursorPar(), cursor.pos());
645 selection.cursor = cursor;
647 // Clear the implicit selection.
648 if (implicitSelection)
655 // the DTP switches for paragraphs. LyX will store them in the first
656 // physical paragraph. When a paragraph is broken, the top settings rest,
657 // the bottom settings are given to the new one. So I can make sure,
658 // they do not duplicate themself and you cannot play dirty tricks with
661 void LyXText::setParagraph(
662 VSpace const & space_top,
663 VSpace const & space_bottom,
664 Spacing const & spacing,
666 string const & labelwidthstring,
669 LyXCursor tmpcursor = cursor;
670 if (!selection.set()) {
671 selection.start = cursor;
672 selection.end = cursor;
675 // make sure that the depth behind the selection are restored, too
676 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
677 ParagraphList::iterator undoendpit = endpit;
678 ParagraphList::iterator pars_end = ownerParagraphs().end();
680 if (endpit != pars_end && endpit->getDepth()) {
681 while (endpit != pars_end && endpit->getDepth()) {
685 } else if (endpit != pars_end) {
686 // because of parindents etc.
690 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
692 int tmppit = selection.end.par();
694 while (tmppit != selection.start.par() - 1) {
695 setCursor(tmppit, 0);
697 ParagraphList::iterator const pit = cursorPar();
698 ParagraphParameters & params = pit->params();
699 params.spaceTop(space_top);
700 params.spaceBottom(space_bottom);
701 params.spacing(spacing);
703 // does the layout allow the new alignment?
704 LyXLayout_ptr const & layout = pit->layout();
706 if (align == LYX_ALIGN_LAYOUT)
707 align = layout->align;
708 if (align & layout->alignpossible) {
709 if (align == layout->align)
710 params.align(LYX_ALIGN_LAYOUT);
714 pit->setLabelWidthString(labelwidthstring);
715 params.noindent(noindent);
719 redoParagraphs(getPar(selection.start), endpit);
722 setCursor(selection.start.par(), selection.start.pos());
723 selection.cursor = cursor;
724 setCursor(selection.end.par(), selection.end.pos());
726 setCursor(tmpcursor.par(), tmpcursor.pos());
733 string expandLabel(LyXTextClass const & textclass,
734 LyXLayout_ptr const & layout, bool appendix)
736 string fmt = appendix ?
737 layout->labelstring_appendix() : layout->labelstring();
739 // handle 'inherited level parts' in 'fmt',
740 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
741 size_t const i = fmt.find('@', 0);
742 if (i != string::npos) {
743 size_t const j = fmt.find('@', i + 1);
744 if (j != string::npos) {
745 string parent(fmt, i + 1, j - i - 1);
746 string label = expandLabel(textclass, textclass[parent], appendix);
747 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
751 return textclass.counters().counterLabel(fmt);
755 void incrementItemDepth(ParagraphList::iterator pit,
756 ParagraphList::iterator first_pit)
758 int const cur_labeltype = pit->layout()->labeltype;
760 if (cur_labeltype != LABEL_ENUMERATE &&
761 cur_labeltype != LABEL_ITEMIZE)
764 int const cur_depth = pit->getDepth();
766 ParagraphList::iterator prev_pit = boost::prior(pit);
768 int const prev_depth = prev_pit->getDepth();
769 int const prev_labeltype = prev_pit->layout()->labeltype;
770 if (prev_depth == 0 && cur_depth > 0) {
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 + 1;
780 } else if (prev_depth == cur_depth) {
781 if (prev_labeltype == cur_labeltype) {
782 pit->itemdepth = prev_pit->itemdepth;
786 if (prev_pit == first_pit)
794 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
795 ParagraphList::iterator firstpit,
801 int const cur_depth = pit->getDepth();
802 ParagraphList::iterator prev_pit = boost::prior(pit);
804 int const prev_depth = prev_pit->getDepth();
805 int const prev_labeltype = prev_pit->layout()->labeltype;
806 if (prev_depth <= cur_depth) {
807 if (prev_labeltype != LABEL_ENUMERATE) {
808 switch (pit->itemdepth) {
810 counters.reset("enumi");
812 counters.reset("enumii");
814 counters.reset("enumiii");
816 counters.reset("enumiv");
822 if (prev_pit == firstpit)
832 // set the counter of a paragraph. This includes the labels
833 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
835 BufferParams const & bufparams = buf.params();
836 LyXTextClass const & textclass = bufparams.getLyXTextClass();
837 LyXLayout_ptr const & layout = pit->layout();
838 ParagraphList::iterator first_pit = ownerParagraphs().begin();
839 Counters & counters = textclass.counters();
844 if (pit == first_pit) {
845 pit->params().appendix(pit->params().startOfAppendix());
847 pit->params().appendix(boost::prior(pit)->params().appendix());
848 if (!pit->params().appendix() &&
849 pit->params().startOfAppendix()) {
850 pit->params().appendix(true);
851 textclass.counters().reset();
854 // Maybe we have to increment the item depth.
855 incrementItemDepth(pit, first_pit);
858 // erase what was there before
859 pit->params().labelString(string());
861 if (layout->margintype == MARGIN_MANUAL) {
862 if (pit->params().labelWidthString().empty())
863 pit->setLabelWidthString(layout->labelstring());
865 pit->setLabelWidthString(string());
868 // is it a layout that has an automatic label?
869 if (layout->labeltype == LABEL_COUNTER) {
870 BufferParams const & bufparams = buf.params();
871 LyXTextClass const & textclass = bufparams.getLyXTextClass();
872 counters.step(layout->counter);
873 string label = expandLabel(textclass, layout, pit->params().appendix());
874 pit->params().labelString(label);
875 } else if (layout->labeltype == LABEL_ITEMIZE) {
876 // At some point of time we should do something more
877 // clever here, like:
878 // pit->params().labelString(
879 // bufparams.user_defined_bullet(pit->itemdepth).getText());
880 // for now, use a simple hardcoded label
882 switch (pit->itemdepth) {
897 pit->params().labelString(itemlabel);
898 } else if (layout->labeltype == LABEL_ENUMERATE) {
899 // Maybe we have to reset the enumeration counter.
900 resetEnumCounterIfNeeded(pit, first_pit, counters);
903 // Yes I know this is a really, really! bad solution
905 string enumcounter = "enum";
907 switch (pit->itemdepth) {
919 // not a valid enumdepth...
923 counters.step(enumcounter);
925 pit->params().labelString(counters.enumLabel(enumcounter));
926 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
927 counters.step("bibitem");
928 int number = counters.value("bibitem");
929 if (pit->bibitem()) {
930 pit->bibitem()->setCounter(number);
931 pit->params().labelString(layout->labelstring());
933 // In biblio should't be following counters but...
935 string s = buf.B_(layout->labelstring());
938 if (layout->labeltype == LABEL_SENSITIVE) {
939 ParagraphList::iterator end = ownerParagraphs().end();
940 ParagraphList::iterator tmppit = pit;
943 while (tmppit != end && tmppit->inInset()
944 // the single '=' is intended below
945 && (in = tmppit->inInset()->owner()))
947 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
948 in->lyxCode() == InsetOld::WRAP_CODE) {
952 Paragraph const * owner = &ownerPar(buf, in);
954 for ( ; tmppit != end; ++tmppit)
955 if (&*tmppit == owner)
963 if (in->lyxCode() == InsetOld::FLOAT_CODE)
964 type = static_cast<InsetFloat*>(in)->params().type;
965 else if (in->lyxCode() == InsetOld::WRAP_CODE)
966 type = static_cast<InsetWrap*>(in)->params().type;
970 Floating const & fl = textclass.floats().getType(type);
972 counters.step(fl.type());
974 // Doesn't work... yet.
975 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
977 // par->SetLayout(0);
978 // s = layout->labelstring;
979 s = _("Senseless: ");
982 pit->params().labelString(s);
988 // Updates all counters. Paragraphs with changed label string will be rebroken
989 void LyXText::updateCounters()
992 bv()->buffer()->params().getLyXTextClass().counters().reset();
994 ParagraphList::iterator beg = ownerParagraphs().begin();
995 ParagraphList::iterator end = ownerParagraphs().end();
996 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
997 string const oldLabel = pit->params().labelString();
1001 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1003 if (pit->params().depth() > maxdepth)
1004 pit->params().depth(maxdepth);
1006 // setCounter can potentially change the labelString.
1007 setCounter(*bv()->buffer(), pit);
1009 string const & newLabel = pit->params().labelString();
1011 if (oldLabel != newLabel)
1017 void LyXText::insertInset(InsetOld * inset)
1019 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1022 recUndo(cursor.par());
1024 cursorPar()->insertInset(cursor.pos(), inset);
1025 // Just to rebreak and refresh correctly.
1026 // The character will not be inserted a second time
1027 insertChar(Paragraph::META_INSET);
1028 // If we enter a highly editable inset the cursor should be before
1029 // the inset. After an Undo LyX tries to call inset->edit(...)
1030 // and fails if the cursor is behind the inset and getInset
1031 // does not return the inset!
1032 if (isHighlyEditableInset(inset))
1039 void LyXText::cutSelection(bool doclear, bool realcut)
1041 // Stuff what we got on the clipboard. Even if there is no selection.
1043 // There is a problem with having the stuffing here in that the
1044 // larger the selection the slower LyX will get. This can be
1045 // solved by running the line below only when the selection has
1046 // finished. The solution used currently just works, to make it
1047 // faster we need to be more clever and probably also have more
1048 // calls to stuffClipboard. (Lgb)
1049 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1051 // This doesn't make sense, if there is no selection
1052 if (!selection.set())
1055 // OK, we have a selection. This is always between selection.start
1056 // and selection.end
1058 // make sure that the depth behind the selection are restored, too
1059 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1060 ParagraphList::iterator undoendpit = endpit;
1061 ParagraphList::iterator pars_end = ownerParagraphs().end();
1063 if (endpit != pars_end && endpit->getDepth()) {
1064 while (endpit != pars_end && endpit->getDepth()) {
1066 undoendpit = endpit;
1068 } else if (endpit != pars_end) {
1069 // because of parindents etc.
1073 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1075 endpit = getPar(selection.end.par());
1076 int endpos = selection.end.pos();
1078 BufferParams const & bufparams = bv()->buffer()->params();
1079 boost::tie(endpit, endpos) = realcut ?
1080 CutAndPaste::cutSelection(bufparams,
1082 getPar(selection.start.par()), endpit,
1083 selection.start.pos(), endpos,
1084 bufparams.textclass,
1086 : CutAndPaste::eraseSelection(bufparams,
1088 getPar(selection.start.par()), endpit,
1089 selection.start.pos(), endpos,
1091 // sometimes necessary
1093 getPar(selection.start.par())->stripLeadingSpaces();
1095 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1096 // cutSelection can invalidate the cursor so we need to set
1098 // we prefer the end for when tracking changes
1100 cursor.par(parOffset(endpit));
1102 // need a valid cursor. (Lgb)
1105 setCursor(cursorPar(), cursor.pos());
1106 selection.cursor = cursor;
1111 void LyXText::copySelection()
1113 // stuff the selection onto the X clipboard, from an explicit copy request
1114 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1116 // this doesnt make sense, if there is no selection
1117 if (!selection.set())
1120 // ok we have a selection. This is always between selection.start
1121 // and sel_end cursor
1123 // copy behind a space if there is one
1124 while (getPar(selection.start)->size() > selection.start.pos()
1125 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1126 && (selection.start.par() != selection.end.par()
1127 || selection.start.pos() < selection.end.pos()))
1128 selection.start.pos(selection.start.pos() + 1);
1130 CutAndPaste::copySelection(getPar(selection.start.par()),
1131 getPar(selection.end.par()),
1132 selection.start.pos(), selection.end.pos(),
1133 bv()->buffer()->params().textclass);
1137 void LyXText::pasteSelection(size_t sel_index)
1139 // this does not make sense, if there is nothing to paste
1140 if (!CutAndPaste::checkPastePossible())
1143 recUndo(cursor.par());
1145 ParagraphList::iterator endpit;
1150 boost::tie(ppp, endpit) =
1151 CutAndPaste::pasteSelection(*bv()->buffer(),
1153 cursorPar(), cursor.pos(),
1154 bv()->buffer()->params().textclass,
1156 bufferErrors(*bv()->buffer(), el);
1157 bv()->showErrorList(_("Paste"));
1159 redoParagraphs(cursorPar(), endpit);
1161 setCursor(cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1165 setCursor(ppp.first, ppp.second);
1171 void LyXText::setSelectionRange(lyx::pos_type length)
1176 selection.cursor = cursor;
1183 // simple replacing. The font of the first selected character is used
1184 void LyXText::replaceSelectionWithString(string const & str)
1186 recUndo(cursor.par());
1189 if (!selection.set()) { // create a dummy selection
1190 selection.end = cursor;
1191 selection.start = cursor;
1194 // Get font setting before we cut
1195 pos_type pos = selection.end.pos();
1196 LyXFont const font = getPar(selection.start)
1197 ->getFontSettings(bv()->buffer()->params(),
1198 selection.start.pos());
1200 // Insert the new string
1201 string::const_iterator cit = str.begin();
1202 string::const_iterator end = str.end();
1203 for (; cit != end; ++cit) {
1204 getPar(selection.end)->insertChar(pos, (*cit), font);
1208 // Cut the selection
1209 cutSelection(true, false);
1215 // needed to insert the selection
1216 void LyXText::insertStringAsLines(string const & str)
1218 ParagraphList::iterator pit = cursorPar();
1219 pos_type pos = cursor.pos();
1220 ParagraphList::iterator endpit = boost::next(cursorPar());
1222 recUndo(cursor.par());
1224 // only to be sure, should not be neccessary
1227 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1229 redoParagraphs(cursorPar(), endpit);
1230 setCursor(cursorPar(), cursor.pos());
1231 selection.cursor = cursor;
1232 setCursor(pit, pos);
1237 // turns double-CR to single CR, others where converted into one
1238 // blank. Then InsertStringAsLines is called
1239 void LyXText::insertStringAsParagraphs(string const & str)
1241 string linestr(str);
1242 bool newline_inserted = false;
1243 string::size_type const siz = linestr.length();
1245 for (string::size_type i = 0; i < siz; ++i) {
1246 if (linestr[i] == '\n') {
1247 if (newline_inserted) {
1248 // we know that \r will be ignored by
1249 // InsertStringA. Of course, it is a dirty
1250 // trick, but it works...
1251 linestr[i - 1] = '\r';
1255 newline_inserted = true;
1257 } else if (IsPrintable(linestr[i])) {
1258 newline_inserted = false;
1261 insertStringAsLines(linestr);
1265 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1267 setCursor(parOffset(pit), pos);
1271 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1273 LyXCursor old_cursor = cursor;
1274 setCursorIntern(par, pos, setfont, boundary);
1275 return deleteEmptyParagraphMechanism(old_cursor);
1279 void LyXText::redoCursor()
1281 #warning maybe the same for selections?
1282 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1286 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1287 pos_type pos, bool boundary)
1289 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1293 cur.boundary(boundary);
1295 // no rows, no fun...
1296 if (ownerParagraphs().begin()->rows.empty())
1299 // get the cursor y position in text
1301 ParagraphList::iterator pit = getPar(par);
1302 Row const & row = *pit->getRow(pos);
1304 int y = pit->y + row.y_offset();
1306 // y is now the beginning of the cursor row
1307 y += row.baseline();
1308 // y is now the cursor baseline
1311 pos_type const end = row.endpos();
1313 // None of these should happen, but we're scaredy-cats
1315 lyxerr << "dont like -1" << endl;
1318 BOOST_ASSERT(false);
1319 } else if (pos > pit->size()) {
1320 lyxerr << "dont like 1, pos: " << pos
1321 << " size: " << pit->size()
1322 << " row.pos():" << row.pos()
1323 << " paroffset: " << par << endl;
1326 BOOST_ASSERT(false);
1327 } else if (pos > end) {
1328 lyxerr << "dont like 2 please report" << endl;
1329 // This shouldn't happen.
1332 BOOST_ASSERT(false);
1333 } else if (pos < row.pos()) {
1334 lyxerr << "dont like 3 please report pos:" << pos
1335 << " size: " << pit->size()
1336 << " row.pos():" << row.pos()
1337 << " paroffset: " << par << endl;
1340 BOOST_ASSERT(false);
1342 // now get the cursors x position
1343 cur.x(int(getCursorX(pit, row, pos, boundary)));
1347 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1348 pos_type pos, bool boundary) const
1350 pos_type cursor_vpos = 0;
1352 double fill_separator = row.fill_separator();
1353 double fill_hfill = row.fill_hfill();
1354 double fill_label_hfill = row.fill_label_hfill();
1355 pos_type const row_pos = row.pos();
1356 pos_type const end = row.endpos();
1359 cursor_vpos = row_pos;
1360 else if (pos >= end && !boundary)
1361 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1363 else if (pos > row_pos && (pos >= end || boundary))
1364 // Place cursor after char at (logical) position pos - 1
1365 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1366 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1368 // Place cursor before char at (logical) position pos
1369 cursor_vpos = (bidi.level(pos) % 2 == 0)
1370 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1372 pos_type body_pos = pit->beginOfBody();
1374 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1377 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1378 pos_type pos = bidi.vis2log(vpos);
1379 if (body_pos > 0 && pos == body_pos - 1) {
1380 x += fill_label_hfill
1381 + font_metrics::width(pit->layout()->labelsep,
1383 if (pit->isLineSeparator(body_pos - 1))
1384 x -= singleWidth(pit, body_pos - 1);
1387 if (hfillExpansion(*pit, row, pos)) {
1388 x += singleWidth(pit, pos);
1389 if (pos >= body_pos)
1392 x += fill_label_hfill;
1393 } else if (pit->isSeparator(pos)) {
1394 x += singleWidth(pit, pos);
1395 if (pos >= body_pos)
1396 x += fill_separator;
1398 x += singleWidth(pit, pos);
1404 void LyXText::setCursorIntern(paroffset_type par,
1405 pos_type pos, bool setfont, bool boundary)
1407 setCursor(cursor, par, pos, boundary);
1408 bv()->x_target(cursor.x() + xo_);
1414 void LyXText::setCurrentFont()
1416 pos_type pos = cursor.pos();
1417 ParagraphList::iterator pit = cursorPar();
1419 if (cursor.boundary() && pos > 0)
1423 if (pos == pit->size())
1425 else // potentional bug... BUG (Lgb)
1426 if (pit->isSeparator(pos)) {
1427 if (pos > pit->getRow(pos)->pos() &&
1428 bidi.level(pos) % 2 ==
1429 bidi.level(pos - 1) % 2)
1431 else if (pos + 1 < pit->size())
1436 BufferParams const & bufparams = bv()->buffer()->params();
1437 current_font = pit->getFontSettings(bufparams, pos);
1438 real_current_font = getFont(pit, pos);
1440 if (cursor.pos() == pit->size() &&
1441 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1442 !cursor.boundary()) {
1443 Language const * lang =
1444 pit->getParLanguage(bufparams);
1445 current_font.setLanguage(lang);
1446 current_font.setNumber(LyXFont::OFF);
1447 real_current_font.setLanguage(lang);
1448 real_current_font.setNumber(LyXFont::OFF);
1453 // returns the column near the specified x-coordinate of the row
1454 // x is set to the real beginning of this column
1455 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1456 Row const & row, int & x, bool & boundary) const
1458 double tmpx = row.x();
1459 double fill_separator = row.fill_separator();
1460 double fill_hfill = row.fill_hfill();
1461 double fill_label_hfill = row.fill_label_hfill();
1463 pos_type vc = row.pos();
1464 pos_type end = row.endpos();
1466 LyXLayout_ptr const & layout = pit->layout();
1468 bool left_side = false;
1470 pos_type body_pos = pit->beginOfBody();
1471 double last_tmpx = tmpx;
1475 !pit->isLineSeparator(body_pos - 1)))
1478 // check for empty row
1484 while (vc < end && tmpx <= x) {
1485 c = bidi.vis2log(vc);
1487 if (body_pos > 0 && c == body_pos - 1) {
1488 tmpx += fill_label_hfill +
1489 font_metrics::width(layout->labelsep, getLabelFont(pit));
1490 if (pit->isLineSeparator(body_pos - 1))
1491 tmpx -= singleWidth(pit, body_pos - 1);
1494 if (hfillExpansion(*pit, row, c)) {
1495 tmpx += singleWidth(pit, c);
1499 tmpx += fill_label_hfill;
1500 } else if (pit->isSeparator(c)) {
1501 tmpx += singleWidth(pit, c);
1503 tmpx += fill_separator;
1505 tmpx += singleWidth(pit, c);
1510 if ((tmpx + last_tmpx) / 2 > x) {
1515 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1518 // This (rtl_support test) is not needed, but gives
1519 // some speedup if rtl_support == false
1520 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1522 // If lastrow is false, we don't need to compute
1523 // the value of rtl.
1524 bool const rtl = (lastrow)
1525 ? pit->isRightToLeftPar(bv()->buffer()->params())
1528 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1529 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1531 else if (vc == row.pos()) {
1532 c = bidi.vis2log(vc);
1533 if (bidi.level(c) % 2 == 1)
1536 c = bidi.vis2log(vc - 1);
1537 bool const rtl = (bidi.level(c) % 2 == 1);
1538 if (left_side == rtl) {
1540 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1544 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1545 if (bidi.level(end -1) % 2 == 0)
1546 tmpx -= singleWidth(pit, end - 1);
1548 tmpx += singleWidth(pit, end - 1);
1558 void LyXText::setCursorFromCoordinates(int x, int y)
1560 LyXCursor old_cursor = cursor;
1561 setCursorFromCoordinates(cursor, x, y);
1563 deleteEmptyParagraphMechanism(old_cursor);
1566 //x,y are coordinates relative to this LyXText
1567 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1569 // Get the row first.
1570 ParagraphList::iterator pit;
1571 Row const & row = *getRowNearY(y, pit);
1572 y = pit->y + row.y_offset();
1575 pos_type const column = getColumnNearX(pit, row, x, bound);
1576 cur.par(parOffset(pit));
1577 cur.pos(row.pos() + column);
1579 cur.y(y + row.baseline());
1581 cur.boundary(bound);
1586 bool LyXText::checkAndActivateInset(bool front)
1588 if (cursor.pos() == cursorPar()->size())
1590 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1591 if (!isHighlyEditableInset(inset))
1593 inset->edit(bv(), front);
1598 DispatchResult LyXText::moveRight()
1600 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1601 return moveLeftIntern(false, true, false);
1603 return moveRightIntern(true, true, false);
1607 DispatchResult LyXText::moveLeft()
1609 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1610 return moveRightIntern(true, true, false);
1612 return moveLeftIntern(false, true, false);
1616 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1618 ParagraphList::iterator c_par = cursorPar();
1619 if (boost::next(c_par) == ownerParagraphs().end()
1620 && cursor.pos() >= c_par->size())
1621 return DispatchResult(false, FINISHED_RIGHT);
1622 if (activate_inset && checkAndActivateInset(front))
1623 return DispatchResult(true, true);
1627 return DispatchResult(true);
1631 DispatchResult LyXText::moveLeftIntern(bool front,
1632 bool activate_inset, bool selecting)
1634 if (cursor.par() == 0 && cursor.pos() <= 0)
1635 return DispatchResult(false, FINISHED);
1639 if (activate_inset && checkAndActivateInset(front))
1640 return DispatchResult(true, true);
1641 return DispatchResult(true);
1645 DispatchResult LyXText::moveUp()
1647 if (cursorRow() == firstRow())
1648 return DispatchResult(false, FINISHED_UP);
1651 return DispatchResult(true);
1655 DispatchResult LyXText::moveDown()
1657 if (cursorRow() == lastRow())
1658 return DispatchResult(false, FINISHED_DOWN);
1661 return DispatchResult(true);
1665 bool LyXText::cursorLeft(bool internal)
1667 if (cursor.pos() > 0) {
1668 bool boundary = cursor.boundary();
1669 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1670 if (!internal && !boundary &&
1671 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1672 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1676 if (cursor.par() != 0) {
1677 // steps into the paragraph above
1678 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1686 bool LyXText::cursorRight(bool internal)
1688 if (!internal && cursor.boundary()) {
1689 setCursor(cursor.par(), cursor.pos(), true, false);
1693 if (cursor.pos() != cursorPar()->size()) {
1694 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1695 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1697 setCursor(cursor.par(), cursor.pos(), true, true);
1701 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1702 setCursor(cursor.par() + 1, 0);
1710 void LyXText::cursorUp(bool selecting)
1712 Row const & row = *cursorRow();
1713 int x = bv()->x_target() - xo_;
1714 int y = cursor.y() - row.baseline() - 1;
1715 setCursorFromCoordinates(x, y);
1718 int y_abs = y + yo_ - bv()->top_y();
1719 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1720 if (inset_hit && isHighlyEditableInset(inset_hit))
1721 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1726 void LyXText::cursorDown(bool selecting)
1728 Row const & row = *cursorRow();
1729 int x = bv()->x_target() - xo_;
1730 int y = cursor.y() - row.baseline() + row.height() + 1;
1731 setCursorFromCoordinates(x, y);
1734 int y_abs = y + yo_ - bv()->top_y();
1735 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1736 if (inset_hit && isHighlyEditableInset(inset_hit))
1737 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1742 void LyXText::cursorUpParagraph()
1744 ParagraphList::iterator cpit = cursorPar();
1745 if (cursor.pos() > 0)
1747 else if (cpit != ownerParagraphs().begin())
1748 setCursor(boost::prior(cpit), 0);
1752 void LyXText::cursorDownParagraph()
1754 ParagraphList::iterator pit = cursorPar();
1755 ParagraphList::iterator next_pit = boost::next(pit);
1757 if (next_pit != ownerParagraphs().end())
1758 setCursor(next_pit, 0);
1760 setCursor(pit, pit->size());
1764 // fix the cursor `cur' after a characters has been deleted at `where'
1765 // position. Called by deleteEmptyParagraphMechanism
1766 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1768 // if cursor is not in the paragraph where the delete occured,
1770 if (cur.par() != where.par())
1773 // if cursor position is after the place where the delete occured,
1775 if (cur.pos() > where.pos())
1776 cur.pos(cur.pos()-1);
1778 // check also if we don't want to set the cursor on a spot behind the
1779 // pagragraph because we erased the last character.
1780 if (cur.pos() > getPar(cur)->size())
1781 cur.pos(getPar(cur)->size());
1783 // recompute row et al. for this cursor
1784 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1788 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1790 // Would be wrong to delete anything if we have a selection.
1791 if (selection.set())
1794 // Don't do anything if the cursor is invalid
1795 if (old_cursor.par() == -1)
1798 // We allow all kinds of "mumbo-jumbo" when freespacing.
1799 ParagraphList::iterator const old_pit = getPar(old_cursor);
1800 if (old_pit->isFreeSpacing())
1803 /* Ok I'll put some comments here about what is missing.
1804 I have fixed BackSpace (and thus Delete) to not delete
1805 double-spaces automagically. I have also changed Cut,
1806 Copy and Paste to hopefully do some sensible things.
1807 There are still some small problems that can lead to
1808 double spaces stored in the document file or space at
1809 the beginning of paragraphs. This happens if you have
1810 the cursor between to spaces and then save. Or if you
1811 cut and paste and the selection have a space at the
1812 beginning and then save right after the paste. I am
1813 sure none of these are very hard to fix, but I will
1814 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1815 that I can get some feedback. (Lgb)
1818 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1819 // delete the LineSeparator.
1822 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1823 // delete the LineSeparator.
1826 // If the pos around the old_cursor were spaces, delete one of them.
1827 if (old_cursor.par() != cursor.par()
1828 || old_cursor.pos() != cursor.pos()) {
1830 // Only if the cursor has really moved
1831 if (old_cursor.pos() > 0
1832 && old_cursor.pos() < old_pit->size()
1833 && old_pit->isLineSeparator(old_cursor.pos())
1834 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1835 bool erased = old_pit->erase(old_cursor.pos() - 1);
1836 redoParagraph(old_pit);
1840 #ifdef WITH_WARNINGS
1841 #warning This will not work anymore when we have multiple views of the same buffer
1842 // In this case, we will have to correct also the cursors held by
1843 // other bufferviews. It will probably be easier to do that in a more
1844 // automated way in LyXCursor code. (JMarc 26/09/2001)
1846 // correct all cursors held by the LyXText
1847 fixCursorAfterDelete(cursor, old_cursor);
1848 fixCursorAfterDelete(selection.cursor, old_cursor);
1849 fixCursorAfterDelete(selection.start, old_cursor);
1850 fixCursorAfterDelete(selection.end, old_cursor);
1855 // don't delete anything if this is the ONLY paragraph!
1856 if (ownerParagraphs().size() == 1)
1859 // Do not delete empty paragraphs with keepempty set.
1860 if (old_pit->allowEmpty())
1863 // only do our magic if we changed paragraph
1864 if (old_cursor.par() == cursor.par())
1867 // record if we have deleted a paragraph
1868 // we can't possibly have deleted a paragraph before this point
1869 bool deleted = false;
1871 if (old_pit->empty()
1872 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1873 // ok, we will delete something
1874 LyXCursor tmpcursor;
1878 bool selection_position_was_oldcursor_position =
1879 selection.cursor.par() == old_cursor.par()
1880 && selection.cursor.pos() == old_cursor.pos();
1883 cursor = old_cursor; // that undo can restore the right cursor position
1885 ParagraphList::iterator endpit = boost::next(old_pit);
1886 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1889 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1893 ParagraphList::iterator tmppit = cursorPar();
1895 ownerParagraphs().erase(old_pit);
1896 // update cursor par offset
1897 cursor.par(parOffset(tmppit));
1901 setCursorIntern(cursor.par(), cursor.pos());
1903 if (selection_position_was_oldcursor_position) {
1904 // correct selection
1905 selection.cursor = cursor;
1912 if (old_pit->stripLeadingSpaces()) {
1913 redoParagraph(old_pit);
1915 setCursorIntern(cursor.par(), cursor.pos());
1916 selection.cursor = cursor;
1922 ParagraphList & LyXText::ownerParagraphs() const
1924 return *paragraphs_;
1928 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1930 recordUndo(Undo::ATOMIC, this, first, last);
1934 void LyXText::recUndo(lyx::paroffset_type par) const
1936 recordUndo(Undo::ATOMIC, this, par, par);
1940 bool LyXText::isInInset() const
1942 // Sub-level has non-null bv owner and non-null inset owner.
1943 return inset_owner != 0;
1947 int defaultRowHeight()
1949 LyXFont const font(LyXFont::ALL_SANE);
1950 return int(font_metrics::maxHeight(font) * 1.2);