3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
34 #include "errorlist.h"
36 #include "FloatList.h"
37 #include "funcrequest.h"
42 #include "lyxrow_funcs.h"
43 #include "paragraph.h"
44 #include "paragraph_funcs.h"
45 #include "ParagraphParameters.h"
49 #include "frontends/font_metrics.h"
50 #include "frontends/LyXView.h"
52 #include "insets/insetbibitem.h"
53 #include "insets/insetenv.h"
54 #include "insets/insetfloat.h"
55 #include "insets/insetwrap.h"
57 #include "support/lstrings.h"
58 #include "support/textutils.h"
59 #include "support/tostr.h"
60 #include "support/std_sstream.h"
62 #include <boost/tuple/tuple.hpp>
65 using lyx::paroffset_type;
66 using lyx::support::bformat;
69 using std::ostringstream;
73 LyXText::LyXText(BufferView * bv, InsetText * inset, bool in_inset,
74 ParagraphList & paragraphs)
75 : height(0), width(0), textwidth_(bv ? bv->workWidth() : 100),
76 inset_owner(inset), bv_owner(bv),
77 in_inset_(in_inset), paragraphs_(¶graphs), xo_(0), yo_(0)
81 void LyXText::init(BufferView * bview)
85 ParagraphList::iterator const beg = ownerParagraphs().begin();
86 ParagraphList::iterator const end = ownerParagraphs().end();
87 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
93 current_font = getFont(beg, 0);
95 redoParagraphs(beg, end);
96 setCursorIntern(0, 0);
97 selection.cursor = cursor;
103 // Gets the fully instantiated font at a given position in a paragraph
104 // Basically the same routine as Paragraph::getFont() in paragraph.C.
105 // The difference is that this one is used for displaying, and thus we
106 // are allowed to make cosmetic improvements. For instance make footnotes
108 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
110 BOOST_ASSERT(pos >= 0);
112 LyXLayout_ptr const & layout = pit->layout();
114 BufferParams const & params = bv()->buffer()->params();
115 pos_type const body_pos = pit->beginOfBody();
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 LyXFont f = pit->getFontSettings(params, pos);
121 pit->inInset()->getDrawFont(f);
122 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
123 return f.realize(layout->reslabelfont);
125 return f.realize(layout->resfont);
128 // The uncommon case need not be optimized as much
131 layoutfont = layout->labelfont;
133 layoutfont = layout->font;
135 LyXFont font = pit->getFontSettings(params, pos);
136 font.realize(layoutfont);
139 pit->inInset()->getDrawFont(font);
141 // Realize with the fonts of lesser depth.
142 //font.realize(outerFont(pit, ownerParagraphs()));
143 font.realize(defaultfont_);
149 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
151 LyXLayout_ptr const & layout = pit->layout();
153 if (!pit->getDepth())
154 return layout->resfont;
156 LyXFont font = layout->font;
157 // Realize with the fonts of lesser depth.
158 //font.realize(outerFont(pit, ownerParagraphs()));
159 font.realize(defaultfont_);
165 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
167 LyXLayout_ptr const & layout = pit->layout();
169 if (!pit->getDepth())
170 return layout->reslabelfont;
172 LyXFont font = layout->labelfont;
173 // Realize with the fonts of lesser depth.
174 font.realize(outerFont(pit, ownerParagraphs()));
175 font.realize(defaultfont_);
181 void LyXText::setCharFont(ParagraphList::iterator pit,
182 pos_type pos, LyXFont const & fnt,
185 BufferParams const & params = bv()->buffer()->params();
186 LyXFont font = getFont(pit, pos);
187 font.update(fnt, params.language, toggleall);
188 // Let the insets convert their font
189 if (pit->isInset(pos)) {
190 InsetOld * inset = pit->getInset(pos);
191 if (isEditableInset(inset)) {
192 static_cast<UpdatableInset *>(inset)
193 ->setFont(bv(), fnt, toggleall, true);
197 // Plug through to version below:
198 setCharFont(pit, pos, font);
202 void LyXText::setCharFont(
203 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
206 LyXLayout_ptr const & layout = pit->layout();
208 // Get concrete layout font to reduce against
211 if (pos < pit->beginOfBody())
212 layoutfont = layout->labelfont;
214 layoutfont = layout->font;
216 // Realize against environment font information
217 if (pit->getDepth()) {
218 ParagraphList::iterator tp = pit;
219 while (!layoutfont.resolved() &&
220 tp != ownerParagraphs().end() &&
222 tp = outerHook(tp, ownerParagraphs());
223 if (tp != ownerParagraphs().end())
224 layoutfont.realize(tp->layout()->font);
228 layoutfont.realize(defaultfont_);
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 pit->setFont(pos, font);
237 InsetOld * LyXText::getInset() const
239 ParagraphList::iterator pit = cursorPar();
240 pos_type const pos = cursor.pos();
242 if (pos < pit->size() && pit->isInset(pos)) {
243 return pit->getInset(pos);
249 void LyXText::toggleInset()
251 InsetOld * inset = getInset();
252 // is there an editable inset at cursor position?
253 if (!isEditableInset(inset)) {
254 // No, try to see if we are inside a collapsable inset
255 if (inset_owner && inset_owner->owner()
256 && inset_owner->owner()->isOpen()) {
258 inset_owner->owner()->close();
259 bv()->getLyXText()->cursorRight(true);
260 bv()->updateParagraphDialog();
264 //bv()->owner()->message(inset->editMessage());
266 // do we want to keep this?? (JMarc)
267 if (!isHighlyEditableInset(inset))
268 recUndo(cursor.par());
278 // Asger is not sure we want to do this...
279 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
282 LyXLayout_ptr const & layout = par.layout();
283 pos_type const psize = par.size();
286 for (pos_type pos = 0; pos < psize; ++pos) {
287 if (pos < par.beginOfBody())
288 layoutfont = layout->labelfont;
290 layoutfont = layout->font;
292 LyXFont tmpfont = par.getFontSettings(params, pos);
293 tmpfont.reduce(layoutfont);
294 par.setFont(pos, tmpfont);
299 ParagraphList::iterator
300 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
301 LyXCursor & send_cur,
302 string const & layout)
304 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
305 ParagraphList::iterator undoendpit = endpit;
306 ParagraphList::iterator pars_end = ownerParagraphs().end();
308 if (endpit != pars_end && endpit->getDepth()) {
309 while (endpit != pars_end && endpit->getDepth()) {
313 } else if (endpit != pars_end) {
314 // because of parindents etc.
318 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
320 // ok we have a selection. This is always between sstart_cur
321 // and sel_end cursor
323 ParagraphList::iterator pit = getPar(sstart_cur);
324 ParagraphList::iterator epit = boost::next(getPar(send_cur));
326 BufferParams const & bufparams = bv()->buffer()->params();
327 LyXLayout_ptr const & lyxlayout =
328 bufparams.getLyXTextClass()[layout];
331 pit->applyLayout(lyxlayout);
332 makeFontEntriesLayoutSpecific(bufparams, *pit);
333 pit->params().spaceTop(lyxlayout->fill_top ?
334 VSpace(VSpace::VFILL)
335 : VSpace(VSpace::NONE));
336 pit->params().spaceBottom(lyxlayout->fill_bottom ?
337 VSpace(VSpace::VFILL)
338 : VSpace(VSpace::NONE));
339 if (lyxlayout->margintype == MARGIN_MANUAL)
340 pit->setLabelWidthString(lyxlayout->labelstring());
341 cur.par(std::distance(ownerParagraphs().begin(), pit));
343 } while (pit != epit);
349 // set layout over selection and make a total rebreak of those paragraphs
350 void LyXText::setLayout(string const & layout)
352 LyXCursor tmpcursor = cursor; // store the current cursor
354 // if there is no selection just set the layout
355 // of the current paragraph
356 if (!selection.set()) {
357 selection.start = cursor; // dummy selection
358 selection.end = cursor;
361 // special handling of new environment insets
362 BufferParams const & params = bv()->buffer()->params();
363 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
364 if (lyxlayout->is_environment) {
365 // move everything in a new environment inset
366 lyxerr << "setting layout " << layout << endl;
367 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
368 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
369 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
370 InsetOld * inset = new InsetEnvironment(params, layout);
371 if (bv()->insertInset(inset)) {
373 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
380 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
381 selection.end, layout);
382 redoParagraphs(getPar(selection.start), endpit);
384 // we have to reset the selection, because the
385 // geometry could have changed
386 setCursor(selection.start.par(), selection.start.pos(), false);
387 selection.cursor = cursor;
388 setCursor(selection.end.par(), selection.end.pos(), false);
392 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
399 void getSelectionSpan(LyXText & text,
400 ParagraphList::iterator & beg,
401 ParagraphList::iterator & end)
403 if (!text.selection.set()) {
404 beg = text.cursorPar();
405 end = boost::next(beg);
407 beg = text.getPar(text.selection.start);
408 end = boost::next(text.getPar(text.selection.end));
413 bool changeDepthAllowed(bv_funcs::DEPTH_CHANGE type,
414 Paragraph const & par,
417 if (par.layout()->labeltype == LABEL_BIBLIO)
419 int const depth = par.params().depth();
420 if (type == bv_funcs::INC_DEPTH && depth < max_depth)
422 if (type == bv_funcs::DEC_DEPTH && depth > 0)
431 bool LyXText::changeDepthAllowed(bv_funcs::DEPTH_CHANGE type)
433 ParagraphList::iterator beg, end;
434 getSelectionSpan(*this, beg, end);
436 if (beg != ownerParagraphs().begin())
437 max_depth = boost::prior(beg)->getMaxDepthAfter();
439 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
440 if (::changeDepthAllowed(type, *pit, max_depth))
442 max_depth = pit->getMaxDepthAfter();
448 void LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type)
450 ParagraphList::iterator beg, end;
451 getSelectionSpan(*this, beg, end);
453 recUndo(parOffset(beg), parOffset(end) - 1);
456 if (beg != ownerParagraphs().begin())
457 max_depth = boost::prior(beg)->getMaxDepthAfter();
459 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
460 if (::changeDepthAllowed(type, *pit, max_depth)) {
461 int const depth = pit->params().depth();
462 if (type == bv_funcs::INC_DEPTH)
463 pit->params().depth(depth + 1);
465 pit->params().depth(depth - 1);
467 max_depth = pit->getMaxDepthAfter();
469 // this handles the counter labels, and also fixes up
470 // depth values for follow-on (child) paragraphs
476 // set font over selection and make a total rebreak of those paragraphs
477 void LyXText::setFont(LyXFont const & font, bool toggleall)
479 // if there is no selection just set the current_font
480 if (!selection.set()) {
481 // Determine basis font
483 if (cursor.pos() < cursorPar()->beginOfBody()) {
484 layoutfont = getLabelFont(cursorPar());
486 layoutfont = getLayoutFont(cursorPar());
488 // Update current font
489 real_current_font.update(font,
490 bv()->buffer()->params().language,
493 // Reduce to implicit settings
494 current_font = real_current_font;
495 current_font.reduce(layoutfont);
496 // And resolve it completely
497 real_current_font.realize(layoutfont);
502 LyXCursor tmpcursor = cursor; // store the current cursor
504 // ok we have a selection. This is always between sel_start_cursor
505 // and sel_end cursor
507 recUndo(selection.start.par(), selection.end.par());
509 cursor = selection.start;
510 while (cursor.par() != selection.end.par() ||
511 cursor.pos() < selection.end.pos())
513 if (cursor.pos() < cursorPar()->size()) {
514 // an open footnote should behave like a closed one
515 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
516 cursor.pos(cursor.pos() + 1);
519 cursor.par(cursor.par() + 1);
524 redoParagraph(getPar(selection.start));
526 // we have to reset the selection, because the
527 // geometry could have changed, but we keep
528 // it for user convenience
529 setCursor(selection.start.par(), selection.start.pos());
530 selection.cursor = cursor;
531 setCursor(selection.end.par(), selection.end.pos());
533 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
534 tmpcursor.boundary());
538 // important for the screen
541 // the cursor set functions have a special mechanism. When they
542 // realize, that you left an empty paragraph, they will delete it.
544 // need the selection cursor:
545 void LyXText::setSelection()
547 TextCursor::setSelection();
551 void LyXText::clearSelection()
553 TextCursor::clearSelection();
555 // reset this in the bv()!
556 if (bv() && bv()->text)
557 bv()->text->xsel_cache.set(false);
561 void LyXText::cursorHome()
563 ParagraphList::iterator cpit = cursorPar();
564 setCursor(cpit, cpit->getRow(cursor.pos())->pos());
568 void LyXText::cursorEnd()
570 ParagraphList::iterator cpit = cursorPar();
571 pos_type end = cpit->getRow(cursor.pos())->endpos();
572 // if not on the last row of the par, put the cursor before
574 setCursor(cpit, end == cpit->size() ? end : end - 1);
578 void LyXText::cursorTop()
580 setCursor(ownerParagraphs().begin(), 0);
584 void LyXText::cursorBottom()
586 ParagraphList::iterator lastpit =
587 boost::prior(ownerParagraphs().end());
588 setCursor(lastpit, lastpit->size());
592 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
594 // If the mask is completely neutral, tell user
595 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
596 // Could only happen with user style
597 bv()->owner()->message(_("No font change defined. "
598 "Use Character under the Layout menu to define font change."));
602 // Try implicit word selection
603 // If there is a change in the language the implicit word selection
605 LyXCursor resetCursor = cursor;
606 bool implicitSelection =
607 font.language() == ignore_language
608 && font.number() == LyXFont::IGNORE
609 && selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT);
612 setFont(font, toggleall);
614 // Implicit selections are cleared afterwards
615 //and cursor is set to the original position.
616 if (implicitSelection) {
618 cursor = resetCursor;
619 setCursor(cursorPar(), cursor.pos());
620 selection.cursor = cursor;
625 string LyXText::getStringToIndex()
627 // Try implicit word selection
628 // If there is a change in the language the implicit word selection
630 LyXCursor const reset_cursor = cursor;
631 bool const implicitSelection =
632 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
635 if (!selection.set())
636 bv()->owner()->message(_("Nothing to index!"));
637 else if (selection.start.par() != selection.end.par())
638 bv()->owner()->message(_("Cannot index more than one paragraph!"));
640 idxstring = selectionAsString(*bv()->buffer(), false);
642 // Reset cursors to their original position.
643 cursor = reset_cursor;
644 setCursor(cursorPar(), cursor.pos());
645 selection.cursor = cursor;
647 // Clear the implicit selection.
648 if (implicitSelection)
655 // the DTP switches for paragraphs. LyX will store them in the first
656 // physical paragraph. When a paragraph is broken, the top settings rest,
657 // the bottom settings are given to the new one. So I can make sure,
658 // they do not duplicate themself and you cannot play dirty tricks with
661 void LyXText::setParagraph(VSpace const & space_top,
662 VSpace const & space_bottom,
663 Spacing const & spacing,
665 string const & labelwidthstring,
668 LyXCursor tmpcursor = cursor;
669 if (!selection.set()) {
670 selection.start = cursor;
671 selection.end = cursor;
674 // make sure that the depth behind the selection are restored, too
675 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
676 ParagraphList::iterator undoendpit = endpit;
677 ParagraphList::iterator pars_end = ownerParagraphs().end();
679 if (endpit != pars_end && endpit->getDepth()) {
680 while (endpit != pars_end && endpit->getDepth()) {
684 } else if (endpit != pars_end) {
685 // because of parindents etc.
689 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
691 int tmppit = selection.end.par();
693 while (tmppit != selection.start.par() - 1) {
694 setCursor(tmppit, 0);
696 ParagraphList::iterator const pit = cursorPar();
697 ParagraphParameters & params = pit->params();
698 params.spaceTop(space_top);
699 params.spaceBottom(space_bottom);
700 params.spacing(spacing);
702 // does the layout allow the new alignment?
703 LyXLayout_ptr const & layout = pit->layout();
705 if (align == LYX_ALIGN_LAYOUT)
706 align = layout->align;
707 if (align & layout->alignpossible) {
708 if (align == layout->align)
709 params.align(LYX_ALIGN_LAYOUT);
713 pit->setLabelWidthString(labelwidthstring);
714 params.noindent(noindent);
718 redoParagraphs(getPar(selection.start), endpit);
721 setCursor(selection.start.par(), selection.start.pos());
722 selection.cursor = cursor;
723 setCursor(selection.end.par(), selection.end.pos());
725 setCursor(tmpcursor.par(), tmpcursor.pos());
729 string expandLabel(LyXTextClass const & textclass,
730 LyXLayout_ptr const & layout, bool appendix)
732 string fmt = appendix ?
733 layout->labelstring_appendix() : layout->labelstring();
735 // handle 'inherited level parts' in 'fmt',
736 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
737 size_t const i = fmt.find('@', 0);
738 if (i != string::npos) {
739 size_t const j = fmt.find('@', i + 1);
740 if (j != string::npos) {
741 string parent(fmt, i + 1, j - i - 1);
742 string label = expandLabel(textclass, textclass[parent], appendix);
743 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
747 return textclass.counters().counterLabel(fmt);
753 void incrementItemDepth(ParagraphList::iterator pit,
754 ParagraphList::iterator first_pit)
756 int const cur_labeltype = pit->layout()->labeltype;
758 if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
761 int const cur_depth = pit->getDepth();
763 ParagraphList::iterator prev_pit = boost::prior(pit);
765 int const prev_depth = prev_pit->getDepth();
766 int const prev_labeltype = prev_pit->layout()->labeltype;
767 if (prev_depth == 0 && cur_depth > 0) {
768 if (prev_labeltype == cur_labeltype) {
769 pit->itemdepth = prev_pit->itemdepth + 1;
772 } else if (prev_depth < cur_depth) {
773 if (prev_labeltype == cur_labeltype) {
774 pit->itemdepth = prev_pit->itemdepth + 1;
777 } else if (prev_depth == cur_depth) {
778 if (prev_labeltype == cur_labeltype) {
779 pit->itemdepth = prev_pit->itemdepth;
783 if (prev_pit == first_pit)
791 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
792 ParagraphList::iterator firstpit,
798 int const cur_depth = pit->getDepth();
799 ParagraphList::iterator prev_pit = boost::prior(pit);
801 int const prev_depth = prev_pit->getDepth();
802 int const prev_labeltype = prev_pit->layout()->labeltype;
803 if (prev_depth <= cur_depth) {
804 if (prev_labeltype != LABEL_ENUMERATE) {
805 switch (pit->itemdepth) {
807 counters.reset("enumi");
809 counters.reset("enumii");
811 counters.reset("enumiii");
813 counters.reset("enumiv");
819 if (prev_pit == firstpit)
829 // set the counter of a paragraph. This includes the labels
830 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
832 BufferParams const & bufparams = buf.params();
833 LyXTextClass const & textclass = bufparams.getLyXTextClass();
834 LyXLayout_ptr const & layout = pit->layout();
835 ParagraphList::iterator first_pit = ownerParagraphs().begin();
836 Counters & counters = textclass.counters();
841 if (pit == first_pit) {
842 pit->params().appendix(pit->params().startOfAppendix());
844 pit->params().appendix(boost::prior(pit)->params().appendix());
845 if (!pit->params().appendix() &&
846 pit->params().startOfAppendix()) {
847 pit->params().appendix(true);
848 textclass.counters().reset();
851 // Maybe we have to increment the item depth.
852 incrementItemDepth(pit, first_pit);
855 // erase what was there before
856 pit->params().labelString(string());
858 if (layout->margintype == MARGIN_MANUAL) {
859 if (pit->params().labelWidthString().empty())
860 pit->setLabelWidthString(layout->labelstring());
862 pit->setLabelWidthString(string());
865 // is it a layout that has an automatic label?
866 if (layout->labeltype == LABEL_COUNTER) {
867 BufferParams const & bufparams = buf.params();
868 LyXTextClass const & textclass = bufparams.getLyXTextClass();
869 counters.step(layout->counter);
870 string label = expandLabel(textclass, layout, pit->params().appendix());
871 pit->params().labelString(label);
872 } else if (layout->labeltype == LABEL_ITEMIZE) {
873 // At some point of time we should do something more
874 // clever here, like:
875 // pit->params().labelString(
876 // bufparams.user_defined_bullet(pit->itemdepth).getText());
877 // for now, use a simple hardcoded label
879 switch (pit->itemdepth) {
894 pit->params().labelString(itemlabel);
895 } else if (layout->labeltype == LABEL_ENUMERATE) {
896 // Maybe we have to reset the enumeration counter.
897 resetEnumCounterIfNeeded(pit, first_pit, counters);
900 // Yes I know this is a really, really! bad solution
902 string enumcounter = "enum";
904 switch (pit->itemdepth) {
916 // not a valid enumdepth...
920 counters.step(enumcounter);
922 pit->params().labelString(counters.enumLabel(enumcounter));
923 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
924 counters.step("bibitem");
925 int number = counters.value("bibitem");
926 if (pit->bibitem()) {
927 pit->bibitem()->setCounter(number);
928 pit->params().labelString(layout->labelstring());
930 // In biblio should't be following counters but...
932 string s = buf.B_(layout->labelstring());
935 if (layout->labeltype == LABEL_SENSITIVE) {
936 ParagraphList::iterator end = ownerParagraphs().end();
937 ParagraphList::iterator tmppit = pit;
940 while (tmppit != end && tmppit->inInset()
941 // the single '=' is intended below
942 && (in = tmppit->inInset()->owner()))
944 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
945 in->lyxCode() == InsetOld::WRAP_CODE) {
949 Paragraph const * owner = &ownerPar(buf, in);
951 for ( ; tmppit != end; ++tmppit)
952 if (&*tmppit == owner)
960 if (in->lyxCode() == InsetOld::FLOAT_CODE)
961 type = static_cast<InsetFloat*>(in)->params().type;
962 else if (in->lyxCode() == InsetOld::WRAP_CODE)
963 type = static_cast<InsetWrap*>(in)->params().type;
967 Floating const & fl = textclass.floats().getType(type);
969 counters.step(fl.type());
971 // Doesn't work... yet.
972 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
974 // par->SetLayout(0);
975 // s = layout->labelstring;
976 s = _("Senseless: ");
979 pit->params().labelString(s);
985 // Updates all counters.
986 void LyXText::updateCounters()
989 bv()->buffer()->params().getLyXTextClass().counters().reset();
991 bool update_pos = false;
993 ParagraphList::iterator beg = ownerParagraphs().begin();
994 ParagraphList::iterator end = ownerParagraphs().end();
995 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
996 string const oldLabel = pit->params().labelString();
999 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1001 if (pit->params().depth() > maxdepth)
1002 pit->params().depth(maxdepth);
1004 // setCounter can potentially change the labelString.
1005 setCounter(*bv()->buffer(), pit);
1006 string const & newLabel = pit->params().labelString();
1007 if (oldLabel != newLabel) {
1008 redoParagraphInternal(pit);
1014 updateParPositions();
1018 void LyXText::insertInset(InsetOld * inset)
1020 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1023 recUndo(cursor.par());
1025 cursorPar()->insertInset(cursor.pos(), inset);
1026 // Just to rebreak and refresh correctly.
1027 // The character will not be inserted a second time
1028 insertChar(Paragraph::META_INSET);
1029 // If we enter a highly editable inset the cursor should be before
1030 // the inset. After an undo LyX tries to call inset->edit(...)
1031 // and fails if the cursor is behind the inset and getInset
1032 // does not return the inset!
1033 if (isHighlyEditableInset(inset))
1040 void LyXText::cutSelection(bool doclear, bool realcut)
1042 // Stuff what we got on the clipboard. Even if there is no selection.
1044 // There is a problem with having the stuffing here in that the
1045 // larger the selection the slower LyX will get. This can be
1046 // solved by running the line below only when the selection has
1047 // finished. The solution used currently just works, to make it
1048 // faster we need to be more clever and probably also have more
1049 // calls to stuffClipboard. (Lgb)
1050 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1052 // This doesn't make sense, if there is no selection
1053 if (!selection.set())
1056 // OK, we have a selection. This is always between selection.start
1057 // and selection.end
1059 // make sure that the depth behind the selection are restored, too
1060 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1061 ParagraphList::iterator undoendpit = endpit;
1062 ParagraphList::iterator pars_end = ownerParagraphs().end();
1064 if (endpit != pars_end && endpit->getDepth()) {
1065 while (endpit != pars_end && endpit->getDepth()) {
1067 undoendpit = endpit;
1069 } else if (endpit != pars_end) {
1070 // because of parindents etc.
1074 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1076 endpit = getPar(selection.end.par());
1077 int endpos = selection.end.pos();
1079 BufferParams const & bufparams = bv()->buffer()->params();
1080 boost::tie(endpit, endpos) = realcut ?
1081 CutAndPaste::cutSelection(bufparams,
1083 getPar(selection.start.par()), endpit,
1084 selection.start.pos(), endpos,
1085 bufparams.textclass,
1087 : CutAndPaste::eraseSelection(bufparams,
1089 getPar(selection.start.par()), endpit,
1090 selection.start.pos(), endpos,
1092 // sometimes necessary
1094 getPar(selection.start.par())->stripLeadingSpaces();
1096 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1097 // cutSelection can invalidate the cursor so we need to set
1099 // we prefer the end for when tracking changes
1101 cursor.par(parOffset(endpit));
1103 // need a valid cursor. (Lgb)
1106 setCursor(cursorPar(), cursor.pos());
1107 selection.cursor = cursor;
1112 void LyXText::copySelection()
1114 // stuff the selection onto the X clipboard, from an explicit copy request
1115 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1117 // this doesnt make sense, if there is no selection
1118 if (!selection.set())
1121 // ok we have a selection. This is always between selection.start
1122 // and sel_end cursor
1124 // copy behind a space if there is one
1125 while (getPar(selection.start)->size() > selection.start.pos()
1126 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1127 && (selection.start.par() != selection.end.par()
1128 || selection.start.pos() < selection.end.pos()))
1129 selection.start.pos(selection.start.pos() + 1);
1131 CutAndPaste::copySelection(getPar(selection.start.par()),
1132 getPar(selection.end.par()),
1133 selection.start.pos(), selection.end.pos(),
1134 bv()->buffer()->params().textclass);
1138 void LyXText::pasteSelection(size_t sel_index)
1140 // this does not make sense, if there is nothing to paste
1141 if (!CutAndPaste::checkPastePossible())
1144 recUndo(cursor.par());
1146 ParagraphList::iterator endpit;
1151 boost::tie(ppp, endpit) =
1152 CutAndPaste::pasteSelection(*bv()->buffer(),
1154 cursorPar(), cursor.pos(),
1155 bv()->buffer()->params().textclass,
1157 bufferErrors(*bv()->buffer(), el);
1158 bv()->showErrorList(_("Paste"));
1160 redoParagraphs(cursorPar(), endpit);
1162 setCursor(cursor.par(), cursor.pos());
1165 selection.cursor = cursor;
1166 setCursor(ppp.first, ppp.second);
1172 void LyXText::setSelectionRange(lyx::pos_type length)
1177 selection.cursor = cursor;
1184 // simple replacing. The font of the first selected character is used
1185 void LyXText::replaceSelectionWithString(string const & str)
1187 recUndo(cursor.par());
1190 if (!selection.set()) { // create a dummy selection
1191 selection.end = cursor;
1192 selection.start = cursor;
1195 // Get font setting before we cut
1196 pos_type pos = selection.end.pos();
1197 LyXFont const font = getPar(selection.start)
1198 ->getFontSettings(bv()->buffer()->params(),
1199 selection.start.pos());
1201 // Insert the new string
1202 string::const_iterator cit = str.begin();
1203 string::const_iterator end = str.end();
1204 for (; cit != end; ++cit) {
1205 getPar(selection.end)->insertChar(pos, (*cit), font);
1209 // Cut the selection
1210 cutSelection(true, false);
1216 // needed to insert the selection
1217 void LyXText::insertStringAsLines(string const & str)
1219 ParagraphList::iterator pit = cursorPar();
1220 pos_type pos = cursor.pos();
1221 ParagraphList::iterator endpit = boost::next(cursorPar());
1223 recUndo(cursor.par());
1225 // only to be sure, should not be neccessary
1228 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1230 redoParagraphs(cursorPar(), endpit);
1231 setCursor(cursorPar(), cursor.pos());
1232 selection.cursor = cursor;
1233 setCursor(pit, pos);
1238 // turns double-CR to single CR, others where converted into one
1239 // blank. Then InsertStringAsLines is called
1240 void LyXText::insertStringAsParagraphs(string const & str)
1242 string linestr(str);
1243 bool newline_inserted = false;
1244 string::size_type const siz = linestr.length();
1246 for (string::size_type i = 0; i < siz; ++i) {
1247 if (linestr[i] == '\n') {
1248 if (newline_inserted) {
1249 // we know that \r will be ignored by
1250 // InsertStringA. Of course, it is a dirty
1251 // trick, but it works...
1252 linestr[i - 1] = '\r';
1256 newline_inserted = true;
1258 } else if (IsPrintable(linestr[i])) {
1259 newline_inserted = false;
1262 insertStringAsLines(linestr);
1266 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1268 setCursor(parOffset(pit), pos);
1272 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1274 LyXCursor old_cursor = cursor;
1275 setCursorIntern(par, pos, setfont, boundary);
1276 return deleteEmptyParagraphMechanism(old_cursor);
1280 void LyXText::redoCursor()
1282 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1284 if (!selection.set())
1287 LyXCursor tmpcursor = cursor;
1288 setCursor(selection.cursor.par(), selection.cursor.pos());
1289 selection.cursor = cursor;
1290 setCursor(tmpcursor.par(), tmpcursor.pos());
1295 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1296 pos_type pos, bool boundary)
1298 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1302 cur.boundary(boundary);
1304 // no rows, no fun...
1305 if (ownerParagraphs().begin()->rows.empty())
1308 // get the cursor y position in text
1310 ParagraphList::iterator pit = getPar(par);
1311 Row const & row = *pit->getRow(pos);
1313 int y = pit->y + row.y_offset();
1315 // y is now the beginning of the cursor row
1316 y += row.baseline();
1317 // y is now the cursor baseline
1320 pos_type const end = row.endpos();
1322 // None of these should happen, but we're scaredy-cats
1324 lyxerr << "dont like -1" << endl;
1327 BOOST_ASSERT(false);
1328 } else if (pos > pit->size()) {
1329 lyxerr << "dont like 1, pos: " << pos
1330 << " size: " << pit->size()
1331 << " row.pos():" << row.pos()
1332 << " paroffset: " << par << endl;
1335 BOOST_ASSERT(false);
1336 } else if (pos > end) {
1337 lyxerr << "dont like 2 please report" << endl;
1338 // This shouldn't happen.
1341 BOOST_ASSERT(false);
1342 } else if (pos < row.pos()) {
1343 lyxerr << "dont like 3 please report pos:" << pos
1344 << " size: " << pit->size()
1345 << " row.pos():" << row.pos()
1346 << " paroffset: " << par << endl;
1349 BOOST_ASSERT(false);
1351 // now get the cursors x position
1352 cur.x(int(getCursorX(pit, row, pos, boundary)));
1356 float LyXText::getCursorX(ParagraphList::iterator pit, Row const & row,
1357 pos_type pos, bool boundary) const
1359 pos_type cursor_vpos = 0;
1361 double fill_separator = row.fill_separator();
1362 double fill_hfill = row.fill_hfill();
1363 double fill_label_hfill = row.fill_label_hfill();
1364 pos_type const row_pos = row.pos();
1365 pos_type const end = row.endpos();
1368 cursor_vpos = row_pos;
1369 else if (pos >= end && !boundary)
1370 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1372 else if (pos > row_pos && (pos >= end || boundary))
1373 // Place cursor after char at (logical) position pos - 1
1374 cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
1375 ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
1377 // Place cursor before char at (logical) position pos
1378 cursor_vpos = (bidi.level(pos) % 2 == 0)
1379 ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
1381 pos_type body_pos = pit->beginOfBody();
1383 (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
1386 for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
1387 pos_type pos = bidi.vis2log(vpos);
1388 if (body_pos > 0 && pos == body_pos - 1) {
1389 x += fill_label_hfill
1390 + font_metrics::width(pit->layout()->labelsep,
1392 if (pit->isLineSeparator(body_pos - 1))
1393 x -= singleWidth(pit, body_pos - 1);
1396 if (hfillExpansion(*pit, row, pos)) {
1397 x += singleWidth(pit, pos);
1398 if (pos >= body_pos)
1401 x += fill_label_hfill;
1402 } else if (pit->isSeparator(pos)) {
1403 x += singleWidth(pit, pos);
1404 if (pos >= body_pos)
1405 x += fill_separator;
1407 x += singleWidth(pit, pos);
1413 void LyXText::setCursorIntern(paroffset_type par,
1414 pos_type pos, bool setfont, bool boundary)
1416 setCursor(cursor, par, pos, boundary);
1417 bv()->x_target(cursor.x() + xo_);
1423 void LyXText::setCurrentFont()
1425 pos_type pos = cursor.pos();
1426 ParagraphList::iterator pit = cursorPar();
1428 if (cursor.boundary() && pos > 0)
1432 if (pos == pit->size())
1434 else // potentional bug... BUG (Lgb)
1435 if (pit->isSeparator(pos)) {
1436 if (pos > pit->getRow(pos)->pos() &&
1437 bidi.level(pos) % 2 ==
1438 bidi.level(pos - 1) % 2)
1440 else if (pos + 1 < pit->size())
1445 BufferParams const & bufparams = bv()->buffer()->params();
1446 current_font = pit->getFontSettings(bufparams, pos);
1447 real_current_font = getFont(pit, pos);
1449 if (cursor.pos() == pit->size() &&
1450 bidi.isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1451 !cursor.boundary()) {
1452 Language const * lang =
1453 pit->getParLanguage(bufparams);
1454 current_font.setLanguage(lang);
1455 current_font.setNumber(LyXFont::OFF);
1456 real_current_font.setLanguage(lang);
1457 real_current_font.setNumber(LyXFont::OFF);
1462 // returns the column near the specified x-coordinate of the row
1463 // x is set to the real beginning of this column
1464 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1465 Row const & row, int & x, bool & boundary) const
1467 double tmpx = row.x();
1468 double fill_separator = row.fill_separator();
1469 double fill_hfill = row.fill_hfill();
1470 double fill_label_hfill = row.fill_label_hfill();
1472 pos_type vc = row.pos();
1473 pos_type end = row.endpos();
1475 LyXLayout_ptr const & layout = pit->layout();
1477 bool left_side = false;
1479 pos_type body_pos = pit->beginOfBody();
1480 double last_tmpx = tmpx;
1484 !pit->isLineSeparator(body_pos - 1)))
1487 // check for empty row
1493 while (vc < end && tmpx <= x) {
1494 c = bidi.vis2log(vc);
1496 if (body_pos > 0 && c == body_pos - 1) {
1497 tmpx += fill_label_hfill +
1498 font_metrics::width(layout->labelsep, getLabelFont(pit));
1499 if (pit->isLineSeparator(body_pos - 1))
1500 tmpx -= singleWidth(pit, body_pos - 1);
1503 if (hfillExpansion(*pit, row, c)) {
1504 tmpx += singleWidth(pit, c);
1508 tmpx += fill_label_hfill;
1509 } else if (pit->isSeparator(c)) {
1510 tmpx += singleWidth(pit, c);
1512 tmpx += fill_separator;
1514 tmpx += singleWidth(pit, c);
1519 if ((tmpx + last_tmpx) / 2 > x) {
1524 BOOST_ASSERT(vc <= end); // This shouldn't happen.
1527 // This (rtl_support test) is not needed, but gives
1528 // some speedup if rtl_support == false
1529 bool const lastrow = lyxrc.rtl_support && row.endpos() == pit->size();
1531 // If lastrow is false, we don't need to compute
1532 // the value of rtl.
1533 bool const rtl = (lastrow)
1534 ? pit->isRightToLeftPar(bv()->buffer()->params())
1537 ((rtl && left_side && vc == row.pos() && x < tmpx - 5) ||
1538 (!rtl && !left_side && vc == end && x > tmpx + 5)))
1540 else if (vc == row.pos()) {
1541 c = bidi.vis2log(vc);
1542 if (bidi.level(c) % 2 == 1)
1545 c = bidi.vis2log(vc - 1);
1546 bool const rtl = (bidi.level(c) % 2 == 1);
1547 if (left_side == rtl) {
1549 boundary = bidi.isBoundary(*bv()->buffer(), *pit, c);
1553 if (row.pos() < end && c >= end && pit->isNewline(end - 1)) {
1554 if (bidi.level(end -1) % 2 == 0)
1555 tmpx -= singleWidth(pit, end - 1);
1557 tmpx += singleWidth(pit, end - 1);
1567 void LyXText::setCursorFromCoordinates(int x, int y)
1569 LyXCursor old_cursor = cursor;
1570 setCursorFromCoordinates(cursor, x, y);
1572 deleteEmptyParagraphMechanism(old_cursor);
1575 // x,y are coordinates relative to this LyXText
1576 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1578 // Get the row first.
1579 ParagraphList::iterator pit;
1580 Row const & row = *getRowNearY(y, pit);
1581 y = pit->y + row.y_offset();
1584 pos_type const column = getColumnNearX(pit, row, x, bound);
1585 cur.par(parOffset(pit));
1586 cur.pos(row.pos() + column);
1588 cur.y(y + row.baseline());
1590 cur.boundary(bound);
1594 bool LyXText::checkAndActivateInset(bool front)
1596 if (cursor.pos() == cursorPar()->size())
1598 InsetOld * inset = cursorPar()->getInset(cursor.pos());
1599 if (!isHighlyEditableInset(inset))
1601 inset->edit(bv(), front);
1606 DispatchResult LyXText::moveRight()
1608 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1609 return moveLeftIntern(false, true, false);
1611 return moveRightIntern(true, true, false);
1615 DispatchResult LyXText::moveLeft()
1617 if (cursorPar()->isRightToLeftPar(bv()->buffer()->params()))
1618 return moveRightIntern(true, true, false);
1620 return moveLeftIntern(false, true, false);
1624 DispatchResult LyXText::moveRightIntern(bool front, bool activate_inset, bool selecting)
1626 ParagraphList::iterator c_par = cursorPar();
1627 if (boost::next(c_par) == ownerParagraphs().end()
1628 && cursor.pos() >= c_par->size())
1629 return DispatchResult(false, FINISHED_RIGHT);
1630 if (activate_inset && checkAndActivateInset(front))
1631 return DispatchResult(true, true);
1635 return DispatchResult(true);
1639 DispatchResult LyXText::moveLeftIntern(bool front,
1640 bool activate_inset, bool selecting)
1642 if (cursor.par() == 0 && cursor.pos() <= 0)
1643 return DispatchResult(false, FINISHED);
1647 if (activate_inset && checkAndActivateInset(front))
1648 return DispatchResult(true, true);
1649 return DispatchResult(true);
1653 DispatchResult LyXText::moveUp()
1655 if (cursorPar() == firstPar() && cursorRow() == firstRow())
1656 return DispatchResult(false, FINISHED_UP);
1659 return DispatchResult(true);
1663 DispatchResult LyXText::moveDown()
1665 if (cursorPar() == lastPar() && cursorRow() == lastRow())
1666 return DispatchResult(false, FINISHED_DOWN);
1669 return DispatchResult(true);
1673 bool LyXText::cursorLeft(bool internal)
1675 if (cursor.pos() > 0) {
1676 bool boundary = cursor.boundary();
1677 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1678 if (!internal && !boundary &&
1679 bidi.isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1680 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1684 if (cursor.par() != 0) {
1685 // steps into the paragraph above
1686 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1694 bool LyXText::cursorRight(bool internal)
1696 if (!internal && cursor.boundary()) {
1697 setCursor(cursor.par(), cursor.pos(), true, false);
1701 if (cursor.pos() != cursorPar()->size()) {
1702 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1703 if (!internal && bidi.isBoundary(*bv()->buffer(), *cursorPar(),
1705 setCursor(cursor.par(), cursor.pos(), true, true);
1709 if (cursor.par() + 1 != int(ownerParagraphs().size())) {
1710 setCursor(cursor.par() + 1, 0);
1718 void LyXText::cursorUp(bool selecting)
1720 Row const & row = *cursorRow();
1721 int x = bv()->x_target() - xo_;
1722 int y = cursor.y() - row.baseline() - 1;
1723 setCursorFromCoordinates(x, y);
1726 int y_abs = y + yo_ - bv()->top_y();
1727 InsetOld * inset_hit = checkInsetHit(bv()->x_target(), y_abs);
1728 if (inset_hit && isHighlyEditableInset(inset_hit))
1729 inset_hit->edit(bv(), bv()->x_target(), y_abs);
1734 void LyXText::cursorDown(bool selecting)
1736 Row const & row = *cursorRow();
1737 int x = bv()->x_target() - xo_;
1738 int y = cursor.y() - row.baseline() + row.height() + 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::cursorUpParagraph()
1752 ParagraphList::iterator cpit = cursorPar();
1753 if (cursor.pos() > 0)
1755 else if (cpit != ownerParagraphs().begin())
1756 setCursor(boost::prior(cpit), 0);
1760 void LyXText::cursorDownParagraph()
1762 ParagraphList::iterator pit = cursorPar();
1763 ParagraphList::iterator next_pit = boost::next(pit);
1765 if (next_pit != ownerParagraphs().end())
1766 setCursor(next_pit, 0);
1768 setCursor(pit, pit->size());
1772 // fix the cursor `cur' after a characters has been deleted at `where'
1773 // position. Called by deleteEmptyParagraphMechanism
1774 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1776 // if cursor is not in the paragraph where the delete occured,
1778 if (cur.par() != where.par())
1781 // if cursor position is after the place where the delete occured,
1783 if (cur.pos() > where.pos())
1784 cur.pos(cur.pos()-1);
1786 // check also if we don't want to set the cursor on a spot behind the
1787 // pagragraph because we erased the last character.
1788 if (cur.pos() > getPar(cur)->size())
1789 cur.pos(getPar(cur)->size());
1791 // recompute row et al. for this cursor
1792 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1796 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1798 // Would be wrong to delete anything if we have a selection.
1799 if (selection.set())
1802 // Don't do anything if the cursor is invalid
1803 if (old_cursor.par() == -1)
1806 // We allow all kinds of "mumbo-jumbo" when freespacing.
1807 ParagraphList::iterator const old_pit = getPar(old_cursor);
1808 if (old_pit->isFreeSpacing())
1811 /* Ok I'll put some comments here about what is missing.
1812 I have fixed BackSpace (and thus Delete) to not delete
1813 double-spaces automagically. I have also changed Cut,
1814 Copy and Paste to hopefully do some sensible things.
1815 There are still some small problems that can lead to
1816 double spaces stored in the document file or space at
1817 the beginning of paragraphs. This happens if you have
1818 the cursor between to spaces and then save. Or if you
1819 cut and paste and the selection have a space at the
1820 beginning and then save right after the paste. I am
1821 sure none of these are very hard to fix, but I will
1822 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1823 that I can get some feedback. (Lgb)
1826 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1827 // delete the LineSeparator.
1830 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1831 // delete the LineSeparator.
1834 // If the pos around the old_cursor were spaces, delete one of them.
1835 if (old_cursor.par() != cursor.par()
1836 || old_cursor.pos() != cursor.pos()) {
1838 // Only if the cursor has really moved
1839 if (old_cursor.pos() > 0
1840 && old_cursor.pos() < old_pit->size()
1841 && old_pit->isLineSeparator(old_cursor.pos())
1842 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1843 bool erased = old_pit->erase(old_cursor.pos() - 1);
1844 redoParagraph(old_pit);
1848 #ifdef WITH_WARNINGS
1849 #warning This will not work anymore when we have multiple views of the same buffer
1850 // In this case, we will have to correct also the cursors held by
1851 // other bufferviews. It will probably be easier to do that in a more
1852 // automated way in LyXCursor code. (JMarc 26/09/2001)
1854 // correct all cursors held by the LyXText
1855 fixCursorAfterDelete(cursor, old_cursor);
1856 fixCursorAfterDelete(selection.cursor, old_cursor);
1857 fixCursorAfterDelete(selection.start, old_cursor);
1858 fixCursorAfterDelete(selection.end, old_cursor);
1863 // don't delete anything if this is the ONLY paragraph!
1864 if (ownerParagraphs().size() == 1)
1867 // Do not delete empty paragraphs with keepempty set.
1868 if (old_pit->allowEmpty())
1871 // only do our magic if we changed paragraph
1872 if (old_cursor.par() == cursor.par())
1875 // record if we have deleted a paragraph
1876 // we can't possibly have deleted a paragraph before this point
1877 bool deleted = false;
1879 if (old_pit->empty()
1880 || (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1881 // ok, we will delete something
1882 LyXCursor tmpcursor;
1886 bool selection_position_was_oldcursor_position =
1887 selection.cursor.par() == old_cursor.par()
1888 && selection.cursor.pos() == old_cursor.pos();
1891 cursor = old_cursor; // that undo can restore the right cursor position
1893 ParagraphList::iterator endpit = boost::next(old_pit);
1894 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1897 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1901 ParagraphList::iterator tmppit = cursorPar();
1903 ownerParagraphs().erase(old_pit);
1904 // update cursor par offset
1905 cursor.par(parOffset(tmppit));
1909 setCursorIntern(cursor.par(), cursor.pos());
1911 if (selection_position_was_oldcursor_position) {
1912 // correct selection
1913 selection.cursor = cursor;
1920 if (old_pit->stripLeadingSpaces()) {
1921 redoParagraph(old_pit);
1923 setCursorIntern(cursor.par(), cursor.pos());
1924 selection.cursor = cursor;
1930 ParagraphList & LyXText::ownerParagraphs() const
1932 return *paragraphs_;
1936 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1938 recordUndo(Undo::ATOMIC, this, first, last);
1942 void LyXText::recUndo(lyx::paroffset_type par) const
1944 recordUndo(Undo::ATOMIC, this, par, par);
1948 bool LyXText::isInInset() const
1950 // Sub-level has non-null bv owner and non-null inset owner.
1951 return inset_owner != 0;
1955 int defaultRowHeight()
1957 LyXFont const font(LyXFont::ALL_SANE);
1958 return int(font_metrics::maxHeight(font) * 1.2);