1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
31 #include "FloatList.h"
33 #include "ParagraphParameters.h"
35 #include "lyxrow_funcs.h"
36 #include "metricsinfo.h"
37 #include "paragraph_funcs.h"
39 #include "insets/insetbibitem.h"
40 #include "insets/insetenv.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
48 #include <boost/tuple/tuple.hpp>
52 using namespace lyx::support;
62 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
63 ParagraphList & paragraphs)
64 : height(0), width(0), anchor_y_(0),
65 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
66 in_inset_(ininset), paragraphs_(paragraphs)
71 void LyXText::init(BufferView * bview)
75 ParagraphList::iterator const beg = ownerParagraphs().begin();
76 ParagraphList::iterator const end = ownerParagraphs().end();
77 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
85 current_font = getFont(beg, 0);
87 redoParagraphs(beg, end);
88 setCursorIntern(beg, 0);
89 selection.cursor = cursor;
95 // Gets the fully instantiated font at a given position in a paragraph
96 // Basically the same routine as Paragraph::getFont() in paragraph.C.
97 // The difference is that this one is used for displaying, and thus we
98 // are allowed to make cosmetic improvements. For instance make footnotes
100 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
104 LyXLayout_ptr const & layout = pit->layout();
106 BufferParams const & params = bv()->buffer()->params;
108 // We specialize the 95% common case:
109 if (!pit->getDepth()) {
110 if (layout->labeltype == LABEL_MANUAL
111 && pos < pit->beginningOfBody()) {
113 LyXFont f = pit->getFontSettings(params, pos);
115 pit->inInset()->getDrawFont(f);
116 return f.realize(layout->reslabelfont);
118 LyXFont f = pit->getFontSettings(params, pos);
120 pit->inInset()->getDrawFont(f);
121 return f.realize(layout->resfont);
125 // The uncommon case need not be optimized as much
129 if (pos < pit->beginningOfBody()) {
131 layoutfont = layout->labelfont;
134 layoutfont = layout->font;
137 LyXFont tmpfont = pit->getFontSettings(params, pos);
138 tmpfont.realize(layoutfont);
141 pit->inInset()->getDrawFont(tmpfont);
143 // Realize with the fonts of lesser depth.
144 tmpfont.realize(outerFont(pit, ownerParagraphs()));
145 tmpfont.realize(defaultfont_);
151 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
153 LyXLayout_ptr const & layout = pit->layout();
155 if (!pit->getDepth())
156 return layout->resfont;
158 LyXFont font = layout->font;
159 // Realize with the fonts of lesser depth.
160 font.realize(outerFont(pit, ownerParagraphs()));
161 font.realize(defaultfont_);
167 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
169 LyXLayout_ptr const & layout = pit->layout();
171 if (!pit->getDepth())
172 return layout->reslabelfont;
174 LyXFont font = layout->labelfont;
175 // Realize with the fonts of lesser depth.
176 font.realize(outerFont(pit, ownerParagraphs()));
177 font.realize(defaultfont_);
183 void LyXText::setCharFont(ParagraphList::iterator pit,
184 pos_type pos, LyXFont const & fnt,
187 BufferParams const & params = bv()->buffer()->params;
188 LyXFont font = getFont(pit, pos);
189 font.update(fnt, params.language, toggleall);
190 // Let the insets convert their font
191 if (pit->isInset(pos)) {
192 InsetOld * inset = pit->getInset(pos);
193 if (isEditableInset(inset)) {
194 static_cast<UpdatableInset *>(inset)
195 ->setFont(bv(), fnt, toggleall, true);
199 // Plug through to version below:
200 setCharFont(pit, pos, font);
204 void LyXText::setCharFont(
205 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
208 LyXLayout_ptr const & layout = pit->layout();
210 // Get concrete layout font to reduce against
213 if (pos < pit->beginningOfBody())
214 layoutfont = layout->labelfont;
216 layoutfont = layout->font;
218 // Realize against environment font information
219 if (pit->getDepth()) {
220 ParagraphList::iterator tp = pit;
221 while (!layoutfont.resolved() &&
222 tp != ownerParagraphs().end() &&
224 tp = outerHook(tp, ownerParagraphs());
225 if (tp != ownerParagraphs().end())
226 layoutfont.realize(tp->layout()->font);
230 layoutfont.realize(defaultfont_);
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 pit->setFont(pos, font);
239 InsetOld * LyXText::getInset() const
241 ParagraphList::iterator pit = cursor.par();
242 pos_type const pos = cursor.pos();
244 if (pos < pit->size() && pit->isInset(pos)) {
245 return pit->getInset(pos);
251 void LyXText::toggleInset()
253 InsetOld * inset = getInset();
254 // is there an editable inset at cursor position?
255 if (!isEditableInset(inset)) {
256 // No, try to see if we are inside a collapsable inset
257 if (inset_owner && inset_owner->owner()
258 && inset_owner->owner()->isOpen()) {
259 bv()->unlockInset(inset_owner->owner());
260 inset_owner->owner()->close(bv());
261 bv()->getLyXText()->cursorRight(bv());
265 //bv()->owner()->message(inset->editMessage());
267 // do we want to keep this?? (JMarc)
268 if (!isHighlyEditableInset(inset))
269 recordUndo(bv(), Undo::ATOMIC);
280 /* used in setlayout */
281 // Asger is not sure we want to do this...
282 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
285 LyXLayout_ptr const & layout = par.layout();
286 pos_type const psize = par.size();
289 for (pos_type pos = 0; pos < psize; ++pos) {
290 if (pos < par.beginningOfBody())
291 layoutfont = layout->labelfont;
293 layoutfont = layout->font;
295 LyXFont tmpfont = par.getFontSettings(params, pos);
296 tmpfont.reduce(layoutfont);
297 par.setFont(pos, tmpfont);
302 ParagraphList::iterator
303 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
304 LyXCursor & send_cur,
305 string const & layout)
307 ParagraphList::iterator endpit = boost::next(send_cur.par());
308 ParagraphList::iterator undoendpit = endpit;
309 ParagraphList::iterator pars_end = ownerParagraphs().end();
311 if (endpit != pars_end && endpit->getDepth()) {
312 while (endpit != pars_end && endpit->getDepth()) {
316 } else if (endpit != pars_end) {
317 // because of parindents etc.
321 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
323 // ok we have a selection. This is always between sstart_cur
324 // and sel_end cursor
326 ParagraphList::iterator pit = sstart_cur.par();
327 ParagraphList::iterator epit = boost::next(send_cur.par());
329 LyXLayout_ptr const & lyxlayout =
330 bv()->buffer()->params.getLyXTextClass()[layout];
333 pit->applyLayout(lyxlayout);
334 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
335 pit->params().spaceTop(lyxlayout->fill_top ?
336 VSpace(VSpace::VFILL)
337 : VSpace(VSpace::NONE));
338 pit->params().spaceBottom(lyxlayout->fill_bottom ?
339 VSpace(VSpace::VFILL)
340 : VSpace(VSpace::NONE));
341 if (lyxlayout->margintype == MARGIN_MANUAL)
342 pit->setLabelWidthString(lyxlayout->labelstring());
345 } while (pit != epit);
351 // set layout over selection and make a total rebreak of those paragraphs
352 void LyXText::setLayout(string const & layout)
354 LyXCursor tmpcursor = cursor; // store the current cursor
356 // if there is no selection just set the layout
357 // of the current paragraph
358 if (!selection.set()) {
359 selection.start = cursor; // dummy selection
360 selection.end = cursor;
363 // special handling of new environment insets
364 BufferParams const & params = bv()->buffer()->params;
365 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
366 if (lyxlayout->is_environment) {
367 // move everything in a new environment inset
368 lyxerr << "setting layout " << layout << endl;
369 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
370 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
371 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
372 InsetOld * inset = new InsetEnvironment(params, layout);
373 if (bv()->insertInset(inset)) {
375 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
382 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
383 selection.end, layout);
384 redoParagraphs(selection.start.par(), endpit);
386 // we have to reset the selection, because the
387 // geometry could have changed
388 setCursor(selection.start.par(), selection.start.pos(), false);
389 selection.cursor = cursor;
390 setCursor(selection.end.par(), selection.end.pos(), false);
394 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
398 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
400 ParagraphList::iterator pit = cursor.par();
401 ParagraphList::iterator end = cursor.par();
402 ParagraphList::iterator start = pit;
404 if (selection.set()) {
405 pit = selection.start.par();
406 end = selection.end.par();
410 ParagraphList::iterator pastend = boost::next(end);
413 recordUndo(bv(), Undo::ATOMIC, start, end);
415 bool changed = false;
417 int prev_after_depth = 0;
418 #warning parlist ... could be nicer ?
419 if (start != ownerParagraphs().begin()) {
420 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
424 int const depth = pit->params().depth();
425 if (type == bv_funcs::INC_DEPTH) {
426 if (depth < prev_after_depth
427 && pit->layout()->labeltype != LABEL_BIBLIO) {
430 pit->params().depth(depth + 1);
435 pit->params().depth(depth - 1);
438 prev_after_depth = pit->getMaxDepthAfter();
450 redoParagraphs(start, pastend);
452 // We need to actually move the text->cursor. I don't
453 // understand why ...
454 LyXCursor tmpcursor = cursor;
456 // we have to reset the visual selection because the
457 // geometry could have changed
458 if (selection.set()) {
459 setCursor(selection.start.par(), selection.start.pos());
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos());
464 // this handles the counter labels, and also fixes up
465 // depth values for follow-on (child) paragraphs
469 setCursor(tmpcursor.par(), tmpcursor.pos());
475 // set font over selection and make a total rebreak of those paragraphs
476 void LyXText::setFont(LyXFont const & font, bool toggleall)
478 // if there is no selection just set the current_font
479 if (!selection.set()) {
480 // Determine basis font
482 if (cursor.pos() < cursor.par()->beginningOfBody()) {
483 layoutfont = getLabelFont(cursor.par());
485 layoutfont = getLayoutFont(cursor.par());
487 // Update current font
488 real_current_font.update(font,
489 bv()->buffer()->params.language,
492 // Reduce to implicit settings
493 current_font = real_current_font;
494 current_font.reduce(layoutfont);
495 // And resolve it completely
496 real_current_font.realize(layoutfont);
501 LyXCursor tmpcursor = cursor; // store the current cursor
503 // ok we have a selection. This is always between sel_start_cursor
504 // and sel_end cursor
506 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
508 cursor = selection.start;
509 while (cursor.par() != selection.end.par() ||
510 cursor.pos() < selection.end.pos())
512 if (cursor.pos() < cursor.par()->size()) {
513 // an open footnote should behave like a closed one
514 setCharFont(cursor.par(), cursor.pos(),
516 cursor.pos(cursor.pos() + 1);
519 cursor.par(boost::next(cursor.par()));
524 redoParagraph(selection.start.par());
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 // rebreaks all paragraphs between the specified pars
539 // This function is needed after SetLayout and SetFont etc.
540 void LyXText::redoParagraphs(ParagraphList::iterator start,
541 ParagraphList::iterator end)
543 for ( ; start != end; ++start)
544 redoParagraph(start);
548 void LyXText::redoParagraph(ParagraphList::iterator pit)
550 RowList::iterator rit = pit->rows.begin();
551 RowList::iterator end = pit->rows.end();
553 // remove rows of paragraph, keep track of height changes
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
559 InsetList::iterator ii = pit->insetlist.begin();
560 InsetList::iterator iend = pit->insetlist.end();
561 for (; ii != iend; ++ii) {
563 MetricsInfo mi(bv(), getFont(pit, ii->pos), 0);
564 ii->inset->metrics(mi, dim);
567 // rebreak the paragraph
568 for (pos_type z = 0; z < pit->size() + 1; ) {
570 z = rowBreakPoint(pit, row) + 1;
572 pit->rows.push_back(row);
575 // set height and fill of rows
576 for (rit = pit->rows.begin(); rit != end; ++rit) {
577 rit->fill(fill(pit, rit, workWidth()));
578 prepareToPrint(pit, rit);
579 setHeightOfRow(pit, rit);
582 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
586 void LyXText::fullRebreak()
588 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
590 selection.cursor = cursor;
594 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
596 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
597 // << " workWidth: " << workWidth() << endl;
598 //Assert(mi.base.textwidth);
605 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
608 dim.asc = firstRow()->ascent_of_text();
609 dim.des = height - dim.asc;
610 dim.wid = std::max(mi.base.textwidth, int(width));
614 // important for the screen
617 // the cursor set functions have a special mechanism. When they
618 // realize, that you left an empty paragraph, they will delete it.
619 // They also delete the corresponding row
621 // need the selection cursor:
622 void LyXText::setSelection()
624 TextCursor::setSelection();
629 void LyXText::clearSelection()
631 TextCursor::clearSelection();
633 // reset this in the bv_owner!
634 if (bv_owner && bv_owner->text)
635 bv_owner->text->xsel_cache.set(false);
639 void LyXText::cursorHome()
641 setCursor(cursor.par(), cursorRow()->pos());
645 void LyXText::cursorEnd()
647 if (cursor.par()->empty())
650 RowList::iterator rit = cursorRow();
651 RowList::iterator next_rit = boost::next(rit);
652 RowList::iterator end = boost::next(rit);
653 ParagraphList::iterator pit = cursor.par();
654 pos_type last_pos = lastPos(*pit, rit);
656 if (next_rit == end) {
660 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
665 setCursor(pit, last_pos);
669 void LyXText::cursorTop()
671 setCursor(ownerParagraphs().begin(), 0);
675 void LyXText::cursorBottom()
677 ParagraphList::iterator lastpit =
678 boost::prior(ownerParagraphs().end());
679 setCursor(lastpit, lastpit->size());
683 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
685 // If the mask is completely neutral, tell user
686 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
687 // Could only happen with user style
688 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
692 // Try implicit word selection
693 // If there is a change in the language the implicit word selection
695 LyXCursor resetCursor = cursor;
696 bool implicitSelection = (font.language() == ignore_language
697 && font.number() == LyXFont::IGNORE)
698 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
701 setFont(font, toggleall);
703 // Implicit selections are cleared afterwards
704 //and cursor is set to the original position.
705 if (implicitSelection) {
707 cursor = resetCursor;
708 setCursor(cursor.par(), cursor.pos());
709 selection.cursor = cursor;
714 string LyXText::getStringToIndex()
716 // Try implicit word selection
717 // If there is a change in the language the implicit word selection
719 LyXCursor const reset_cursor = cursor;
720 bool const implicitSelection =
721 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
724 if (!selection.set())
725 bv()->owner()->message(_("Nothing to index!"));
726 else if (selection.start.par() != selection.end.par())
727 bv()->owner()->message(_("Cannot index more than one paragraph!"));
729 idxstring = selectionAsString(bv()->buffer(), false);
731 // Reset cursors to their original position.
732 cursor = reset_cursor;
733 setCursor(cursor.par(), cursor.pos());
734 selection.cursor = cursor;
736 // Clear the implicit selection.
737 if (implicitSelection)
744 // the DTP switches for paragraphs. LyX will store them in the first
745 // physical paragraph. When a paragraph is broken, the top settings rest,
746 // the bottom settings are given to the new one. So I can make sure,
747 // they do not duplicate themself and you cannnot make dirty things with
750 void LyXText::setParagraph(bool line_top, bool line_bottom,
751 bool pagebreak_top, bool pagebreak_bottom,
752 VSpace const & space_top,
753 VSpace const & space_bottom,
754 Spacing const & spacing,
756 string const & labelwidthstring,
759 LyXCursor tmpcursor = cursor;
760 if (!selection.set()) {
761 selection.start = cursor;
762 selection.end = cursor;
765 // make sure that the depth behind the selection are restored, too
766 ParagraphList::iterator endpit = boost::next(selection.end.par());
767 ParagraphList::iterator undoendpit = endpit;
768 ParagraphList::iterator pars_end = ownerParagraphs().end();
770 if (endpit != pars_end && endpit->getDepth()) {
771 while (endpit != pars_end && endpit->getDepth()) {
775 } else if (endpit != pars_end) {
776 // because of parindents etc.
780 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
781 boost::prior(undoendpit));
784 ParagraphList::iterator tmppit = selection.end.par();
786 while (tmppit != boost::prior(selection.start.par())) {
787 setCursor(tmppit, 0);
789 ParagraphList::iterator pit = cursor.par();
790 ParagraphParameters & params = pit->params();
792 params.lineTop(line_top);
793 params.lineBottom(line_bottom);
794 params.pagebreakTop(pagebreak_top);
795 params.pagebreakBottom(pagebreak_bottom);
796 params.spaceTop(space_top);
797 params.spaceBottom(space_bottom);
798 params.spacing(spacing);
799 // does the layout allow the new alignment?
800 LyXLayout_ptr const & layout = pit->layout();
802 if (align == LYX_ALIGN_LAYOUT)
803 align = layout->align;
804 if (align & layout->alignpossible) {
805 if (align == layout->align)
806 params.align(LYX_ALIGN_LAYOUT);
810 pit->setLabelWidthString(labelwidthstring);
811 params.noindent(noindent);
812 tmppit = boost::prior(pit);
815 redoParagraphs(selection.start.par(), endpit);
818 setCursor(selection.start.par(), selection.start.pos());
819 selection.cursor = cursor;
820 setCursor(selection.end.par(), selection.end.pos());
822 setCursor(tmpcursor.par(), tmpcursor.pos());
828 // set the counter of a paragraph. This includes the labels
829 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
831 LyXTextClass const & textclass = buf->params.getLyXTextClass();
832 LyXLayout_ptr const & layout = pit->layout();
834 if (pit != ownerParagraphs().begin()) {
836 pit->params().appendix(boost::prior(pit)->params().appendix());
837 if (!pit->params().appendix() &&
838 pit->params().startOfAppendix()) {
839 pit->params().appendix(true);
840 textclass.counters().reset();
842 pit->enumdepth = boost::prior(pit)->enumdepth;
843 pit->itemdepth = boost::prior(pit)->itemdepth;
845 pit->params().appendix(pit->params().startOfAppendix());
850 // Maybe we have to increment the enumeration depth.
851 // BUT, enumeration in a footnote is considered in isolation from its
852 // surrounding paragraph so don't increment if this is the
853 // first line of the footnote
854 // AND, bibliographies can't have their depth changed ie. they
855 // are always of depth 0
856 if (pit != ownerParagraphs().begin()
857 && boost::prior(pit)->getDepth() < pit->getDepth()
858 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
859 && pit->enumdepth < 3
860 && layout->labeltype != LABEL_BIBLIO) {
864 // Maybe we have to decrement the enumeration depth, see note above
865 if (pit != ownerParagraphs().begin()
866 && boost::prior(pit)->getDepth() > pit->getDepth()
867 && layout->labeltype != LABEL_BIBLIO) {
868 pit->enumdepth = depthHook(pit, ownerParagraphs(),
869 pit->getDepth())->enumdepth;
872 if (!pit->params().labelString().empty()) {
873 pit->params().labelString(string());
876 if (layout->margintype == MARGIN_MANUAL) {
877 if (pit->params().labelWidthString().empty())
878 pit->setLabelWidthString(layout->labelstring());
880 pit->setLabelWidthString(string());
883 // is it a layout that has an automatic label?
884 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
885 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
889 if (i >= 0 && i <= buf->params.secnumdepth) {
893 textclass.counters().step(layout->latexname());
895 // Is there a label? Useful for Chapter layout
896 if (!pit->params().appendix()) {
897 s << buf->B_(layout->labelstring());
899 s << buf->B_(layout->labelstring_appendix());
902 // Use of an integer is here less than elegant. For now.
903 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
904 if (!pit->params().appendix()) {
905 numbertype = "sectioning";
907 numbertype = "appendix";
908 if (pit->isRightToLeftPar(buf->params))
915 << textclass.counters()
916 .numberLabel(layout->latexname(),
917 numbertype, langtype, head);
919 pit->params().labelString(STRCONV(s.str()));
921 // reset enum counters
922 textclass.counters().reset("enum");
923 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
924 textclass.counters().reset("enum");
925 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
927 // Yes I know this is a really, really! bad solution
929 string enumcounter("enum");
931 switch (pit->enumdepth) {
943 // not a valid enumdepth...
947 textclass.counters().step(enumcounter);
949 s << textclass.counters()
950 .numberLabel(enumcounter, "enumeration");
951 pit->params().labelString(STRCONV(s.str()));
953 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
954 textclass.counters().step("bibitem");
955 int number = textclass.counters().value("bibitem");
956 if (pit->bibitem()) {
957 pit->bibitem()->setCounter(number);
958 pit->params().labelString(layout->labelstring());
960 // In biblio should't be following counters but...
962 string s = buf->B_(layout->labelstring());
965 if (layout->labeltype == LABEL_SENSITIVE) {
966 ParagraphList::iterator end = ownerParagraphs().end();
967 ParagraphList::iterator tmppit = pit;
970 while (tmppit != end && tmppit->inInset()
971 // the single '=' is intended below
972 && (in = tmppit->inInset()->owner()))
974 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
975 in->lyxCode() == InsetOld::WRAP_CODE) {
979 tmppit = ownerParagraphs().begin();
980 for ( ; tmppit != end; ++tmppit)
981 if (&*tmppit == in->parOwner())
989 if (in->lyxCode() == InsetOld::FLOAT_CODE)
990 type = static_cast<InsetFloat*>(in)->params().type;
991 else if (in->lyxCode() == InsetOld::WRAP_CODE)
992 type = static_cast<InsetWrap*>(in)->params().type;
996 Floating const & fl = textclass.floats().getType(type);
998 textclass.counters().step(fl.type());
1000 // Doesn't work... yet.
1001 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1003 // par->SetLayout(0);
1004 // s = layout->labelstring;
1005 s = _("Senseless: ");
1008 pit->params().labelString(s);
1010 // reset the enumeration counter. They are always reset
1011 // when there is any other layout between
1012 // Just fall-through between the cases so that all
1013 // enum counters deeper than enumdepth is also reset.
1014 switch (pit->enumdepth) {
1016 textclass.counters().reset("enumi");
1018 textclass.counters().reset("enumii");
1020 textclass.counters().reset("enumiii");
1022 textclass.counters().reset("enumiv");
1028 // Updates all counters. Paragraphs with changed label string will be rebroken
1029 void LyXText::updateCounters()
1032 bv()->buffer()->params.getLyXTextClass().counters().reset();
1034 ParagraphList::iterator beg = ownerParagraphs().begin();
1035 ParagraphList::iterator end = ownerParagraphs().end();
1036 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1037 string const oldLabel = pit->params().labelString();
1039 size_t maxdepth = 0;
1041 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1043 if (pit->params().depth() > maxdepth)
1044 pit->params().depth(maxdepth);
1046 // setCounter can potentially change the labelString.
1047 setCounter(bv()->buffer(), pit);
1049 string const & newLabel = pit->params().labelString();
1051 if (oldLabel != newLabel)
1057 void LyXText::insertInset(InsetOld * inset)
1059 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1061 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1063 cursor.par()->insertInset(cursor.pos(), inset);
1064 // Just to rebreak and refresh correctly.
1065 // The character will not be inserted a second time
1066 insertChar(Paragraph::META_INSET);
1067 // If we enter a highly editable inset the cursor should be before
1068 // the inset. After an Undo LyX tries to call inset->edit(...)
1069 // and fails if the cursor is behind the inset and getInset
1070 // does not return the inset!
1071 if (isHighlyEditableInset(inset))
1077 void LyXText::cutSelection(bool doclear, bool realcut)
1079 // Stuff what we got on the clipboard. Even if there is no selection.
1081 // There is a problem with having the stuffing here in that the
1082 // larger the selection the slower LyX will get. This can be
1083 // solved by running the line below only when the selection has
1084 // finished. The solution used currently just works, to make it
1085 // faster we need to be more clever and probably also have more
1086 // calls to stuffClipboard. (Lgb)
1087 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1089 // This doesn't make sense, if there is no selection
1090 if (!selection.set())
1093 // OK, we have a selection. This is always between selection.start
1094 // and selection.end
1096 // make sure that the depth behind the selection are restored, too
1097 ParagraphList::iterator endpit = boost::next(selection.end.par());
1098 ParagraphList::iterator undoendpit = endpit;
1099 ParagraphList::iterator pars_end = ownerParagraphs().end();
1101 if (endpit != pars_end && endpit->getDepth()) {
1102 while (endpit != pars_end && endpit->getDepth()) {
1104 undoendpit = endpit;
1106 } else if (endpit != pars_end) {
1107 // because of parindents etc.
1111 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1112 boost::prior(undoendpit));
1114 endpit = selection.end.par();
1115 int endpos = selection.end.pos();
1117 boost::tie(endpit, endpos) = realcut ?
1118 CutAndPaste::cutSelection(bv()->buffer()->params,
1120 selection.start.par(), endpit,
1121 selection.start.pos(), endpos,
1122 bv()->buffer()->params.textclass,
1124 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1126 selection.start.par(), endpit,
1127 selection.start.pos(), endpos,
1129 // sometimes necessary
1131 selection.start.par()->stripLeadingSpaces();
1133 redoParagraphs(selection.start.par(), boost::next(endpit));
1134 // cutSelection can invalidate the cursor so we need to set
1136 // we prefer the end for when tracking changes
1140 // need a valid cursor. (Lgb)
1143 setCursor(cursor.par(), cursor.pos());
1144 selection.cursor = cursor;
1149 void LyXText::copySelection()
1151 // stuff the selection onto the X clipboard, from an explicit copy request
1152 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1154 // this doesnt make sense, if there is no selection
1155 if (!selection.set())
1158 // ok we have a selection. This is always between selection.start
1159 // and sel_end cursor
1161 // copy behind a space if there is one
1162 while (selection.start.par()->size() > selection.start.pos()
1163 && selection.start.par()->isLineSeparator(selection.start.pos())
1164 && (selection.start.par() != selection.end.par()
1165 || selection.start.pos() < selection.end.pos()))
1166 selection.start.pos(selection.start.pos() + 1);
1168 CutAndPaste::copySelection(selection.start.par(),
1169 selection.end.par(),
1170 selection.start.pos(), selection.end.pos(),
1171 bv()->buffer()->params.textclass);
1175 void LyXText::pasteSelection(size_t sel_index)
1177 // this does not make sense, if there is nothing to paste
1178 if (!CutAndPaste::checkPastePossible())
1181 recordUndo(bv(), Undo::INSERT, cursor.par());
1183 ParagraphList::iterator endpit;
1188 boost::tie(ppp, endpit) =
1189 CutAndPaste::pasteSelection(*bv()->buffer(),
1191 cursor.par(), cursor.pos(),
1192 bv()->buffer()->params.textclass,
1194 bufferErrors(*bv()->buffer(), el);
1195 bv()->showErrorList(_("Paste"));
1197 redoParagraphs(cursor.par(), endpit);
1199 setCursor(cursor.par(), cursor.pos());
1202 selection.cursor = cursor;
1203 setCursor(ppp.first, ppp.second);
1209 void LyXText::setSelectionRange(lyx::pos_type length)
1214 selection.cursor = cursor;
1221 // simple replacing. The font of the first selected character is used
1222 void LyXText::replaceSelectionWithString(string const & str)
1224 recordUndo(bv(), Undo::ATOMIC);
1227 if (!selection.set()) { // create a dummy selection
1228 selection.end = cursor;
1229 selection.start = cursor;
1232 // Get font setting before we cut
1233 pos_type pos = selection.end.pos();
1234 LyXFont const font = selection.start.par()
1235 ->getFontSettings(bv()->buffer()->params,
1236 selection.start.pos());
1238 // Insert the new string
1239 string::const_iterator cit = str.begin();
1240 string::const_iterator end = str.end();
1241 for (; cit != end; ++cit) {
1242 selection.end.par()->insertChar(pos, (*cit), font);
1246 // Cut the selection
1247 cutSelection(true, false);
1253 // needed to insert the selection
1254 void LyXText::insertStringAsLines(string const & str)
1256 ParagraphList::iterator pit = cursor.par();
1257 pos_type pos = cursor.pos();
1258 ParagraphList::iterator endpit = boost::next(cursor.par());
1260 recordUndo(bv(), Undo::ATOMIC);
1262 // only to be sure, should not be neccessary
1265 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1267 redoParagraphs(cursor.par(), endpit);
1268 setCursor(cursor.par(), cursor.pos());
1269 selection.cursor = cursor;
1270 setCursor(pit, pos);
1275 // turns double-CR to single CR, others where converted into one
1276 // blank. Then InsertStringAsLines is called
1277 void LyXText::insertStringAsParagraphs(string const & str)
1279 string linestr(str);
1280 bool newline_inserted = false;
1281 string::size_type const siz = linestr.length();
1283 for (string::size_type i = 0; i < siz; ++i) {
1284 if (linestr[i] == '\n') {
1285 if (newline_inserted) {
1286 // we know that \r will be ignored by
1287 // InsertStringA. Of course, it is a dirty
1288 // trick, but it works...
1289 linestr[i - 1] = '\r';
1293 newline_inserted = true;
1295 } else if (IsPrintable(linestr[i])) {
1296 newline_inserted = false;
1299 insertStringAsLines(linestr);
1303 bool LyXText::setCursor(ParagraphList::iterator pit,
1305 bool setfont, bool boundary)
1307 LyXCursor old_cursor = cursor;
1308 setCursorIntern(pit, pos, setfont, boundary);
1309 return deleteEmptyParagraphMechanism(old_cursor);
1313 void LyXText::redoCursor()
1315 #warning maybe the same for selections?
1316 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1320 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1321 pos_type pos, bool boundary)
1323 Assert(pit != ownerParagraphs().end());
1327 cur.boundary(boundary);
1331 // get the cursor y position in text
1333 RowList::iterator row = getRow(pit, pos, y);
1334 RowList::iterator old_row = row;
1335 // if we are before the first char of this row and are still in the
1336 // same paragraph and there is a previous row then put the cursor on
1337 // the end of the previous row
1338 cur.iy(y + row->baseline());
1339 if (row != pit->rows.begin()
1341 && pos < pit->size()
1342 && pit->getChar(pos) == Paragraph::META_INSET) {
1343 InsetOld * ins = pit->getInset(pos);
1344 if (ins && (ins->needFullRow() || ins->display())) {
1350 // y is now the beginning of the cursor row
1351 y += row->baseline();
1352 // y is now the cursor baseline
1355 pos_type last = lastPrintablePos(*pit, old_row);
1357 // None of these should happen, but we're scaredy-cats
1358 if (pos > pit->size()) {
1359 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1362 } else if (pos > last + 1) {
1363 lyxerr << "dont like 2 please report" << endl;
1364 // This shouldn't happen.
1367 } else if (pos < row->pos()) {
1368 lyxerr << "dont like 3 please report" << endl;
1373 // now get the cursors x position
1374 float x = getCursorX(pit, row, pos, last, boundary);
1377 if (old_row != row) {
1378 x = getCursorX(pit, old_row, pos, last, boundary);
1382 /* We take out this for the time being because 1) the redraw code is not
1383 prepared to this yet and 2) because some good policy has yet to be decided
1384 while editting: for instance how to act on rows being created/deleted
1388 //if the cursor is in a visible row, anchor to it
1390 if (topy < y && y < topy + bv()->workHeight())
1396 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1397 pos_type pos, pos_type last, bool boundary) const
1399 pos_type cursor_vpos = 0;
1400 double x = rit->x();
1401 double fill_separator = rit->fill_separator();
1402 double fill_hfill = rit->fill_hfill();
1403 double fill_label_hfill = rit->fill_label_hfill();
1404 pos_type const rit_pos = rit->pos();
1407 cursor_vpos = rit_pos;
1408 else if (pos > last && !boundary)
1409 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1410 ? rit_pos : last + 1;
1411 else if (pos > rit_pos && (pos > last || boundary))
1412 // Place cursor after char at (logical) position pos - 1
1413 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1414 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1416 // Place cursor before char at (logical) position pos
1417 cursor_vpos = (bidi_level(pos) % 2 == 0)
1418 ? log2vis(pos) : log2vis(pos) + 1;
1420 pos_type body_pos = pit->beginningOfBody();
1422 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1425 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1426 pos_type pos = vis2log(vpos);
1427 if (body_pos > 0 && pos == body_pos - 1) {
1428 x += fill_label_hfill +
1429 font_metrics::width(
1430 pit->layout()->labelsep, getLabelFont(pit));
1431 if (pit->isLineSeparator(body_pos - 1))
1432 x -= singleWidth(pit, body_pos - 1);
1435 if (hfillExpansion(*pit, rit, pos)) {
1436 x += singleWidth(pit, pos);
1437 if (pos >= body_pos)
1440 x += fill_label_hfill;
1441 } else if (pit->isSeparator(pos)) {
1442 x += singleWidth(pit, pos);
1443 if (pos >= body_pos)
1444 x += fill_separator;
1446 x += singleWidth(pit, pos);
1452 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1453 pos_type pos, bool setfont, bool boundary)
1455 setCursor(cursor, pit, pos, boundary);
1461 void LyXText::setCurrentFont()
1463 pos_type pos = cursor.pos();
1464 ParagraphList::iterator pit = cursor.par();
1466 if (cursor.boundary() && pos > 0)
1470 if (pos == pit->size())
1472 else // potentional bug... BUG (Lgb)
1473 if (pit->isSeparator(pos)) {
1474 if (pos > cursorRow()->pos() &&
1475 bidi_level(pos) % 2 ==
1476 bidi_level(pos - 1) % 2)
1478 else if (pos + 1 < pit->size())
1483 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1484 real_current_font = getFont(pit, pos);
1486 if (cursor.pos() == pit->size() &&
1487 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1488 !cursor.boundary()) {
1489 Language const * lang =
1490 pit->getParLanguage(bv()->buffer()->params);
1491 current_font.setLanguage(lang);
1492 current_font.setNumber(LyXFont::OFF);
1493 real_current_font.setLanguage(lang);
1494 real_current_font.setNumber(LyXFont::OFF);
1499 // returns the column near the specified x-coordinate of the row
1500 // x is set to the real beginning of this column
1501 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1502 RowList::iterator rit, int & x, bool & boundary) const
1504 double tmpx = rit->x();
1505 double fill_separator = rit->fill_separator();
1506 double fill_hfill = rit->fill_hfill();
1507 double fill_label_hfill = rit->fill_label_hfill();
1509 pos_type vc = rit->pos();
1510 pos_type last = lastPrintablePos(*pit, rit);
1512 LyXLayout_ptr const & layout = pit->layout();
1514 bool left_side = false;
1516 pos_type body_pos = pit->beginningOfBody();
1517 double last_tmpx = tmpx;
1520 (body_pos - 1 > last ||
1521 !pit->isLineSeparator(body_pos - 1)))
1524 // check for empty row
1530 while (vc <= last && tmpx <= x) {
1533 if (body_pos > 0 && c == body_pos - 1) {
1534 tmpx += fill_label_hfill +
1535 font_metrics::width(layout->labelsep, getLabelFont(pit));
1536 if (pit->isLineSeparator(body_pos - 1))
1537 tmpx -= singleWidth(pit, body_pos - 1);
1540 if (hfillExpansion(*pit, rit, c)) {
1541 tmpx += singleWidth(pit, c);
1545 tmpx += fill_label_hfill;
1546 } else if (pit->isSeparator(c)) {
1547 tmpx += singleWidth(pit, c);
1549 tmpx += fill_separator;
1551 tmpx += singleWidth(pit, c);
1556 if ((tmpx + last_tmpx) / 2 > x) {
1561 if (vc > last + 1) // This shouldn't happen.
1565 // This (rtl_support test) is not needed, but gives
1566 // some speedup if rtl_support == false
1567 bool const lastrow = lyxrc.rtl_support
1568 && boost::next(rit) == pit->rows.end();
1570 // If lastrow is false, we don't need to compute
1571 // the value of rtl.
1572 bool const rtl = (lastrow)
1573 ? pit->isRightToLeftPar(bv()->buffer()->params)
1576 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1577 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1579 else if (vc == rit->pos()) {
1581 if (bidi_level(c) % 2 == 1)
1584 c = vis2log(vc - 1);
1585 bool const rtl = (bidi_level(c) % 2 == 1);
1586 if (left_side == rtl) {
1588 boundary = isBoundary(bv()->buffer(), *pit, c);
1592 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1593 if (bidi_level(last) % 2 == 0)
1594 tmpx -= singleWidth(pit, last);
1596 tmpx += singleWidth(pit, last);
1606 void LyXText::setCursorFromCoordinates(int x, int y)
1608 LyXCursor old_cursor = cursor;
1609 setCursorFromCoordinates(cursor, x, y);
1611 deleteEmptyParagraphMechanism(old_cursor);
1618 * return true if the cursor given is at the end of a row,
1619 * and the next row is filled by an inset that spans an entire
1622 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1624 RowList::iterator row = lt.getRow(cur);
1625 RowList::iterator next = boost::next(row);
1627 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1630 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1633 InsetOld const * inset = cur.par()->getInset(cur.pos());
1634 if (inset->needFullRow() || inset->display())
1642 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1644 // Get the row first.
1645 ParagraphList::iterator pit;
1646 RowList::iterator rit = getRowNearY(y, pit);
1648 pos_type const column = getColumnNearX(pit, rit, x, bound);
1650 cur.pos(rit->pos() + column);
1652 cur.y(y + rit->baseline());
1654 if (beforeFullRowInset(*this, cur)) {
1655 pos_type const last = lastPrintablePos(*pit, rit);
1656 RowList::iterator next_rit = rit;
1657 ParagraphList::iterator next_pit = pit;
1658 nextRow(next_pit, next_rit);
1659 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1660 cur.iy(y + rit->height() + next_rit->baseline());
1665 cur.boundary(bound);
1669 void LyXText::cursorLeft(bool internal)
1671 if (cursor.pos() > 0) {
1672 bool boundary = cursor.boundary();
1673 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1674 if (!internal && !boundary &&
1675 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1676 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1677 } else if (cursor.par() != ownerParagraphs().begin()) {
1678 // steps into the paragraph above
1679 ParagraphList::iterator pit = boost::prior(cursor.par());
1680 setCursor(pit, pit->size());
1685 void LyXText::cursorRight(bool internal)
1687 bool const at_end = (cursor.pos() == cursor.par()->size());
1688 bool const at_newline = !at_end &&
1689 cursor.par()->isNewline(cursor.pos());
1691 if (!internal && cursor.boundary() && !at_newline)
1692 setCursor(cursor.par(), cursor.pos(), true, false);
1694 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1696 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1697 setCursor(cursor.par(), cursor.pos(), true, true);
1698 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1699 setCursor(boost::next(cursor.par()), 0);
1703 void LyXText::cursorUp(bool selecting)
1706 int x = cursor.x_fix();
1707 int y = cursor.y() - cursorRow()->baseline() - 1;
1708 setCursorFromCoordinates(x, y);
1711 int y1 = cursor.iy() - topy;
1714 InsetOld * inset_hit = checkInsetHit(x, y1);
1715 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1716 inset_hit->localDispatch(
1717 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1721 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1722 cursorRow()->baseline() << endl;
1723 setCursorFromCoordinates(cursor.x_fix(),
1724 cursor.y() - cursorRow()->baseline() - 1);
1729 void LyXText::cursorDown(bool selecting)
1732 int x = cursor.x_fix();
1733 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1734 setCursorFromCoordinates(x, y);
1735 if (!selecting && cursorRow() == cursorIRow()) {
1737 int y1 = cursor.iy() - topy;
1740 InsetOld * inset_hit = checkInsetHit(x, y1);
1741 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1742 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1743 inset_hit->localDispatch(cmd);
1747 setCursorFromCoordinates(cursor.x_fix(),
1748 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1753 void LyXText::cursorUpParagraph()
1755 if (cursor.pos() > 0)
1756 setCursor(cursor.par(), 0);
1757 else if (cursor.par() != ownerParagraphs().begin())
1758 setCursor(boost::prior(cursor.par()), 0);
1762 void LyXText::cursorDownParagraph()
1764 ParagraphList::iterator par = cursor.par();
1765 ParagraphList::iterator next_par = boost::next(par);
1767 if (next_par != ownerParagraphs().end())
1768 setCursor(next_par, 0);
1770 setCursor(par, par->size());
1774 // fix the cursor `cur' after a characters has been deleted at `where'
1775 // position. Called by deleteEmptyParagraphMechanism
1776 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1778 // if cursor is not in the paragraph where the delete occured,
1780 if (cur.par() != where.par())
1783 // if cursor position is after the place where the delete occured,
1785 if (cur.pos() > where.pos())
1786 cur.pos(cur.pos()-1);
1788 // check also if we don't want to set the cursor on a spot behind the
1789 // pagragraph because we erased the last character.
1790 if (cur.pos() > cur.par()->size())
1791 cur.pos(cur.par()->size());
1793 // recompute row et al. for this cursor
1794 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1798 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1800 // Would be wrong to delete anything if we have a selection.
1801 if (selection.set())
1804 // We allow all kinds of "mumbo-jumbo" when freespacing.
1805 if (old_cursor.par()->isFreeSpacing())
1808 /* Ok I'll put some comments here about what is missing.
1809 I have fixed BackSpace (and thus Delete) to not delete
1810 double-spaces automagically. I have also changed Cut,
1811 Copy and Paste to hopefully do some sensible things.
1812 There are still some small problems that can lead to
1813 double spaces stored in the document file or space at
1814 the beginning of paragraphs. This happens if you have
1815 the cursor between to spaces and then save. Or if you
1816 cut and paste and the selection have a space at the
1817 beginning and then save right after the paste. I am
1818 sure none of these are very hard to fix, but I will
1819 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1820 that I can get some feedback. (Lgb)
1823 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1824 // delete the LineSeparator.
1827 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1828 // delete the LineSeparator.
1831 // If the pos around the old_cursor were spaces, delete one of them.
1832 if (old_cursor.par() != cursor.par()
1833 || old_cursor.pos() != cursor.pos()) {
1835 // Only if the cursor has really moved
1836 if (old_cursor.pos() > 0
1837 && old_cursor.pos() < old_cursor.par()->size()
1838 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1839 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1840 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1841 redoParagraph(old_cursor.par());
1845 #ifdef WITH_WARNINGS
1846 #warning This will not work anymore when we have multiple views of the same buffer
1847 // In this case, we will have to correct also the cursors held by
1848 // other bufferviews. It will probably be easier to do that in a more
1849 // automated way in LyXCursor code. (JMarc 26/09/2001)
1851 // correct all cursors held by the LyXText
1852 fixCursorAfterDelete(cursor, old_cursor);
1853 fixCursorAfterDelete(selection.cursor, old_cursor);
1854 fixCursorAfterDelete(selection.start, old_cursor);
1855 fixCursorAfterDelete(selection.end, old_cursor);
1860 // don't delete anything if this is the ONLY paragraph!
1861 if (ownerParagraphs().size() == 1)
1864 // Do not delete empty paragraphs with keepempty set.
1865 if (old_cursor.par()->allowEmpty())
1868 // only do our magic if we changed paragraph
1869 if (old_cursor.par() == cursor.par())
1872 // record if we have deleted a paragraph
1873 // we can't possibly have deleted a paragraph before this point
1874 bool deleted = false;
1876 if (old_cursor.par()->empty() ||
1877 (old_cursor.par()->size() == 1 &&
1878 old_cursor.par()->isLineSeparator(0))) {
1879 // ok, we will delete something
1880 LyXCursor tmpcursor;
1884 bool selection_position_was_oldcursor_position = (
1885 selection.cursor.par() == old_cursor.par()
1886 && selection.cursor.pos() == old_cursor.pos());
1889 cursor = old_cursor; // that undo can restore the right cursor position
1891 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1892 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1895 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1899 ownerParagraphs().erase(old_cursor.par());
1903 setCursorIntern(cursor.par(), cursor.pos());
1905 if (selection_position_was_oldcursor_position) {
1906 // correct selection
1907 selection.cursor = cursor;
1911 if (old_cursor.par()->stripLeadingSpaces()) {
1912 redoParagraph(old_cursor.par());
1914 setCursorIntern(cursor.par(), cursor.pos());
1915 selection.cursor = cursor;
1922 ParagraphList & LyXText::ownerParagraphs() const
1928 bool LyXText::isInInset() const
1930 // Sub-level has non-null bv owner and non-null inset owner.
1931 return inset_owner != 0;
1935 int defaultRowHeight()
1937 LyXFont const font(LyXFont::ALL_SANE);
1938 return int(font_metrics::maxAscent(font)
1939 + font_metrics::maxDescent(font) * 1.5);