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), xo_(0), yo_(0),
81 LyXText & LyXText::operator=(LyXText const & lt)
83 // Copy all public variables
86 current_font = lt.current_font;
87 real_current_font = lt.real_current_font;
88 defaultfont_ = lt.defaultfont_;
89 inset_owner = lt.inset_owner;
90 bv_owner = lt.bv_owner;
92 in_inset_ = lt.in_inset_;
93 paragraphs_ = lt.paragraphs_;
97 // Copy all the private variables
99 // we cannot initailize a iterator with a singular iterator.
100 //cache_par_ = lt.cache_par_;
101 cache_pos_ = lt.cache_pos_;
107 void LyXText::init(BufferView * bview)
111 ParagraphList::iterator const beg = ownerParagraphs().begin();
112 ParagraphList::iterator const end = ownerParagraphs().end();
113 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
120 current_font = getFont(beg, 0);
122 redoParagraphs(beg, end);
123 setCursorIntern(0, 0);
124 selection.cursor = cursor;
130 // Gets the fully instantiated font at a given position in a paragraph
131 // Basically the same routine as Paragraph::getFont() in paragraph.C.
132 // The difference is that this one is used for displaying, and thus we
133 // are allowed to make cosmetic improvements. For instance make footnotes
135 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
137 BOOST_ASSERT(pos >= 0);
139 LyXLayout_ptr const & layout = pit->layout();
141 BufferParams const & params = bv()->buffer()->params();
142 pos_type const body_pos = pit->beginOfBody();
144 // We specialize the 95% common case:
145 if (!pit->getDepth()) {
146 LyXFont f = pit->getFontSettings(params, pos);
148 pit->inInset()->getDrawFont(f);
149 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
150 return f.realize(layout->reslabelfont);
152 return f.realize(layout->resfont);
155 // The uncommon case need not be optimized as much
158 layoutfont = layout->labelfont;
160 layoutfont = layout->font;
162 LyXFont font = pit->getFontSettings(params, pos);
163 font.realize(layoutfont);
166 pit->inInset()->getDrawFont(font);
168 // Realize with the fonts of lesser depth.
169 //font.realize(outerFont(pit, ownerParagraphs()));
170 font.realize(defaultfont_);
176 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
178 LyXLayout_ptr const & layout = pit->layout();
180 if (!pit->getDepth())
181 return layout->resfont;
183 LyXFont font = layout->font;
184 // Realize with the fonts of lesser depth.
185 //font.realize(outerFont(pit, ownerParagraphs()));
186 font.realize(defaultfont_);
192 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
194 LyXLayout_ptr const & layout = pit->layout();
196 if (!pit->getDepth())
197 return layout->reslabelfont;
199 LyXFont font = layout->labelfont;
200 // Realize with the fonts of lesser depth.
201 font.realize(outerFont(pit, ownerParagraphs()));
202 font.realize(defaultfont_);
208 void LyXText::setCharFont(ParagraphList::iterator pit,
209 pos_type pos, LyXFont const & fnt,
212 BufferParams const & params = bv()->buffer()->params();
213 LyXFont font = getFont(pit, pos);
214 font.update(fnt, params.language, toggleall);
215 // Let the insets convert their font
216 if (pit->isInset(pos)) {
217 InsetOld * inset = pit->getInset(pos);
218 if (isEditableInset(inset)) {
219 static_cast<UpdatableInset *>(inset)
220 ->setFont(bv(), fnt, toggleall, true);
224 // Plug through to version below:
225 setCharFont(pit, pos, font);
229 void LyXText::setCharFont(
230 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
233 LyXLayout_ptr const & layout = pit->layout();
235 // Get concrete layout font to reduce against
238 if (pos < pit->beginOfBody())
239 layoutfont = layout->labelfont;
241 layoutfont = layout->font;
243 // Realize against environment font information
244 if (pit->getDepth()) {
245 ParagraphList::iterator tp = pit;
246 while (!layoutfont.resolved() &&
247 tp != ownerParagraphs().end() &&
249 tp = outerHook(tp, ownerParagraphs());
250 if (tp != ownerParagraphs().end())
251 layoutfont.realize(tp->layout()->font);
255 layoutfont.realize(defaultfont_);
257 // Now, reduce font against full layout font
258 font.reduce(layoutfont);
260 pit->setFont(pos, font);
264 InsetOld * LyXText::getInset() const
266 ParagraphList::iterator pit = cursorPar();
267 pos_type const pos = cursor.pos();
269 if (pos < pit->size() && pit->isInset(pos)) {
270 return pit->getInset(pos);
276 void LyXText::toggleInset()
278 InsetOld * inset = getInset();
279 // is there an editable inset at cursor position?
280 if (!isEditableInset(inset)) {
281 // No, try to see if we are inside a collapsable inset
282 if (inset_owner && inset_owner->owner()
283 && inset_owner->owner()->isOpen()) {
285 inset_owner->owner()->close();
286 bv()->getLyXText()->cursorRight(true);
287 bv()->updateParagraphDialog();
291 //bv()->owner()->message(inset->editMessage());
293 // do we want to keep this?? (JMarc)
294 if (!isHighlyEditableInset(inset))
295 recUndo(cursor.par());
305 // Asger is not sure we want to do this...
306 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
309 LyXLayout_ptr const & layout = par.layout();
310 pos_type const psize = par.size();
313 for (pos_type pos = 0; pos < psize; ++pos) {
314 if (pos < par.beginOfBody())
315 layoutfont = layout->labelfont;
317 layoutfont = layout->font;
319 LyXFont tmpfont = par.getFontSettings(params, pos);
320 tmpfont.reduce(layoutfont);
321 par.setFont(pos, tmpfont);
326 ParagraphList::iterator
327 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
328 LyXCursor & send_cur,
329 string const & layout)
331 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
332 ParagraphList::iterator undoendpit = endpit;
333 ParagraphList::iterator pars_end = ownerParagraphs().end();
335 if (endpit != pars_end && endpit->getDepth()) {
336 while (endpit != pars_end && endpit->getDepth()) {
340 } else if (endpit != pars_end) {
341 // because of parindents etc.
345 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
347 // ok we have a selection. This is always between sstart_cur
348 // and sel_end cursor
350 ParagraphList::iterator pit = getPar(sstart_cur);
351 ParagraphList::iterator epit = boost::next(getPar(send_cur));
353 BufferParams const & bufparams = bv()->buffer()->params();
354 LyXLayout_ptr const & lyxlayout =
355 bufparams.getLyXTextClass()[layout];
358 pit->applyLayout(lyxlayout);
359 makeFontEntriesLayoutSpecific(bufparams, *pit);
360 pit->params().spaceTop(lyxlayout->fill_top ?
361 VSpace(VSpace::VFILL)
362 : VSpace(VSpace::NONE));
363 pit->params().spaceBottom(lyxlayout->fill_bottom ?
364 VSpace(VSpace::VFILL)
365 : VSpace(VSpace::NONE));
366 if (lyxlayout->margintype == MARGIN_MANUAL)
367 pit->setLabelWidthString(lyxlayout->labelstring());
368 cur.par(std::distance(ownerParagraphs().begin(), pit));
370 } while (pit != epit);
376 // set layout over selection and make a total rebreak of those paragraphs
377 void LyXText::setLayout(string const & layout)
379 LyXCursor tmpcursor = cursor; // store the current cursor
381 // if there is no selection just set the layout
382 // of the current paragraph
383 if (!selection.set()) {
384 selection.start = cursor; // dummy selection
385 selection.end = cursor;
388 // special handling of new environment insets
389 BufferParams const & params = bv()->buffer()->params();
390 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
391 if (lyxlayout->is_environment) {
392 // move everything in a new environment inset
393 lyxerr << "setting layout " << layout << endl;
394 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
395 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
396 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
397 InsetOld * inset = new InsetEnvironment(params, layout);
398 if (bv()->insertInset(inset)) {
400 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
407 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
408 selection.end, layout);
409 redoParagraphs(getPar(selection.start), endpit);
411 // we have to reset the selection, because the
412 // geometry could have changed
413 setCursor(selection.start.par(), selection.start.pos(), false);
414 selection.cursor = cursor;
415 setCursor(selection.end.par(), selection.end.pos(), false);
419 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
423 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
425 ParagraphList::iterator pit = cursorPar();
426 ParagraphList::iterator end = pit;
427 ParagraphList::iterator start = pit;
429 if (selection.set()) {
430 pit = getPar(selection.start);
431 end = getPar(selection.end);
435 ParagraphList::iterator pastend = boost::next(end);
438 recUndo(parOffset(start), parOffset(end));
440 bool changed = false;
442 int prev_after_depth = 0;
443 #warning parlist ... could be nicer ?
444 if (start != ownerParagraphs().begin()) {
445 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
449 int const depth = pit->params().depth();
450 if (type == bv_funcs::INC_DEPTH) {
451 if (depth < prev_after_depth
452 && pit->layout()->labeltype != LABEL_BIBLIO) {
455 pit->params().depth(depth + 1);
460 pit->params().depth(depth - 1);
463 prev_after_depth = pit->getMaxDepthAfter();
465 #warning SERIOUS: Uahh... does this mean we access end->getMaxDepthAfter?
476 redoParagraphs(start, pastend);
478 // We need to actually move the text->cursor. I don't
479 // understand why ...
480 LyXCursor tmpcursor = cursor;
482 // we have to reset the visual selection because the
483 // geometry could have changed
484 if (selection.set()) {
485 setCursor(selection.start.par(), selection.start.pos());
486 selection.cursor = cursor;
487 setCursor(selection.end.par(), selection.end.pos());
490 // this handles the counter labels, and also fixes up
491 // depth values for follow-on (child) paragraphs
495 setCursor(tmpcursor.par(), tmpcursor.pos());
501 // set font over selection and make a total rebreak of those paragraphs
502 void LyXText::setFont(LyXFont const & font, bool toggleall)
504 // if there is no selection just set the current_font
505 if (!selection.set()) {
506 // Determine basis font
508 if (cursor.pos() < cursorPar()->beginOfBody()) {
509 layoutfont = getLabelFont(cursorPar());
511 layoutfont = getLayoutFont(cursorPar());
513 // Update current font
514 real_current_font.update(font,
515 bv()->buffer()->params().language,
518 // Reduce to implicit settings
519 current_font = real_current_font;
520 current_font.reduce(layoutfont);
521 // And resolve it completely
522 real_current_font.realize(layoutfont);
527 LyXCursor tmpcursor = cursor; // store the current cursor
529 // ok we have a selection. This is always between sel_start_cursor
530 // and sel_end cursor
532 recUndo(selection.start.par(), selection.end.par());
534 cursor = selection.start;
535 while (cursor.par() != selection.end.par() ||
536 cursor.pos() < selection.end.pos())
538 if (cursor.pos() < cursorPar()->size()) {
539 // an open footnote should behave like a closed one
540 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
541 cursor.pos(cursor.pos() + 1);
544 cursor.par(cursor.par() + 1);
549 redoParagraph(getPar(selection.start));
551 // we have to reset the selection, because the
552 // geometry could have changed, but we keep
553 // it for user convenience
554 setCursor(selection.start.par(), selection.start.pos());
555 selection.cursor = cursor;
556 setCursor(selection.end.par(), selection.end.pos());
558 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
559 tmpcursor.boundary());
563 // important for the screen
566 // the cursor set functions have a special mechanism. When they
567 // realize, that you left an empty paragraph, they will delete it.
569 // need the selection cursor:
570 void LyXText::setSelection()
572 TextCursor::setSelection();
576 void LyXText::clearSelection()
578 TextCursor::clearSelection();
580 // reset this in the bv()!
581 if (bv() && bv()->text)
582 bv()->text->xsel_cache.set(false);
586 void LyXText::cursorHome()
588 ParagraphList::iterator cpit = cursorPar();
589 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
593 void LyXText::cursorEnd()
595 ParagraphList::iterator cpit = cursorPar();
596 pos_type end = cpit->getRow(cursor.pos())->endpos();
597 // if not on the last row of the par, put the cursor before
599 setCursor(cpit, end == cpit->size() ? end : end - 1);
603 void LyXText::cursorTop()
605 setCursor(ownerParagraphs().begin(), 0);
609 void LyXText::cursorBottom()
611 ParagraphList::iterator lastpit =
612 boost::prior(ownerParagraphs().end());
613 setCursor(lastpit, lastpit->size());
617 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
619 // If the mask is completely neutral, tell user
620 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
621 // Could only happen with user style
622 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
626 // Try implicit word selection
627 // If there is a change in the language the implicit word selection
629 LyXCursor resetCursor = cursor;
630 bool implicitSelection =
631 font.language() == ignore_language
632 && font.number() == LyXFont::IGNORE
633 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
636 setFont(font, toggleall);
638 // Implicit selections are cleared afterwards
639 //and cursor is set to the original position.
640 if (implicitSelection) {
642 cursor = resetCursor;
643 setCursor(cursorPar(), cursor.pos());
644 selection.cursor = cursor;
649 string LyXText::getStringToIndex()
651 // Try implicit word selection
652 // If there is a change in the language the implicit word selection
654 LyXCursor const reset_cursor = cursor;
655 bool const implicitSelection =
656 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
659 if (!selection.set())
660 bv()->owner()->message(_("Nothing to index!"));
661 else if (selection.start.par() != selection.end.par())
662 bv()->owner()->message(_("Cannot index more than one paragraph!"));
664 idxstring = selectionAsString(*bv()->buffer(), false);
666 // Reset cursors to their original position.
667 cursor = reset_cursor;
668 setCursor(cursorPar(), cursor.pos());
669 selection.cursor = cursor;
671 // Clear the implicit selection.
672 if (implicitSelection)
679 // the DTP switches for paragraphs. LyX will store them in the first
680 // physical paragraph. When a paragraph is broken, the top settings rest,
681 // the bottom settings are given to the new one. So I can make sure,
682 // they do not duplicate themself and you cannot play dirty tricks with
685 void LyXText::setParagraph(
686 VSpace const & space_top,
687 VSpace const & space_bottom,
688 Spacing const & spacing,
690 string const & labelwidthstring,
693 LyXCursor tmpcursor = cursor;
694 if (!selection.set()) {
695 selection.start = cursor;
696 selection.end = cursor;
699 // make sure that the depth behind the selection are restored, too
700 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
701 ParagraphList::iterator undoendpit = endpit;
702 ParagraphList::iterator pars_end = ownerParagraphs().end();
704 if (endpit != pars_end && endpit->getDepth()) {
705 while (endpit != pars_end && endpit->getDepth()) {
709 } else if (endpit != pars_end) {
710 // because of parindents etc.
714 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
716 int tmppit = selection.end.par();
718 while (tmppit != selection.start.par() - 1) {
719 setCursor(tmppit, 0);
721 ParagraphList::iterator const pit = cursorPar();
722 ParagraphParameters & params = pit->params();
723 params.spaceTop(space_top);
724 params.spaceBottom(space_bottom);
725 params.spacing(spacing);
727 // does the layout allow the new alignment?
728 LyXLayout_ptr const & layout = pit->layout();
730 if (align == LYX_ALIGN_LAYOUT)
731 align = layout->align;
732 if (align & layout->alignpossible) {
733 if (align == layout->align)
734 params.align(LYX_ALIGN_LAYOUT);
738 pit->setLabelWidthString(labelwidthstring);
739 params.noindent(noindent);
743 redoParagraphs(getPar(selection.start), endpit);
746 setCursor(selection.start.par(), selection.start.pos());
747 selection.cursor = cursor;
748 setCursor(selection.end.par(), selection.end.pos());
750 setCursor(tmpcursor.par(), tmpcursor.pos());
757 string expandLabel(LyXTextClass const & textclass,
758 LyXLayout_ptr const & layout, bool appendix)
760 string fmt = appendix ?
761 layout->labelstring_appendix() : layout->labelstring();
763 // handle 'inherited level parts' in 'fmt',
764 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
765 size_t const i = fmt.find('@', 0);
766 if (i != string::npos) {
767 size_t const j = fmt.find('@', i + 1);
768 if (j != string::npos) {
769 string parent(fmt, i + 1, j - i - 1);
770 string label = expandLabel(textclass, textclass[parent], appendix);
771 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
775 return textclass.counters().counterLabel(fmt);
779 void incrementItemDepth(ParagraphList::iterator pit,
780 ParagraphList::iterator first_pit)
782 int const cur_labeltype = pit->layout()->labeltype;
784 if (cur_labeltype != LABEL_ENUMERATE &&
785 cur_labeltype != LABEL_ITEMIZE)
788 int const cur_depth = pit->getDepth();
790 ParagraphList::iterator prev_pit = boost::prior(pit);
792 int const prev_depth = prev_pit->getDepth();
793 int const prev_labeltype = prev_pit->layout()->labeltype;
794 if (prev_depth == 0 && cur_depth > 0) {
795 if (prev_labeltype == cur_labeltype) {
796 pit->itemdepth = prev_pit->itemdepth + 1;
799 } else if (prev_depth < cur_depth) {
800 if (prev_labeltype == cur_labeltype) {
801 pit->itemdepth = prev_pit->itemdepth + 1;
804 } else if (prev_depth == cur_depth) {
805 if (prev_labeltype == cur_labeltype) {
806 pit->itemdepth = prev_pit->itemdepth;
810 if (prev_pit == first_pit)
818 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
819 ParagraphList::iterator firstpit,
825 int const cur_depth = pit->getDepth();
826 ParagraphList::iterator prev_pit = boost::prior(pit);
828 int const prev_depth = prev_pit->getDepth();
829 int const prev_labeltype = prev_pit->layout()->labeltype;
830 if (prev_depth <= cur_depth) {
831 if (prev_labeltype != LABEL_ENUMERATE) {
832 switch (pit->itemdepth) {
834 counters.reset("enumi");
836 counters.reset("enumii");
838 counters.reset("enumiii");
840 counters.reset("enumiv");
846 if (prev_pit == firstpit)
856 // set the counter of a paragraph. This includes the labels
857 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
859 BufferParams const & bufparams = buf.params();
860 LyXTextClass const & textclass = bufparams.getLyXTextClass();
861 LyXLayout_ptr const & layout = pit->layout();
862 ParagraphList::iterator first_pit = ownerParagraphs().begin();
863 Counters & counters = textclass.counters();
868 if (pit == first_pit) {
869 pit->params().appendix(pit->params().startOfAppendix());
871 pit->params().appendix(boost::prior(pit)->params().appendix());
872 if (!pit->params().appendix() &&
873 pit->params().startOfAppendix()) {
874 pit->params().appendix(true);
875 textclass.counters().reset();
878 // Maybe we have to increment the item depth.
879 incrementItemDepth(pit, first_pit);
882 // erase what was there before
883 pit->params().labelString(string());
885 if (layout->margintype == MARGIN_MANUAL) {
886 if (pit->params().labelWidthString().empty())
887 pit->setLabelWidthString(layout->labelstring());
889 pit->setLabelWidthString(string());
892 // is it a layout that has an automatic label?
893 if (layout->labeltype == LABEL_COUNTER) {
894 BufferParams const & bufparams = buf.params();
895 LyXTextClass const & textclass = bufparams.getLyXTextClass();
896 counters.step(layout->counter);
897 string label = expandLabel(textclass, layout, pit->params().appendix());
898 pit->params().labelString(label);
899 } else if (layout->labeltype == LABEL_ITEMIZE) {
900 // At some point of time we should do something more
901 // clever here, like:
902 // pit->params().labelString(
903 // bufparams.user_defined_bullet(pit->itemdepth).getText());
904 // for now, use a simple hardcoded label
906 switch (pit->itemdepth) {
921 pit->params().labelString(itemlabel);
922 } else if (layout->labeltype == LABEL_ENUMERATE) {
923 // Maybe we have to reset the enumeration counter.
924 resetEnumCounterIfNeeded(pit, first_pit, counters);
927 // Yes I know this is a really, really! bad solution
929 string enumcounter = "enum";
931 switch (pit->itemdepth) {
943 // not a valid enumdepth...
947 counters.step(enumcounter);
949 pit->params().labelString(counters.enumLabel(enumcounter));
950 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
951 counters.step("bibitem");
952 int number = counters.value("bibitem");
953 if (pit->bibitem()) {
954 pit->bibitem()->setCounter(number);
955 pit->params().labelString(layout->labelstring());
957 // In biblio should't be following counters but...
959 string s = buf.B_(layout->labelstring());
962 if (layout->labeltype == LABEL_SENSITIVE) {
963 ParagraphList::iterator end = ownerParagraphs().end();
964 ParagraphList::iterator tmppit = pit;
967 while (tmppit != end && tmppit->inInset()
968 // the single '=' is intended below
969 && (in = tmppit->inInset()->owner()))
971 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
972 in->lyxCode() == InsetOld::WRAP_CODE) {
976 Paragraph const * owner = &ownerPar(buf, in);
978 for ( ; tmppit != end; ++tmppit)
979 if (&*tmppit == owner)
987 if (in->lyxCode() == InsetOld::FLOAT_CODE)
988 type = static_cast<InsetFloat*>(in)->params().type;
989 else if (in->lyxCode() == InsetOld::WRAP_CODE)
990 type = static_cast<InsetWrap*>(in)->params().type;
994 Floating const & fl = textclass.floats().getType(type);
996 counters.step(fl.type());
998 // Doesn't work... yet.
999 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1001 // par->SetLayout(0);
1002 // s = layout->labelstring;
1003 s = _("Senseless: ");
1006 pit->params().labelString(s);
1012 // Updates all counters. Paragraphs with changed label string will be
1013 // not be rebroken as this is too expensive. The next round will get it
1015 void LyXText::updateCounters()
1018 bv()->buffer()->params().getLyXTextClass().counters().reset();
1020 ParagraphList::iterator beg = ownerParagraphs().begin();
1021 ParagraphList::iterator end = ownerParagraphs().end();
1022 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1023 size_t maxdepth = 0;
1025 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1027 if (pit->params().depth() > maxdepth)
1028 pit->params().depth(maxdepth);
1030 // setCounter can potentially change the labelString.
1031 setCounter(*bv()->buffer(), pit);
1036 void LyXText::insertInset(InsetOld * inset)
1038 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1041 recUndo(cursor.par());
1043 cursorPar()->insertInset(cursor.pos(), inset);
1044 // Just to rebreak and refresh correctly.
1045 // The character will not be inserted a second time
1046 insertChar(Paragraph::META_INSET);
1047 // If we enter a highly editable inset the cursor should be before
1048 // the inset. After an Undo LyX tries to call inset->edit(...)
1049 // and fails if the cursor is behind the inset and getInset
1050 // does not return the inset!
1051 if (isHighlyEditableInset(inset))
1058 void LyXText::cutSelection(bool doclear, bool realcut)
1060 // Stuff what we got on the clipboard. Even if there is no selection.
1062 // There is a problem with having the stuffing here in that the
1063 // larger the selection the slower LyX will get. This can be
1064 // solved by running the line below only when the selection has
1065 // finished. The solution used currently just works, to make it
1066 // faster we need to be more clever and probably also have more
1067 // calls to stuffClipboard. (Lgb)
1068 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1070 // This doesn't make sense, if there is no selection
1071 if (!selection.set())
1074 // OK, we have a selection. This is always between selection.start
1075 // and selection.end
1077 // make sure that the depth behind the selection are restored, too
1078 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1079 ParagraphList::iterator undoendpit = endpit;
1080 ParagraphList::iterator pars_end = ownerParagraphs().end();
1082 if (endpit != pars_end && endpit->getDepth()) {
1083 while (endpit != pars_end && endpit->getDepth()) {
1085 undoendpit = endpit;
1087 } else if (endpit != pars_end) {
1088 // because of parindents etc.
1092 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1094 endpit = getPar(selection.end.par());
1095 int endpos = selection.end.pos();
1097 BufferParams const & bufparams = bv()->buffer()->params();
1098 boost::tie(endpit, endpos) = realcut ?
1099 CutAndPaste::cutSelection(bufparams,
1101 getPar(selection.start.par()), endpit,
1102 selection.start.pos(), endpos,
1103 bufparams.textclass,
1105 : CutAndPaste::eraseSelection(bufparams,
1107 getPar(selection.start.par()), endpit,
1108 selection.start.pos(), endpos,
1110 // sometimes necessary
1112 getPar(selection.start.par())->stripLeadingSpaces();
1114 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1115 // cutSelection can invalidate the cursor so we need to set
1117 // we prefer the end for when tracking changes
1119 cursor.par(parOffset(endpit));
1121 // need a valid cursor. (Lgb)
1124 setCursor(cursorPar(), cursor.pos());
1125 selection.cursor = cursor;
1130 void LyXText::copySelection()
1132 // stuff the selection onto the X clipboard, from an explicit copy request
1133 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1135 // this doesnt make sense, if there is no selection
1136 if (!selection.set())
1139 // ok we have a selection. This is always between selection.start
1140 // and sel_end cursor
1142 // copy behind a space if there is one
1143 while (getPar(selection.start)->size() > selection.start.pos()
1144 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1145 && (selection.start.par() != selection.end.par()
1146 || selection.start.pos() < selection.end.pos()))
1147 selection.start.pos(selection.start.pos() + 1);
1149 CutAndPaste::copySelection(getPar(selection.start.par()),
1150 getPar(selection.end.par()),
1151 selection.start.pos(), selection.end.pos(),
1152 bv()->buffer()->params().textclass);
1156 void LyXText::pasteSelection(size_t sel_index)
1158 // this does not make sense, if there is nothing to paste
1159 if (!CutAndPaste::checkPastePossible())
1162 recUndo(cursor.par());
1164 ParagraphList::iterator endpit;
1169 boost::tie(ppp, endpit) =
1170 CutAndPaste::pasteSelection(*bv()->buffer(),
1172 cursorPar(), cursor.pos(),
1173 bv()->buffer()->params().textclass,
1175 bufferErrors(*bv()->buffer(), el);
1176 bv()->showErrorList(_("Paste"));
1178 redoParagraphs(cursorPar(), endpit);
1180 setCursor(cursor.par(), cursor.pos());
1183 selection.cursor = cursor;
1184 setCursor(ppp.first, ppp.second);
1190 void LyXText::setSelectionRange(lyx::pos_type length)
1195 selection.cursor = cursor;
1202 // simple replacing. The font of the first selected character is used
1203 void LyXText::replaceSelectionWithString(string const & str)
1205 recUndo(cursor.par());
1208 if (!selection.set()) { // create a dummy selection
1209 selection.end = cursor;
1210 selection.start = cursor;
1213 // Get font setting before we cut
1214 pos_type pos = selection.end.pos();
1215 LyXFont const font = getPar(selection.start)
1216 ->getFontSettings(bv()->buffer()->params(),
1217 selection.start.pos());
1219 // Insert the new string
1220 string::const_iterator cit = str.begin();
1221 string::const_iterator end = str.end();
1222 for (; cit != end; ++cit) {
1223 getPar(selection.end)->insertChar(pos, (*cit), font);
1227 // Cut the selection
1228 cutSelection(true, false);
1234 // needed to insert the selection
1235 void LyXText::insertStringAsLines(string const & str)
1237 ParagraphList::iterator pit = cursorPar();
1238 pos_type pos = cursor.pos();
1239 ParagraphList::iterator endpit = boost::next(cursorPar());
1241 recUndo(cursor.par());
1243 // only to be sure, should not be neccessary
1246 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1248 redoParagraphs(cursorPar(), endpit);
1249 setCursor(cursorPar(), cursor.pos());
1250 selection.cursor = cursor;
1251 setCursor(pit, pos);
1256 // turns double-CR to single CR, others where converted into one
1257 // blank. Then InsertStringAsLines is called
1258 void LyXText::insertStringAsParagraphs(string const & str)
1260 string linestr(str);
1261 bool newline_inserted = false;
1262 string::size_type const siz = linestr.length();
1264 for (string::size_type i = 0; i < siz; ++i) {
1265 if (linestr[i] == '\n') {
1266 if (newline_inserted) {
1267 // we know that \r will be ignored by
1268 // InsertStringA. Of course, it is a dirty
1269 // trick, but it works...
1270 linestr[i - 1] = '\r';
1274 newline_inserted = true;
1276 } else if (IsPrintable(linestr[i])) {
1277 newline_inserted = false;
1280 insertStringAsLines(linestr);
1284 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1286 setCursor(parOffset(pit), pos);
1290 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1292 LyXCursor old_cursor = cursor;
1293 setCursorIntern(par, pos, setfont, boundary);
1294 return deleteEmptyParagraphMechanism(old_cursor);
1298 void LyXText::redoCursor()
1300 #warning maybe the same for selections?
1301 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1305 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1306 pos_type pos, bool boundary)
1308 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1312 cur.boundary(boundary);
1314 // no rows, no fun...
1315 if (ownerParagraphs().begin()->rows.empty())
1318 // get the cursor y position in text
1320 ParagraphList::iterator pit = getPar(par);
1321 Row const & row = *pit->getRow(pos);
1323 int y = pit->y + row.y_offset();
1325 // y is now the beginning of the cursor row
1326 y += row.baseline();
1327 // y is now the cursor baseline
1330 pos_type const end = row.endpos();
1332 // None of these should happen, but we're scaredy-cats
1334 lyxerr << "dont like -1" << endl;
1337 BOOST_ASSERT(false);
1338 } else if (pos > pit->size()) {
1339 lyxerr << "dont like 1, pos: " << pos
1340 << " size: " << pit->size()
1341 << " row.pos():" << row.pos()
1342 << " paroffset: " << par << endl;
1345 BOOST_ASSERT(false);
1346 } else if (pos > end) {
1347 lyxerr << "dont like 2 please report" << endl;
1348 // This shouldn't happen.
1351 BOOST_ASSERT(false);
1352 } else if (pos < row.pos()) {
1353 lyxerr << "dont like 3 please report pos:" << pos
1354 << " size: " << pit->size()
1355 << " row.pos():" << row.pos()
1356 << " paroffset: " << par << endl;
1359 BOOST_ASSERT(false);
1361 // now get the cursors x position
1362 cur.x(int(getCursorX(pit, row, pos, boundary)));
1366 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1367 pos_type pos, bool boundary) const
1369 pos_type cursor_vpos = 0;
1371 double fill_separator = row.fill_separator();
1372 double fill_hfill = row.fill_hfill();
1373 double fill_label_hfill = row.fill_label_hfill();
1374 pos_type const row_pos = row.pos();
1375 pos_type const end = row.endpos();
1378 cursor_vpos = row_pos;
1379 else if (pos >= end && !boundary)
1380 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1382 else if (pos > row_pos && (pos >= end || boundary))
1383 // Place cursor after char at (logical) position pos - 1
1384 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1385 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1387 // Place cursor before char at (logical) position pos
1388 cursor_vpos = (bidi.level(pos) % 2 == 0)
1389 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1391 pos_type body_pos = pit->beginOfBody();
1393 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1396 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1397 pos_type pos = bidi.vis2log(vpos);
1398 if (body_pos > 0 && pos == body_pos - 1) {
1399 x += fill_label_hfill
1400 + font_metrics::width(pit->layout()->labelsep,
1402 if (pit->isLineSeparator(body_pos - 1))
1403 x -= singleWidth(pit, body_pos - 1);
1406 if (hfillExpansion(*pit, row, pos)) {
1407 x += singleWidth(pit, pos);
1408 if (pos >= body_pos)
1411 x += fill_label_hfill;
1412 } else if (pit->isSeparator(pos)) {
1413 x += singleWidth(pit, pos);
1414 if (pos >= body_pos)
1415 x += fill_separator;
1417 x += singleWidth(pit, pos);
1423 void LyXText::setCursorIntern(paroffset_type par,
1424 pos_type pos, bool setfont, bool boundary)
1426 setCursor(cursor, par, pos, boundary);
1427 bv()->x_target(cursor.x() + xo_);
1433 void LyXText::setCurrentFont()
1435 pos_type pos = cursor.pos();
1436 ParagraphList::iterator pit = cursorPar();
1438 if (cursor.boundary() && pos > 0)
1442 if (pos == pit->size())
1444 else // potentional bug... BUG (Lgb)
1445 if (pit->isSeparator(pos)) {
1446 if (pos > pit->getRow(pos)->pos() &&
1447 bidi.level(pos) % 2 ==
1448 bidi.level(pos - 1) % 2)
1450 else if (pos + 1 < pit->size())
1455 BufferParams const & bufparams = bv()->buffer()->params();
1456 current_font = pit->getFontSettings(bufparams, pos);
1457 real_current_font = getFont(pit, pos);
1459 if (cursor.pos() == pit->size() &&
1460 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1461 !cursor.boundary()) {
1462 Language const * lang =
1463 pit->getParLanguage(bufparams);
1464 current_font.setLanguage(lang);
1465 current_font.setNumber(LyXFont::OFF);
1466 real_current_font.setLanguage(lang);
1467 real_current_font.setNumber(LyXFont::OFF);
1472 // returns the column near the specified x-coordinate of the row
1473 // x is set to the real beginning of this column
1474 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1475 Row const & row, int & x, bool & boundary) const
1477 double tmpx = row.x();
1478 double fill_separator = row.fill_separator();
1479 double fill_hfill = row.fill_hfill();
1480 double fill_label_hfill = row.fill_label_hfill();
1482 pos_type vc = row.pos();
1483 pos_type end = row.endpos();
1485 LyXLayout_ptr const & layout = pit->layout();
1487 bool left_side = false;
1489 pos_type body_pos = pit->beginOfBody();
1490 double last_tmpx = tmpx;
1494 !pit->isLineSeparator(body_pos - 1)))
1497 // check for empty row
1503 while (vc < end && tmpx <= x) {
1504 c = bidi.vis2log(vc);
1506 if (body_pos > 0 && c == body_pos - 1) {
1507 tmpx += fill_label_hfill +
1508 font_metrics::width(layout->labelsep, getLabelFont(pit));
1509 if (pit->isLineSeparator(body_pos - 1))
1510 tmpx -= singleWidth(pit, body_pos - 1);
1513 if (hfillExpansion(*pit, row, c)) {
1514 tmpx += singleWidth(pit, c);
1518 tmpx += fill_label_hfill;
1519 } else if (pit->isSeparator(c)) {
1520 tmpx += singleWidth(pit, c);
1522 tmpx += fill_separator;
1524 tmpx += singleWidth(pit, c);
1529 if ((tmpx + last_tmpx) / 2 > x) {
1534 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1537 // This (rtl_support test) is not needed, but gives
1538 // some speedup if rtl_support == false
1539 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1541 // If lastrow is false, we don't need to compute
1542 // the value of rtl.
1543 bool const rtl = (lastrow)
1544 ? pit->isRightToLeftPar(bv()->buffer()->params())
1547 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1548 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1550 else if (vc == row.pos()) {
1551 c = bidi.vis2log(vc);
1552 if (bidi.level(c) % 2 == 1)
1555 c = bidi.vis2log(vc - 1);
1556 bool const rtl = (bidi.level(c) % 2 == 1);
1557 if (left_side == rtl) {
1559 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1563 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1564 if (bidi.level(end -1) % 2 == 0)
1565 tmpx -= singleWidth(pit, end - 1);
1567 tmpx += singleWidth(pit, end - 1);
1577 void LyXText::setCursorFromCoordinates(int x, int y)
1579 LyXCursor old_cursor = cursor;
1580 setCursorFromCoordinates(cursor, x, y);
1582 deleteEmptyParagraphMechanism(old_cursor);
1585 //x,y are coordinates relative to this LyXText
1586 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1588 // Get the row first.
1589 ParagraphList::iterator pit;
1590 Row const & row = *getRowNearY(y, pit);
1591 y = pit->y + row.y_offset();
1594 pos_type const column = getColumnNearX(pit, row, x, bound);
1595 cur.par(parOffset(pit));
1596 cur.pos(row.pos() + column);
1598 cur.y(y + row.baseline());
1600 cur.boundary(bound);
1605 bool LyXText::checkAndActivateInset(bool front)
1607 if (cursor.pos() == cursorPar()->size())
1609 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1610 if (!isHighlyEditableInset(inset))
1612 inset->edit(bv(), front);
1617 DispatchResult LyXText::moveRight()
1619 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1620 return moveLeftIntern(false, true, false);
1622 return moveRightIntern(true, true, false);
1626 DispatchResult LyXText::moveLeft()
1628 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1629 return moveRightIntern(true, true, false);
1631 return moveLeftIntern(false, true, false);
1635 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1637 ParagraphList::iterator c_par = cursorPar();
1638 if (boost::next(c_par) == ownerParagraphs().end()
1639 && cursor.pos() >= c_par->size())
1640 return DispatchResult(false, FINISHED_RIGHT);
1641 if (activate_inset && checkAndActivateInset(front))
1642 return DispatchResult(true, true);
1646 return DispatchResult(true);
1650 DispatchResult LyXText::moveLeftIntern(bool front,
1651 bool activate_inset, bool selecting)
1653 if (cursor.par() == 0 && cursor.pos() <= 0)
1654 return DispatchResult(false, FINISHED);
1658 if (activate_inset && checkAndActivateInset(front))
1659 return DispatchResult(true, true);
1660 return DispatchResult(true);
1664 DispatchResult LyXText::moveUp()
1666 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1667 return DispatchResult(false, FINISHED_UP);
1670 return DispatchResult(true);
1674 DispatchResult LyXText::moveDown()
1676 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1677 return DispatchResult(false, FINISHED_DOWN);
1680 return DispatchResult(true);
1684 bool LyXText::cursorLeft(bool internal)
1686 if (cursor.pos() > 0) {
1687 bool boundary = cursor.boundary();
1688 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1689 if (!internal && !boundary &&
1690 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1691 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1695 if (cursor.par() != 0) {
1696 // steps into the paragraph above
1697 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1705 bool LyXText::cursorRight(bool internal)
1707 if (!internal && cursor.boundary()) {
1708 setCursor(cursor.par(), cursor.pos(), true, false);
1712 if (cursor.pos() != cursorPar()->size()) {
1713 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1714 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1716 setCursor(cursor.par(), cursor.pos(), true, true);
1720 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1721 setCursor(cursor.par() + 1, 0);
1729 void LyXText::cursorUp(bool selecting)
1731 Row const & row = *cursorRow();
1732 int x = bv()->x_target() - xo_;
1733 int y = cursor.y() - row.baseline() - 1;
1734 setCursorFromCoordinates(x, y);
1737 int y_abs = y + yo_ - bv()->top_y();
1738 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1739 if (inset_hit && isHighlyEditableInset(inset_hit))
1740 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1745 void LyXText::cursorDown(bool selecting)
1747 Row const & row = *cursorRow();
1748 int x = bv()->x_target() - xo_;
1749 int y = cursor.y() - row.baseline() + row.height() + 1;
1750 setCursorFromCoordinates(x, y);
1753 int y_abs = y + yo_ - bv()->top_y();
1754 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1755 if (inset_hit && isHighlyEditableInset(inset_hit))
1756 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1761 void LyXText::cursorUpParagraph()
1763 ParagraphList::iterator cpit = cursorPar();
1764 if (cursor.pos() > 0)
1766 else if (cpit != ownerParagraphs().begin())
1767 setCursor(boost::prior(cpit), 0);
1771 void LyXText::cursorDownParagraph()
1773 ParagraphList::iterator pit = cursorPar();
1774 ParagraphList::iterator next_pit = boost::next(pit);
1776 if (next_pit != ownerParagraphs().end())
1777 setCursor(next_pit, 0);
1779 setCursor(pit, pit->size());
1783 // fix the cursor `cur' after a characters has been deleted at `where'
1784 // position. Called by deleteEmptyParagraphMechanism
1785 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1787 // if cursor is not in the paragraph where the delete occured,
1789 if (cur.par() != where.par())
1792 // if cursor position is after the place where the delete occured,
1794 if (cur.pos() > where.pos())
1795 cur.pos(cur.pos()-1);
1797 // check also if we don't want to set the cursor on a spot behind the
1798 // pagragraph because we erased the last character.
1799 if (cur.pos() > getPar(cur)->size())
1800 cur.pos(getPar(cur)->size());
1802 // recompute row et al. for this cursor
1803 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1807 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1809 // Would be wrong to delete anything if we have a selection.
1810 if (selection.set())
1813 // Don't do anything if the cursor is invalid
1814 if (old_cursor.par() == -1)
1817 // We allow all kinds of "mumbo-jumbo" when freespacing.
1818 ParagraphList::iterator const old_pit = getPar(old_cursor);
1819 if (old_pit->isFreeSpacing())
1822 /* Ok I'll put some comments here about what is missing.
1823 I have fixed BackSpace (and thus Delete) to not delete
1824 double-spaces automagically. I have also changed Cut,
1825 Copy and Paste to hopefully do some sensible things.
1826 There are still some small problems that can lead to
1827 double spaces stored in the document file or space at
1828 the beginning of paragraphs. This happens if you have
1829 the cursor between to spaces and then save. Or if you
1830 cut and paste and the selection have a space at the
1831 beginning and then save right after the paste. I am
1832 sure none of these are very hard to fix, but I will
1833 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1834 that I can get some feedback. (Lgb)
1837 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1838 // delete the LineSeparator.
1841 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1842 // delete the LineSeparator.
1845 // If the pos around the old_cursor were spaces, delete one of them.
1846 if (old_cursor.par() != cursor.par()
1847 || old_cursor.pos() != cursor.pos()) {
1849 // Only if the cursor has really moved
1850 if (old_cursor.pos() > 0
1851 && old_cursor.pos() < old_pit->size()
1852 && old_pit->isLineSeparator(old_cursor.pos())
1853 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1854 bool erased = old_pit->erase(old_cursor.pos() - 1);
1855 redoParagraph(old_pit);
1859 #ifdef WITH_WARNINGS
1860 #warning This will not work anymore when we have multiple views of the same buffer
1861 // In this case, we will have to correct also the cursors held by
1862 // other bufferviews. It will probably be easier to do that in a more
1863 // automated way in LyXCursor code. (JMarc 26/09/2001)
1865 // correct all cursors held by the LyXText
1866 fixCursorAfterDelete(cursor, old_cursor);
1867 fixCursorAfterDelete(selection.cursor, old_cursor);
1868 fixCursorAfterDelete(selection.start, old_cursor);
1869 fixCursorAfterDelete(selection.end, old_cursor);
1874 // don't delete anything if this is the ONLY paragraph!
1875 if (ownerParagraphs().size() == 1)
1878 // Do not delete empty paragraphs with keepempty set.
1879 if (old_pit->allowEmpty())
1882 // only do our magic if we changed paragraph
1883 if (old_cursor.par() == cursor.par())
1886 // record if we have deleted a paragraph
1887 // we can't possibly have deleted a paragraph before this point
1888 bool deleted = false;
1890 if (old_pit->empty()
1891 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1892 // ok, we will delete something
1893 LyXCursor tmpcursor;
1897 bool selection_position_was_oldcursor_position =
1898 selection.cursor.par() == old_cursor.par()
1899 && selection.cursor.pos() == old_cursor.pos();
1902 cursor = old_cursor; // that undo can restore the right cursor position
1904 ParagraphList::iterator endpit = boost::next(old_pit);
1905 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1908 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1912 ParagraphList::iterator tmppit = cursorPar();
1914 ownerParagraphs().erase(old_pit);
1915 // update cursor par offset
1916 cursor.par(parOffset(tmppit));
1920 setCursorIntern(cursor.par(), cursor.pos());
1922 if (selection_position_was_oldcursor_position) {
1923 // correct selection
1924 selection.cursor = cursor;
1931 if (old_pit->stripLeadingSpaces()) {
1932 redoParagraph(old_pit);
1934 setCursorIntern(cursor.par(), cursor.pos());
1935 selection.cursor = cursor;
1941 ParagraphList & LyXText::ownerParagraphs() const
1943 return *paragraphs_;
1947 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1949 recordUndo(Undo::ATOMIC, this, first, last);
1953 void LyXText::recUndo(lyx::paroffset_type par) const
1955 recordUndo(Undo::ATOMIC, this, par, par);
1959 bool LyXText::isInInset() const
1961 // Sub-level has non-null bv owner and non-null inset owner.
1962 return inset_owner != 0;
1966 int defaultRowHeight()
1968 LyXFont const font(LyXFont::ALL_SANE);
1969 return int(font_metrics::maxHeight(font) * 1.2);