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 rebroken
1013 void LyXText::updateCounters()
1016 bv()->buffer()->params().getLyXTextClass().counters().reset();
1018 ParagraphList::iterator beg = ownerParagraphs().begin();
1019 ParagraphList::iterator end = ownerParagraphs().end();
1020 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1021 string const oldLabel = pit->params().labelString();
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);
1033 string const & newLabel = pit->params().labelString();
1035 if (oldLabel != newLabel)
1041 void LyXText::insertInset(InsetOld * inset)
1043 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1046 recUndo(cursor.par());
1048 cursorPar()->insertInset(cursor.pos(), inset);
1049 // Just to rebreak and refresh correctly.
1050 // The character will not be inserted a second time
1051 insertChar(Paragraph::META_INSET);
1052 // If we enter a highly editable inset the cursor should be before
1053 // the inset. After an Undo LyX tries to call inset->edit(...)
1054 // and fails if the cursor is behind the inset and getInset
1055 // does not return the inset!
1056 if (isHighlyEditableInset(inset))
1063 void LyXText::cutSelection(bool doclear, bool realcut)
1065 // Stuff what we got on the clipboard. Even if there is no selection.
1067 // There is a problem with having the stuffing here in that the
1068 // larger the selection the slower LyX will get. This can be
1069 // solved by running the line below only when the selection has
1070 // finished. The solution used currently just works, to make it
1071 // faster we need to be more clever and probably also have more
1072 // calls to stuffClipboard. (Lgb)
1073 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1075 // This doesn't make sense, if there is no selection
1076 if (!selection.set())
1079 // OK, we have a selection. This is always between selection.start
1080 // and selection.end
1082 // make sure that the depth behind the selection are restored, too
1083 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1084 ParagraphList::iterator undoendpit = endpit;
1085 ParagraphList::iterator pars_end = ownerParagraphs().end();
1087 if (endpit != pars_end && endpit->getDepth()) {
1088 while (endpit != pars_end && endpit->getDepth()) {
1090 undoendpit = endpit;
1092 } else if (endpit != pars_end) {
1093 // because of parindents etc.
1097 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1099 endpit = getPar(selection.end.par());
1100 int endpos = selection.end.pos();
1102 BufferParams const & bufparams = bv()->buffer()->params();
1103 boost::tie(endpit, endpos) = realcut ?
1104 CutAndPaste::cutSelection(bufparams,
1106 getPar(selection.start.par()), endpit,
1107 selection.start.pos(), endpos,
1108 bufparams.textclass,
1110 : CutAndPaste::eraseSelection(bufparams,
1112 getPar(selection.start.par()), endpit,
1113 selection.start.pos(), endpos,
1115 // sometimes necessary
1117 getPar(selection.start.par())->stripLeadingSpaces();
1119 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1120 // cutSelection can invalidate the cursor so we need to set
1122 // we prefer the end for when tracking changes
1124 cursor.par(parOffset(endpit));
1126 // need a valid cursor. (Lgb)
1129 setCursor(cursorPar(), cursor.pos());
1130 selection.cursor = cursor;
1135 void LyXText::copySelection()
1137 // stuff the selection onto the X clipboard, from an explicit copy request
1138 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1140 // this doesnt make sense, if there is no selection
1141 if (!selection.set())
1144 // ok we have a selection. This is always between selection.start
1145 // and sel_end cursor
1147 // copy behind a space if there is one
1148 while (getPar(selection.start)->size() > selection.start.pos()
1149 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1150 && (selection.start.par() != selection.end.par()
1151 || selection.start.pos() < selection.end.pos()))
1152 selection.start.pos(selection.start.pos() + 1);
1154 CutAndPaste::copySelection(getPar(selection.start.par()),
1155 getPar(selection.end.par()),
1156 selection.start.pos(), selection.end.pos(),
1157 bv()->buffer()->params().textclass);
1161 void LyXText::pasteSelection(size_t sel_index)
1163 // this does not make sense, if there is nothing to paste
1164 if (!CutAndPaste::checkPastePossible())
1167 recUndo(cursor.par());
1169 ParagraphList::iterator endpit;
1174 boost::tie(ppp, endpit) =
1175 CutAndPaste::pasteSelection(*bv()->buffer(),
1177 cursorPar(), cursor.pos(),
1178 bv()->buffer()->params().textclass,
1180 bufferErrors(*bv()->buffer(), el);
1181 bv()->showErrorList(_("Paste"));
1183 redoParagraphs(cursorPar(), endpit);
1185 setCursor(cursor.par(), cursor.pos());
1188 selection.cursor = cursor;
1189 setCursor(ppp.first, ppp.second);
1195 void LyXText::setSelectionRange(lyx::pos_type length)
1200 selection.cursor = cursor;
1207 // simple replacing. The font of the first selected character is used
1208 void LyXText::replaceSelectionWithString(string const & str)
1210 recUndo(cursor.par());
1213 if (!selection.set()) { // create a dummy selection
1214 selection.end = cursor;
1215 selection.start = cursor;
1218 // Get font setting before we cut
1219 pos_type pos = selection.end.pos();
1220 LyXFont const font = getPar(selection.start)
1221 ->getFontSettings(bv()->buffer()->params(),
1222 selection.start.pos());
1224 // Insert the new string
1225 string::const_iterator cit = str.begin();
1226 string::const_iterator end = str.end();
1227 for (; cit != end; ++cit) {
1228 getPar(selection.end)->insertChar(pos, (*cit), font);
1232 // Cut the selection
1233 cutSelection(true, false);
1239 // needed to insert the selection
1240 void LyXText::insertStringAsLines(string const & str)
1242 ParagraphList::iterator pit = cursorPar();
1243 pos_type pos = cursor.pos();
1244 ParagraphList::iterator endpit = boost::next(cursorPar());
1246 recUndo(cursor.par());
1248 // only to be sure, should not be neccessary
1251 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1253 redoParagraphs(cursorPar(), endpit);
1254 setCursor(cursorPar(), cursor.pos());
1255 selection.cursor = cursor;
1256 setCursor(pit, pos);
1261 // turns double-CR to single CR, others where converted into one
1262 // blank. Then InsertStringAsLines is called
1263 void LyXText::insertStringAsParagraphs(string const & str)
1265 string linestr(str);
1266 bool newline_inserted = false;
1267 string::size_type const siz = linestr.length();
1269 for (string::size_type i = 0; i < siz; ++i) {
1270 if (linestr[i] == '\n') {
1271 if (newline_inserted) {
1272 // we know that \r will be ignored by
1273 // InsertStringA. Of course, it is a dirty
1274 // trick, but it works...
1275 linestr[i - 1] = '\r';
1279 newline_inserted = true;
1281 } else if (IsPrintable(linestr[i])) {
1282 newline_inserted = false;
1285 insertStringAsLines(linestr);
1289 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1291 setCursor(parOffset(pit), pos);
1295 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1297 LyXCursor old_cursor = cursor;
1298 setCursorIntern(par, pos, setfont, boundary);
1299 return deleteEmptyParagraphMechanism(old_cursor);
1303 void LyXText::redoCursor()
1305 #warning maybe the same for selections?
1306 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1310 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1311 pos_type pos, bool boundary)
1313 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1317 cur.boundary(boundary);
1319 // no rows, no fun...
1320 if (ownerParagraphs().begin()->rows.empty())
1323 // get the cursor y position in text
1325 ParagraphList::iterator pit = getPar(par);
1326 Row const & row = *pit->getRow(pos);
1328 int y = pit->y + row.y_offset();
1330 // y is now the beginning of the cursor row
1331 y += row.baseline();
1332 // y is now the cursor baseline
1335 pos_type const end = row.endpos();
1337 // None of these should happen, but we're scaredy-cats
1339 lyxerr << "dont like -1" << endl;
1342 BOOST_ASSERT(false);
1343 } else if (pos > pit->size()) {
1344 lyxerr << "dont like 1, pos: " << pos
1345 << " size: " << pit->size()
1346 << " row.pos():" << row.pos()
1347 << " paroffset: " << par << endl;
1350 BOOST_ASSERT(false);
1351 } else if (pos > end) {
1352 lyxerr << "dont like 2 please report" << endl;
1353 // This shouldn't happen.
1356 BOOST_ASSERT(false);
1357 } else if (pos < row.pos()) {
1358 lyxerr << "dont like 3 please report pos:" << pos
1359 << " size: " << pit->size()
1360 << " row.pos():" << row.pos()
1361 << " paroffset: " << par << endl;
1364 BOOST_ASSERT(false);
1366 // now get the cursors x position
1367 cur.x(int(getCursorX(pit, row, pos, boundary)));
1371 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1372 pos_type pos, bool boundary) const
1374 pos_type cursor_vpos = 0;
1376 double fill_separator = row.fill_separator();
1377 double fill_hfill = row.fill_hfill();
1378 double fill_label_hfill = row.fill_label_hfill();
1379 pos_type const row_pos = row.pos();
1380 pos_type const end = row.endpos();
1383 cursor_vpos = row_pos;
1384 else if (pos >= end && !boundary)
1385 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1387 else if (pos > row_pos && (pos >= end || boundary))
1388 // Place cursor after char at (logical) position pos - 1
1389 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1390 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1392 // Place cursor before char at (logical) position pos
1393 cursor_vpos = (bidi.level(pos) % 2 == 0)
1394 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1396 pos_type body_pos = pit->beginOfBody();
1398 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1401 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1402 pos_type pos = bidi.vis2log(vpos);
1403 if (body_pos > 0 && pos == body_pos - 1) {
1404 x += fill_label_hfill
1405 + font_metrics::width(pit->layout()->labelsep,
1407 if (pit->isLineSeparator(body_pos - 1))
1408 x -= singleWidth(pit, body_pos - 1);
1411 if (hfillExpansion(*pit, row, pos)) {
1412 x += singleWidth(pit, pos);
1413 if (pos >= body_pos)
1416 x += fill_label_hfill;
1417 } else if (pit->isSeparator(pos)) {
1418 x += singleWidth(pit, pos);
1419 if (pos >= body_pos)
1420 x += fill_separator;
1422 x += singleWidth(pit, pos);
1428 void LyXText::setCursorIntern(paroffset_type par,
1429 pos_type pos, bool setfont, bool boundary)
1431 setCursor(cursor, par, pos, boundary);
1432 bv()->x_target(cursor.x() + xo_);
1438 void LyXText::setCurrentFont()
1440 pos_type pos = cursor.pos();
1441 ParagraphList::iterator pit = cursorPar();
1443 if (cursor.boundary() && pos > 0)
1447 if (pos == pit->size())
1449 else // potentional bug... BUG (Lgb)
1450 if (pit->isSeparator(pos)) {
1451 if (pos > pit->getRow(pos)->pos() &&
1452 bidi.level(pos) % 2 ==
1453 bidi.level(pos - 1) % 2)
1455 else if (pos + 1 < pit->size())
1460 BufferParams const & bufparams = bv()->buffer()->params();
1461 current_font = pit->getFontSettings(bufparams, pos);
1462 real_current_font = getFont(pit, pos);
1464 if (cursor.pos() == pit->size() &&
1465 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1466 !cursor.boundary()) {
1467 Language const * lang =
1468 pit->getParLanguage(bufparams);
1469 current_font.setLanguage(lang);
1470 current_font.setNumber(LyXFont::OFF);
1471 real_current_font.setLanguage(lang);
1472 real_current_font.setNumber(LyXFont::OFF);
1477 // returns the column near the specified x-coordinate of the row
1478 // x is set to the real beginning of this column
1479 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1480 Row const & row, int & x, bool & boundary) const
1482 double tmpx = row.x();
1483 double fill_separator = row.fill_separator();
1484 double fill_hfill = row.fill_hfill();
1485 double fill_label_hfill = row.fill_label_hfill();
1487 pos_type vc = row.pos();
1488 pos_type end = row.endpos();
1490 LyXLayout_ptr const & layout = pit->layout();
1492 bool left_side = false;
1494 pos_type body_pos = pit->beginOfBody();
1495 double last_tmpx = tmpx;
1499 !pit->isLineSeparator(body_pos - 1)))
1502 // check for empty row
1508 while (vc < end && tmpx <= x) {
1509 c = bidi.vis2log(vc);
1511 if (body_pos > 0 && c == body_pos - 1) {
1512 tmpx += fill_label_hfill +
1513 font_metrics::width(layout->labelsep, getLabelFont(pit));
1514 if (pit->isLineSeparator(body_pos - 1))
1515 tmpx -= singleWidth(pit, body_pos - 1);
1518 if (hfillExpansion(*pit, row, c)) {
1519 tmpx += singleWidth(pit, c);
1523 tmpx += fill_label_hfill;
1524 } else if (pit->isSeparator(c)) {
1525 tmpx += singleWidth(pit, c);
1527 tmpx += fill_separator;
1529 tmpx += singleWidth(pit, c);
1534 if ((tmpx + last_tmpx) / 2 > x) {
1539 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1542 // This (rtl_support test) is not needed, but gives
1543 // some speedup if rtl_support == false
1544 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1546 // If lastrow is false, we don't need to compute
1547 // the value of rtl.
1548 bool const rtl = (lastrow)
1549 ? pit->isRightToLeftPar(bv()->buffer()->params())
1552 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1553 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1555 else if (vc == row.pos()) {
1556 c = bidi.vis2log(vc);
1557 if (bidi.level(c) % 2 == 1)
1560 c = bidi.vis2log(vc - 1);
1561 bool const rtl = (bidi.level(c) % 2 == 1);
1562 if (left_side == rtl) {
1564 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1568 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1569 if (bidi.level(end -1) % 2 == 0)
1570 tmpx -= singleWidth(pit, end - 1);
1572 tmpx += singleWidth(pit, end - 1);
1582 void LyXText::setCursorFromCoordinates(int x, int y)
1584 LyXCursor old_cursor = cursor;
1585 setCursorFromCoordinates(cursor, x, y);
1587 deleteEmptyParagraphMechanism(old_cursor);
1590 //x,y are coordinates relative to this LyXText
1591 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1593 // Get the row first.
1594 ParagraphList::iterator pit;
1595 Row const & row = *getRowNearY(y, pit);
1596 y = pit->y + row.y_offset();
1599 pos_type const column = getColumnNearX(pit, row, x, bound);
1600 cur.par(parOffset(pit));
1601 cur.pos(row.pos() + column);
1603 cur.y(y + row.baseline());
1605 cur.boundary(bound);
1610 bool LyXText::checkAndActivateInset(bool front)
1612 if (cursor.pos() == cursorPar()->size())
1614 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1615 if (!isHighlyEditableInset(inset))
1617 inset->edit(bv(), front);
1622 DispatchResult LyXText::moveRight()
1624 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1625 return moveLeftIntern(false, true, false);
1627 return moveRightIntern(true, true, false);
1631 DispatchResult LyXText::moveLeft()
1633 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1634 return moveRightIntern(true, true, false);
1636 return moveLeftIntern(false, true, false);
1640 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1642 ParagraphList::iterator c_par = cursorPar();
1643 if (boost::next(c_par) == ownerParagraphs().end()
1644 && cursor.pos() >= c_par->size())
1645 return DispatchResult(false, FINISHED_RIGHT);
1646 if (activate_inset && checkAndActivateInset(front))
1647 return DispatchResult(true, true);
1651 return DispatchResult(true);
1655 DispatchResult LyXText::moveLeftIntern(bool front,
1656 bool activate_inset, bool selecting)
1658 if (cursor.par() == 0 && cursor.pos() <= 0)
1659 return DispatchResult(false, FINISHED);
1663 if (activate_inset && checkAndActivateInset(front))
1664 return DispatchResult(true, true);
1665 return DispatchResult(true);
1669 DispatchResult LyXText::moveUp()
1671 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1672 return DispatchResult(false, FINISHED_UP);
1675 return DispatchResult(true);
1679 DispatchResult LyXText::moveDown()
1681 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1682 return DispatchResult(false, FINISHED_DOWN);
1685 return DispatchResult(true);
1689 bool LyXText::cursorLeft(bool internal)
1691 if (cursor.pos() > 0) {
1692 bool boundary = cursor.boundary();
1693 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1694 if (!internal && !boundary &&
1695 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1696 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1700 if (cursor.par() != 0) {
1701 // steps into the paragraph above
1702 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1710 bool LyXText::cursorRight(bool internal)
1712 if (!internal && cursor.boundary()) {
1713 setCursor(cursor.par(), cursor.pos(), true, false);
1717 if (cursor.pos() != cursorPar()->size()) {
1718 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1719 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1721 setCursor(cursor.par(), cursor.pos(), true, true);
1725 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1726 setCursor(cursor.par() + 1, 0);
1734 void LyXText::cursorUp(bool selecting)
1736 Row const & row = *cursorRow();
1737 int x = bv()->x_target() - xo_;
1738 int y = cursor.y() - row.baseline() - 1;
1739 setCursorFromCoordinates(x, y);
1742 int y_abs = y + yo_ - bv()->top_y();
1743 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1744 if (inset_hit && isHighlyEditableInset(inset_hit))
1745 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1750 void LyXText::cursorDown(bool selecting)
1752 Row const & row = *cursorRow();
1753 int x = bv()->x_target() - xo_;
1754 int y = cursor.y() - row.baseline() + row.height() + 1;
1755 setCursorFromCoordinates(x, y);
1758 int y_abs = y + yo_ - bv()->top_y();
1759 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1760 if (inset_hit && isHighlyEditableInset(inset_hit))
1761 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1766 void LyXText::cursorUpParagraph()
1768 ParagraphList::iterator cpit = cursorPar();
1769 if (cursor.pos() > 0)
1771 else if (cpit != ownerParagraphs().begin())
1772 setCursor(boost::prior(cpit), 0);
1776 void LyXText::cursorDownParagraph()
1778 ParagraphList::iterator pit = cursorPar();
1779 ParagraphList::iterator next_pit = boost::next(pit);
1781 if (next_pit != ownerParagraphs().end())
1782 setCursor(next_pit, 0);
1784 setCursor(pit, pit->size());
1788 // fix the cursor `cur' after a characters has been deleted at `where'
1789 // position. Called by deleteEmptyParagraphMechanism
1790 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1792 // if cursor is not in the paragraph where the delete occured,
1794 if (cur.par() != where.par())
1797 // if cursor position is after the place where the delete occured,
1799 if (cur.pos() > where.pos())
1800 cur.pos(cur.pos()-1);
1802 // check also if we don't want to set the cursor on a spot behind the
1803 // pagragraph because we erased the last character.
1804 if (cur.pos() > getPar(cur)->size())
1805 cur.pos(getPar(cur)->size());
1807 // recompute row et al. for this cursor
1808 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1812 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1814 // Would be wrong to delete anything if we have a selection.
1815 if (selection.set())
1818 // Don't do anything if the cursor is invalid
1819 if (old_cursor.par() == -1)
1822 // We allow all kinds of "mumbo-jumbo" when freespacing.
1823 ParagraphList::iterator const old_pit = getPar(old_cursor);
1824 if (old_pit->isFreeSpacing())
1827 /* Ok I'll put some comments here about what is missing.
1828 I have fixed BackSpace (and thus Delete) to not delete
1829 double-spaces automagically. I have also changed Cut,
1830 Copy and Paste to hopefully do some sensible things.
1831 There are still some small problems that can lead to
1832 double spaces stored in the document file or space at
1833 the beginning of paragraphs. This happens if you have
1834 the cursor between to spaces and then save. Or if you
1835 cut and paste and the selection have a space at the
1836 beginning and then save right after the paste. I am
1837 sure none of these are very hard to fix, but I will
1838 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1839 that I can get some feedback. (Lgb)
1842 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1843 // delete the LineSeparator.
1846 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1847 // delete the LineSeparator.
1850 // If the pos around the old_cursor were spaces, delete one of them.
1851 if (old_cursor.par() != cursor.par()
1852 || old_cursor.pos() != cursor.pos()) {
1854 // Only if the cursor has really moved
1855 if (old_cursor.pos() > 0
1856 && old_cursor.pos() < old_pit->size()
1857 && old_pit->isLineSeparator(old_cursor.pos())
1858 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1859 bool erased = old_pit->erase(old_cursor.pos() - 1);
1860 redoParagraph(old_pit);
1864 #ifdef WITH_WARNINGS
1865 #warning This will not work anymore when we have multiple views of the same buffer
1866 // In this case, we will have to correct also the cursors held by
1867 // other bufferviews. It will probably be easier to do that in a more
1868 // automated way in LyXCursor code. (JMarc 26/09/2001)
1870 // correct all cursors held by the LyXText
1871 fixCursorAfterDelete(cursor, old_cursor);
1872 fixCursorAfterDelete(selection.cursor, old_cursor);
1873 fixCursorAfterDelete(selection.start, old_cursor);
1874 fixCursorAfterDelete(selection.end, old_cursor);
1879 // don't delete anything if this is the ONLY paragraph!
1880 if (ownerParagraphs().size() == 1)
1883 // Do not delete empty paragraphs with keepempty set.
1884 if (old_pit->allowEmpty())
1887 // only do our magic if we changed paragraph
1888 if (old_cursor.par() == cursor.par())
1891 // record if we have deleted a paragraph
1892 // we can't possibly have deleted a paragraph before this point
1893 bool deleted = false;
1895 if (old_pit->empty()
1896 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1897 // ok, we will delete something
1898 LyXCursor tmpcursor;
1902 bool selection_position_was_oldcursor_position =
1903 selection.cursor.par() == old_cursor.par()
1904 && selection.cursor.pos() == old_cursor.pos();
1907 cursor = old_cursor; // that undo can restore the right cursor position
1909 ParagraphList::iterator endpit = boost::next(old_pit);
1910 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1913 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1917 ParagraphList::iterator tmppit = cursorPar();
1919 ownerParagraphs().erase(old_pit);
1920 // update cursor par offset
1921 cursor.par(parOffset(tmppit));
1925 setCursorIntern(cursor.par(), cursor.pos());
1927 if (selection_position_was_oldcursor_position) {
1928 // correct selection
1929 selection.cursor = cursor;
1936 if (old_pit->stripLeadingSpaces()) {
1937 redoParagraph(old_pit);
1939 setCursorIntern(cursor.par(), cursor.pos());
1940 selection.cursor = cursor;
1946 ParagraphList & LyXText::ownerParagraphs() const
1948 return *paragraphs_;
1952 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1954 recordUndo(Undo::ATOMIC, this, first, last);
1958 void LyXText::recUndo(lyx::paroffset_type par) const
1960 recordUndo(Undo::ATOMIC, this, par, par);
1964 bool LyXText::isInInset() const
1966 // Sub-level has non-null bv owner and non-null inset owner.
1967 return inset_owner != 0;
1971 int defaultRowHeight()
1973 LyXFont const font(LyXFont::ALL_SANE);
1974 return int(font_metrics::maxHeight(font) * 1.2);