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
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
559 // rebreak the paragraph
560 // insert a new row, starting at position 0
563 pit->rows.push_back(Row(z));
566 z = rowBreakPoint(pit, pit->rows.back());
568 RowList::iterator tmprow = boost::prior(pit->rows.end());
570 if (z >= pit->size())
574 pit->rows.push_back(Row(z));
577 tmprow->fill(fill(pit, tmprow, workWidth()));
578 setHeightOfRow(pit, tmprow);
581 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
585 void LyXText::fullRebreak()
587 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
589 selection.cursor = cursor;
593 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
595 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
596 // << " workWidth: " << workWidth() << endl;
597 //Assert(mi.base.textwidth);
605 ParagraphList::iterator pit = ownerParagraphs().begin();
606 ParagraphList::iterator end = ownerParagraphs().end();
608 for (; pit != end; ++pit) {
611 InsetList::iterator ii = pit->insetlist.begin();
612 InsetList::iterator iend = pit->insetlist.end();
613 for (; ii != iend; ++ii) {
616 #warning FIXME: pos != 0
617 m.base.font = getFont(pit, 0);
618 ii->inset->metrics(m, dim);
625 dim.asc = firstRow()->ascent_of_text();
626 dim.des = height - dim.asc;
627 dim.wid = std::max(mi.base.textwidth, int(width));
631 // important for the screen
634 // the cursor set functions have a special mechanism. When they
635 // realize, that you left an empty paragraph, they will delete it.
636 // They also delete the corresponding row
638 // need the selection cursor:
639 void LyXText::setSelection()
641 TextCursor::setSelection();
646 void LyXText::clearSelection()
648 TextCursor::clearSelection();
650 // reset this in the bv_owner!
651 if (bv_owner && bv_owner->text)
652 bv_owner->text->xsel_cache.set(false);
656 void LyXText::cursorHome()
658 setCursor(cursor.par(), cursorRow()->pos());
662 void LyXText::cursorEnd()
664 if (cursor.par()->empty())
667 RowList::iterator rit = cursorRow();
668 RowList::iterator next_rit = boost::next(rit);
669 RowList::iterator end = boost::next(rit);
670 ParagraphList::iterator pit = cursor.par();
671 pos_type last_pos = lastPos(*pit, rit);
673 if (next_rit == end) {
677 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
682 setCursor(pit, last_pos);
686 void LyXText::cursorTop()
688 setCursor(ownerParagraphs().begin(), 0);
692 void LyXText::cursorBottom()
694 ParagraphList::iterator lastpit =
695 boost::prior(ownerParagraphs().end());
696 setCursor(lastpit, lastpit->size());
700 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
702 // If the mask is completely neutral, tell user
703 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
704 // Could only happen with user style
705 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
709 // Try implicit word selection
710 // If there is a change in the language the implicit word selection
712 LyXCursor resetCursor = cursor;
713 bool implicitSelection = (font.language() == ignore_language
714 && font.number() == LyXFont::IGNORE)
715 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
718 setFont(font, toggleall);
720 // Implicit selections are cleared afterwards
721 //and cursor is set to the original position.
722 if (implicitSelection) {
724 cursor = resetCursor;
725 setCursor(cursor.par(), cursor.pos());
726 selection.cursor = cursor;
731 string LyXText::getStringToIndex()
733 // Try implicit word selection
734 // If there is a change in the language the implicit word selection
736 LyXCursor const reset_cursor = cursor;
737 bool const implicitSelection =
738 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
741 if (!selection.set())
742 bv()->owner()->message(_("Nothing to index!"));
743 else if (selection.start.par() != selection.end.par())
744 bv()->owner()->message(_("Cannot index more than one paragraph!"));
746 idxstring = selectionAsString(bv()->buffer(), false);
748 // Reset cursors to their original position.
749 cursor = reset_cursor;
750 setCursor(cursor.par(), cursor.pos());
751 selection.cursor = cursor;
753 // Clear the implicit selection.
754 if (implicitSelection)
761 // the DTP switches for paragraphs. LyX will store them in the first
762 // physical paragraph. When a paragraph is broken, the top settings rest,
763 // the bottom settings are given to the new one. So I can make sure,
764 // they do not duplicate themself and you cannnot make dirty things with
767 void LyXText::setParagraph(bool line_top, bool line_bottom,
768 bool pagebreak_top, bool pagebreak_bottom,
769 VSpace const & space_top,
770 VSpace const & space_bottom,
771 Spacing const & spacing,
773 string const & labelwidthstring,
776 LyXCursor tmpcursor = cursor;
777 if (!selection.set()) {
778 selection.start = cursor;
779 selection.end = cursor;
782 // make sure that the depth behind the selection are restored, too
783 ParagraphList::iterator endpit = boost::next(selection.end.par());
784 ParagraphList::iterator undoendpit = endpit;
785 ParagraphList::iterator pars_end = ownerParagraphs().end();
787 if (endpit != pars_end && endpit->getDepth()) {
788 while (endpit != pars_end && endpit->getDepth()) {
792 } else if (endpit != pars_end) {
793 // because of parindents etc.
797 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
798 boost::prior(undoendpit));
801 ParagraphList::iterator tmppit = selection.end.par();
803 while (tmppit != boost::prior(selection.start.par())) {
804 setCursor(tmppit, 0);
806 ParagraphList::iterator pit = cursor.par();
807 ParagraphParameters & params = pit->params();
809 params.lineTop(line_top);
810 params.lineBottom(line_bottom);
811 params.pagebreakTop(pagebreak_top);
812 params.pagebreakBottom(pagebreak_bottom);
813 params.spaceTop(space_top);
814 params.spaceBottom(space_bottom);
815 params.spacing(spacing);
816 // does the layout allow the new alignment?
817 LyXLayout_ptr const & layout = pit->layout();
819 if (align == LYX_ALIGN_LAYOUT)
820 align = layout->align;
821 if (align & layout->alignpossible) {
822 if (align == layout->align)
823 params.align(LYX_ALIGN_LAYOUT);
827 pit->setLabelWidthString(labelwidthstring);
828 params.noindent(noindent);
829 tmppit = boost::prior(pit);
832 redoParagraphs(selection.start.par(), endpit);
835 setCursor(selection.start.par(), selection.start.pos());
836 selection.cursor = cursor;
837 setCursor(selection.end.par(), selection.end.pos());
839 setCursor(tmpcursor.par(), tmpcursor.pos());
845 // set the counter of a paragraph. This includes the labels
846 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
848 LyXTextClass const & textclass = buf->params.getLyXTextClass();
849 LyXLayout_ptr const & layout = pit->layout();
851 if (pit != ownerParagraphs().begin()) {
853 pit->params().appendix(boost::prior(pit)->params().appendix());
854 if (!pit->params().appendix() &&
855 pit->params().startOfAppendix()) {
856 pit->params().appendix(true);
857 textclass.counters().reset();
859 pit->enumdepth = boost::prior(pit)->enumdepth;
860 pit->itemdepth = boost::prior(pit)->itemdepth;
862 pit->params().appendix(pit->params().startOfAppendix());
867 // Maybe we have to increment the enumeration depth.
868 // BUT, enumeration in a footnote is considered in isolation from its
869 // surrounding paragraph so don't increment if this is the
870 // first line of the footnote
871 // AND, bibliographies can't have their depth changed ie. they
872 // are always of depth 0
873 if (pit != ownerParagraphs().begin()
874 && boost::prior(pit)->getDepth() < pit->getDepth()
875 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
876 && pit->enumdepth < 3
877 && layout->labeltype != LABEL_BIBLIO) {
881 // Maybe we have to decrement the enumeration depth, see note above
882 if (pit != ownerParagraphs().begin()
883 && boost::prior(pit)->getDepth() > pit->getDepth()
884 && layout->labeltype != LABEL_BIBLIO) {
885 pit->enumdepth = depthHook(pit, ownerParagraphs(),
886 pit->getDepth())->enumdepth;
889 if (!pit->params().labelString().empty()) {
890 pit->params().labelString(string());
893 if (layout->margintype == MARGIN_MANUAL) {
894 if (pit->params().labelWidthString().empty())
895 pit->setLabelWidthString(layout->labelstring());
897 pit->setLabelWidthString(string());
900 // is it a layout that has an automatic label?
901 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
902 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
906 if (i >= 0 && i <= buf->params.secnumdepth) {
910 textclass.counters().step(layout->latexname());
912 // Is there a label? Useful for Chapter layout
913 if (!pit->params().appendix()) {
914 s << buf->B_(layout->labelstring());
916 s << buf->B_(layout->labelstring_appendix());
919 // Use of an integer is here less than elegant. For now.
920 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
921 if (!pit->params().appendix()) {
922 numbertype = "sectioning";
924 numbertype = "appendix";
925 if (pit->isRightToLeftPar(buf->params))
932 << textclass.counters()
933 .numberLabel(layout->latexname(),
934 numbertype, langtype, head);
936 pit->params().labelString(STRCONV(s.str()));
938 // reset enum counters
939 textclass.counters().reset("enum");
940 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
941 textclass.counters().reset("enum");
942 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
944 // Yes I know this is a really, really! bad solution
946 string enumcounter("enum");
948 switch (pit->enumdepth) {
960 // not a valid enumdepth...
964 textclass.counters().step(enumcounter);
966 s << textclass.counters()
967 .numberLabel(enumcounter, "enumeration");
968 pit->params().labelString(STRCONV(s.str()));
970 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
971 textclass.counters().step("bibitem");
972 int number = textclass.counters().value("bibitem");
973 if (pit->bibitem()) {
974 pit->bibitem()->setCounter(number);
975 pit->params().labelString(layout->labelstring());
977 // In biblio should't be following counters but...
979 string s = buf->B_(layout->labelstring());
982 if (layout->labeltype == LABEL_SENSITIVE) {
983 ParagraphList::iterator end = ownerParagraphs().end();
984 ParagraphList::iterator tmppit = pit;
987 while (tmppit != end && tmppit->inInset()
988 // the single '=' is intended below
989 && (in = tmppit->inInset()->owner()))
991 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
992 in->lyxCode() == InsetOld::WRAP_CODE) {
996 tmppit = ownerParagraphs().begin();
997 for ( ; tmppit != end; ++tmppit)
998 if (&*tmppit == in->parOwner())
1006 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1007 type = static_cast<InsetFloat*>(in)->params().type;
1008 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1009 type = static_cast<InsetWrap*>(in)->params().type;
1013 Floating const & fl = textclass.floats().getType(type);
1015 textclass.counters().step(fl.type());
1017 // Doesn't work... yet.
1018 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1020 // par->SetLayout(0);
1021 // s = layout->labelstring;
1022 s = _("Senseless: ");
1025 pit->params().labelString(s);
1027 // reset the enumeration counter. They are always reset
1028 // when there is any other layout between
1029 // Just fall-through between the cases so that all
1030 // enum counters deeper than enumdepth is also reset.
1031 switch (pit->enumdepth) {
1033 textclass.counters().reset("enumi");
1035 textclass.counters().reset("enumii");
1037 textclass.counters().reset("enumiii");
1039 textclass.counters().reset("enumiv");
1045 // Updates all counters. Paragraphs with changed label string will be rebroken
1046 void LyXText::updateCounters()
1049 bv()->buffer()->params.getLyXTextClass().counters().reset();
1051 ParagraphList::iterator beg = ownerParagraphs().begin();
1052 ParagraphList::iterator end = ownerParagraphs().end();
1053 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1054 string const oldLabel = pit->params().labelString();
1056 size_t maxdepth = 0;
1058 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1060 if (pit->params().depth() > maxdepth)
1061 pit->params().depth(maxdepth);
1063 // setCounter can potentially change the labelString.
1064 setCounter(bv()->buffer(), pit);
1066 string const & newLabel = pit->params().labelString();
1068 if (oldLabel != newLabel)
1074 void LyXText::insertInset(InsetOld * inset)
1076 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1078 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1080 cursor.par()->insertInset(cursor.pos(), inset);
1081 // Just to rebreak and refresh correctly.
1082 // The character will not be inserted a second time
1083 insertChar(Paragraph::META_INSET);
1084 // If we enter a highly editable inset the cursor should be before
1085 // the inset. After an Undo LyX tries to call inset->edit(...)
1086 // and fails if the cursor is behind the inset and getInset
1087 // does not return the inset!
1088 if (isHighlyEditableInset(inset))
1094 void LyXText::cutSelection(bool doclear, bool realcut)
1096 // Stuff what we got on the clipboard. Even if there is no selection.
1098 // There is a problem with having the stuffing here in that the
1099 // larger the selection the slower LyX will get. This can be
1100 // solved by running the line below only when the selection has
1101 // finished. The solution used currently just works, to make it
1102 // faster we need to be more clever and probably also have more
1103 // calls to stuffClipboard. (Lgb)
1104 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1106 // This doesn't make sense, if there is no selection
1107 if (!selection.set())
1110 // OK, we have a selection. This is always between selection.start
1111 // and selection.end
1113 // make sure that the depth behind the selection are restored, too
1114 ParagraphList::iterator endpit = boost::next(selection.end.par());
1115 ParagraphList::iterator undoendpit = endpit;
1116 ParagraphList::iterator pars_end = ownerParagraphs().end();
1118 if (endpit != pars_end && endpit->getDepth()) {
1119 while (endpit != pars_end && endpit->getDepth()) {
1121 undoendpit = endpit;
1123 } else if (endpit != pars_end) {
1124 // because of parindents etc.
1128 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1129 boost::prior(undoendpit));
1131 endpit = selection.end.par();
1132 int endpos = selection.end.pos();
1134 boost::tie(endpit, endpos) = realcut ?
1135 CutAndPaste::cutSelection(bv()->buffer()->params,
1137 selection.start.par(), endpit,
1138 selection.start.pos(), endpos,
1139 bv()->buffer()->params.textclass,
1141 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1143 selection.start.par(), endpit,
1144 selection.start.pos(), endpos,
1146 // sometimes necessary
1148 selection.start.par()->stripLeadingSpaces();
1150 redoParagraphs(selection.start.par(), boost::next(endpit));
1151 // cutSelection can invalidate the cursor so we need to set
1153 // we prefer the end for when tracking changes
1157 // need a valid cursor. (Lgb)
1160 setCursor(cursor.par(), cursor.pos());
1161 selection.cursor = cursor;
1166 void LyXText::copySelection()
1168 // stuff the selection onto the X clipboard, from an explicit copy request
1169 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1171 // this doesnt make sense, if there is no selection
1172 if (!selection.set())
1175 // ok we have a selection. This is always between selection.start
1176 // and sel_end cursor
1178 // copy behind a space if there is one
1179 while (selection.start.par()->size() > selection.start.pos()
1180 && selection.start.par()->isLineSeparator(selection.start.pos())
1181 && (selection.start.par() != selection.end.par()
1182 || selection.start.pos() < selection.end.pos()))
1183 selection.start.pos(selection.start.pos() + 1);
1185 CutAndPaste::copySelection(selection.start.par(),
1186 selection.end.par(),
1187 selection.start.pos(), selection.end.pos(),
1188 bv()->buffer()->params.textclass);
1192 void LyXText::pasteSelection(size_t sel_index)
1194 // this does not make sense, if there is nothing to paste
1195 if (!CutAndPaste::checkPastePossible())
1198 recordUndo(bv(), Undo::INSERT, cursor.par());
1200 ParagraphList::iterator endpit;
1205 boost::tie(ppp, endpit) =
1206 CutAndPaste::pasteSelection(*bv()->buffer(),
1208 cursor.par(), cursor.pos(),
1209 bv()->buffer()->params.textclass,
1211 bufferErrors(*bv()->buffer(), el);
1212 bv()->showErrorList(_("Paste"));
1214 redoParagraphs(cursor.par(), endpit);
1216 setCursor(cursor.par(), cursor.pos());
1219 selection.cursor = cursor;
1220 setCursor(ppp.first, ppp.second);
1226 void LyXText::setSelectionRange(lyx::pos_type length)
1231 selection.cursor = cursor;
1238 // simple replacing. The font of the first selected character is used
1239 void LyXText::replaceSelectionWithString(string const & str)
1241 recordUndo(bv(), Undo::ATOMIC);
1244 if (!selection.set()) { // create a dummy selection
1245 selection.end = cursor;
1246 selection.start = cursor;
1249 // Get font setting before we cut
1250 pos_type pos = selection.end.pos();
1251 LyXFont const font = selection.start.par()
1252 ->getFontSettings(bv()->buffer()->params,
1253 selection.start.pos());
1255 // Insert the new string
1256 string::const_iterator cit = str.begin();
1257 string::const_iterator end = str.end();
1258 for (; cit != end; ++cit) {
1259 selection.end.par()->insertChar(pos, (*cit), font);
1263 // Cut the selection
1264 cutSelection(true, false);
1270 // needed to insert the selection
1271 void LyXText::insertStringAsLines(string const & str)
1273 ParagraphList::iterator pit = cursor.par();
1274 pos_type pos = cursor.pos();
1275 ParagraphList::iterator endpit = boost::next(cursor.par());
1277 recordUndo(bv(), Undo::ATOMIC);
1279 // only to be sure, should not be neccessary
1282 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1284 redoParagraphs(cursor.par(), endpit);
1285 setCursor(cursor.par(), cursor.pos());
1286 selection.cursor = cursor;
1287 setCursor(pit, pos);
1292 // turns double-CR to single CR, others where converted into one
1293 // blank. Then InsertStringAsLines is called
1294 void LyXText::insertStringAsParagraphs(string const & str)
1296 string linestr(str);
1297 bool newline_inserted = false;
1298 string::size_type const siz = linestr.length();
1300 for (string::size_type i = 0; i < siz; ++i) {
1301 if (linestr[i] == '\n') {
1302 if (newline_inserted) {
1303 // we know that \r will be ignored by
1304 // InsertStringA. Of course, it is a dirty
1305 // trick, but it works...
1306 linestr[i - 1] = '\r';
1310 newline_inserted = true;
1312 } else if (IsPrintable(linestr[i])) {
1313 newline_inserted = false;
1316 insertStringAsLines(linestr);
1320 bool LyXText::setCursor(ParagraphList::iterator pit,
1322 bool setfont, bool boundary)
1324 LyXCursor old_cursor = cursor;
1325 setCursorIntern(pit, pos, setfont, boundary);
1326 return deleteEmptyParagraphMechanism(old_cursor);
1330 void LyXText::redoCursor()
1332 #warning maybe the same for selections?
1333 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1337 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1338 pos_type pos, bool boundary)
1340 Assert(pit != ownerParagraphs().end());
1344 cur.boundary(boundary);
1348 // get the cursor y position in text
1350 RowList::iterator row = getRow(pit, pos, y);
1351 RowList::iterator old_row = row;
1352 // if we are before the first char of this row and are still in the
1353 // same paragraph and there is a previous row then put the cursor on
1354 // the end of the previous row
1355 cur.iy(y + row->baseline());
1356 if (row != pit->rows.begin()
1358 && pos < pit->size()
1359 && pit->getChar(pos) == Paragraph::META_INSET) {
1360 InsetOld * ins = pit->getInset(pos);
1361 if (ins && (ins->needFullRow() || ins->display())) {
1367 // y is now the beginning of the cursor row
1368 y += row->baseline();
1369 // y is now the cursor baseline
1372 pos_type last = lastPrintablePos(*pit, old_row);
1374 // None of these should happen, but we're scaredy-cats
1375 if (pos > pit->size()) {
1376 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1379 } else if (pos > last + 1) {
1380 lyxerr << "dont like 2 please report" << endl;
1381 // This shouldn't happen.
1384 } else if (pos < row->pos()) {
1385 lyxerr << "dont like 3 please report" << endl;
1390 // now get the cursors x position
1391 float x = getCursorX(pit, row, pos, last, boundary);
1394 if (old_row != row) {
1395 x = getCursorX(pit, old_row, pos, last, boundary);
1399 /* We take out this for the time being because 1) the redraw code is not
1400 prepared to this yet and 2) because some good policy has yet to be decided
1401 while editting: for instance how to act on rows being created/deleted
1405 //if the cursor is in a visible row, anchor to it
1407 if (topy < y && y < topy + bv()->workHeight())
1413 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1414 pos_type pos, pos_type last, bool boundary) const
1416 pos_type cursor_vpos = 0;
1418 double fill_separator;
1420 double fill_label_hfill;
1421 // This call HAS to be here because of the BidiTables!!!
1422 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1425 pos_type const rit_pos = rit->pos();
1428 cursor_vpos = rit_pos;
1429 else if (pos > last && !boundary)
1430 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1431 ? rit_pos : last + 1;
1432 else if (pos > rit_pos && (pos > last || boundary))
1433 // Place cursor after char at (logical) position pos - 1
1434 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1435 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1437 // Place cursor before char at (logical) position pos
1438 cursor_vpos = (bidi_level(pos) % 2 == 0)
1439 ? log2vis(pos) : log2vis(pos) + 1;
1441 pos_type body_pos = pit->beginningOfBody();
1443 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1446 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1447 pos_type pos = vis2log(vpos);
1448 if (body_pos > 0 && pos == body_pos - 1) {
1449 x += fill_label_hfill +
1450 font_metrics::width(
1451 pit->layout()->labelsep, getLabelFont(pit));
1452 if (pit->isLineSeparator(body_pos - 1))
1453 x -= singleWidth(pit, body_pos - 1);
1456 if (hfillExpansion(*pit, rit, pos)) {
1457 x += singleWidth(pit, pos);
1458 if (pos >= body_pos)
1461 x += fill_label_hfill;
1462 } else if (pit->isSeparator(pos)) {
1463 x += singleWidth(pit, pos);
1464 if (pos >= body_pos)
1465 x += fill_separator;
1467 x += singleWidth(pit, pos);
1473 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1474 pos_type pos, bool setfont, bool boundary)
1476 setCursor(cursor, pit, pos, boundary);
1482 void LyXText::setCurrentFont()
1484 pos_type pos = cursor.pos();
1485 ParagraphList::iterator pit = cursor.par();
1487 if (cursor.boundary() && pos > 0)
1491 if (pos == pit->size())
1493 else // potentional bug... BUG (Lgb)
1494 if (pit->isSeparator(pos)) {
1495 if (pos > cursorRow()->pos() &&
1496 bidi_level(pos) % 2 ==
1497 bidi_level(pos - 1) % 2)
1499 else if (pos + 1 < pit->size())
1504 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1505 real_current_font = getFont(pit, pos);
1507 if (cursor.pos() == pit->size() &&
1508 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1509 !cursor.boundary()) {
1510 Language const * lang =
1511 pit->getParLanguage(bv()->buffer()->params);
1512 current_font.setLanguage(lang);
1513 current_font.setNumber(LyXFont::OFF);
1514 real_current_font.setLanguage(lang);
1515 real_current_font.setNumber(LyXFont::OFF);
1520 // returns the column near the specified x-coordinate of the row
1521 // x is set to the real beginning of this column
1522 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1523 RowList::iterator rit, int & x, bool & boundary) const
1526 double fill_separator;
1528 double fill_label_hfill;
1530 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1532 pos_type vc = rit->pos();
1533 pos_type last = lastPrintablePos(*pit, rit);
1535 LyXLayout_ptr const & layout = pit->layout();
1537 bool left_side = false;
1539 pos_type body_pos = pit->beginningOfBody();
1540 double last_tmpx = tmpx;
1543 (body_pos - 1 > last ||
1544 !pit->isLineSeparator(body_pos - 1)))
1547 // check for empty row
1553 while (vc <= last && tmpx <= x) {
1556 if (body_pos > 0 && c == body_pos - 1) {
1557 tmpx += fill_label_hfill +
1558 font_metrics::width(layout->labelsep, getLabelFont(pit));
1559 if (pit->isLineSeparator(body_pos - 1))
1560 tmpx -= singleWidth(pit, body_pos - 1);
1563 if (hfillExpansion(*pit, rit, c)) {
1564 tmpx += singleWidth(pit, c);
1568 tmpx += fill_label_hfill;
1569 } else if (pit->isSeparator(c)) {
1570 tmpx += singleWidth(pit, c);
1572 tmpx += fill_separator;
1574 tmpx += singleWidth(pit, c);
1579 if ((tmpx + last_tmpx) / 2 > x) {
1584 if (vc > last + 1) // This shouldn't happen.
1588 // This (rtl_support test) is not needed, but gives
1589 // some speedup if rtl_support == false
1590 bool const lastrow = lyxrc.rtl_support
1591 && boost::next(rit) == pit->rows.end();
1593 // If lastrow is false, we don't need to compute
1594 // the value of rtl.
1595 bool const rtl = (lastrow)
1596 ? pit->isRightToLeftPar(bv()->buffer()->params)
1599 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1600 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1602 else if (vc == rit->pos()) {
1604 if (bidi_level(c) % 2 == 1)
1607 c = vis2log(vc - 1);
1608 bool const rtl = (bidi_level(c) % 2 == 1);
1609 if (left_side == rtl) {
1611 boundary = isBoundary(bv()->buffer(), *pit, c);
1615 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1616 if (bidi_level(last) % 2 == 0)
1617 tmpx -= singleWidth(pit, last);
1619 tmpx += singleWidth(pit, last);
1629 void LyXText::setCursorFromCoordinates(int x, int y)
1631 LyXCursor old_cursor = cursor;
1632 setCursorFromCoordinates(cursor, x, y);
1634 deleteEmptyParagraphMechanism(old_cursor);
1641 * return true if the cursor given is at the end of a row,
1642 * and the next row is filled by an inset that spans an entire
1645 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1647 RowList::iterator row = lt.getRow(cur);
1648 RowList::iterator next = boost::next(row);
1650 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1653 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1656 InsetOld const * inset = cur.par()->getInset(cur.pos());
1657 if (inset->needFullRow() || inset->display())
1665 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1667 // Get the row first.
1668 ParagraphList::iterator pit;
1669 RowList::iterator row = getRowNearY(y, pit);
1671 pos_type const column = getColumnNearX(pit, row, x, bound);
1673 cur.pos(row->pos() + column);
1675 cur.y(y + row->baseline());
1677 // if (beforeFullRowInset(*this, cur)) {
1678 // pos_type const last = lastPrintablePos(*this, pit, row);
1679 // RowList::iterator next_row = nextRow(row);
1680 // cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1681 // cur.iy(y + row->height() + next_row->baseline());
1686 cur.boundary(bound);
1690 void LyXText::cursorLeft(bool internal)
1692 if (cursor.pos() > 0) {
1693 bool boundary = cursor.boundary();
1694 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1695 if (!internal && !boundary &&
1696 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1697 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1698 } else if (cursor.par() != ownerParagraphs().begin()) {
1699 // steps into the paragraph above
1700 ParagraphList::iterator pit = boost::prior(cursor.par());
1701 setCursor(pit, pit->size());
1706 void LyXText::cursorRight(bool internal)
1708 bool const at_end = (cursor.pos() == cursor.par()->size());
1709 bool const at_newline = !at_end &&
1710 cursor.par()->isNewline(cursor.pos());
1712 if (!internal && cursor.boundary() && !at_newline)
1713 setCursor(cursor.par(), cursor.pos(), true, false);
1715 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1717 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1718 setCursor(cursor.par(), cursor.pos(), true, true);
1719 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1720 setCursor(boost::next(cursor.par()), 0);
1724 void LyXText::cursorUp(bool selecting)
1727 int x = cursor.x_fix();
1728 int y = cursor.y() - cursorRow()->baseline() - 1;
1729 setCursorFromCoordinates(x, y);
1732 int y1 = cursor.iy() - topy;
1735 InsetOld * inset_hit = checkInsetHit(x, y1);
1736 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1737 inset_hit->localDispatch(
1738 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1742 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1743 cursorRow()->baseline() << endl;
1744 setCursorFromCoordinates(cursor.x_fix(),
1745 cursor.y() - cursorRow()->baseline() - 1);
1750 void LyXText::cursorDown(bool selecting)
1753 int x = cursor.x_fix();
1754 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1755 setCursorFromCoordinates(x, y);
1756 if (!selecting && cursorRow() == cursorIRow()) {
1758 int y1 = cursor.iy() - topy;
1761 InsetOld * inset_hit = checkInsetHit(x, y1);
1762 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1763 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1764 inset_hit->localDispatch(cmd);
1768 setCursorFromCoordinates(cursor.x_fix(),
1769 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1774 void LyXText::cursorUpParagraph()
1776 if (cursor.pos() > 0)
1777 setCursor(cursor.par(), 0);
1778 else if (cursor.par() != ownerParagraphs().begin())
1779 setCursor(boost::prior(cursor.par()), 0);
1783 void LyXText::cursorDownParagraph()
1785 ParagraphList::iterator par = cursor.par();
1786 ParagraphList::iterator next_par = boost::next(par);
1788 if (next_par != ownerParagraphs().end())
1789 setCursor(next_par, 0);
1791 setCursor(par, par->size());
1795 // fix the cursor `cur' after a characters has been deleted at `where'
1796 // position. Called by deleteEmptyParagraphMechanism
1797 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1799 // if cursor is not in the paragraph where the delete occured,
1801 if (cur.par() != where.par())
1804 // if cursor position is after the place where the delete occured,
1806 if (cur.pos() > where.pos())
1807 cur.pos(cur.pos()-1);
1809 // check also if we don't want to set the cursor on a spot behind the
1810 // pagragraph because we erased the last character.
1811 if (cur.pos() > cur.par()->size())
1812 cur.pos(cur.par()->size());
1814 // recompute row et al. for this cursor
1815 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1819 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1821 // Would be wrong to delete anything if we have a selection.
1822 if (selection.set())
1825 // We allow all kinds of "mumbo-jumbo" when freespacing.
1826 if (old_cursor.par()->isFreeSpacing())
1829 /* Ok I'll put some comments here about what is missing.
1830 I have fixed BackSpace (and thus Delete) to not delete
1831 double-spaces automagically. I have also changed Cut,
1832 Copy and Paste to hopefully do some sensible things.
1833 There are still some small problems that can lead to
1834 double spaces stored in the document file or space at
1835 the beginning of paragraphs. This happens if you have
1836 the cursor betwenn to spaces and then save. Or if you
1837 cut and paste and the selection have a space at the
1838 beginning and then save right after the paste. I am
1839 sure none of these are very hard to fix, but I will
1840 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1841 that I can get some feedback. (Lgb)
1844 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1845 // delete the LineSeparator.
1848 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1849 // delete the LineSeparator.
1852 // If the pos around the old_cursor were spaces, delete one of them.
1853 if (old_cursor.par() != cursor.par()
1854 || old_cursor.pos() != cursor.pos()) {
1856 // Only if the cursor has really moved
1857 if (old_cursor.pos() > 0
1858 && old_cursor.pos() < old_cursor.par()->size()
1859 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1860 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1861 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1862 redoParagraph(old_cursor.par());
1866 #ifdef WITH_WARNINGS
1867 #warning This will not work anymore when we have multiple views of the same buffer
1868 // In this case, we will have to correct also the cursors held by
1869 // other bufferviews. It will probably be easier to do that in a more
1870 // automated way in LyXCursor code. (JMarc 26/09/2001)
1872 // correct all cursors held by the LyXText
1873 fixCursorAfterDelete(cursor, old_cursor);
1874 fixCursorAfterDelete(selection.cursor, old_cursor);
1875 fixCursorAfterDelete(selection.start, old_cursor);
1876 fixCursorAfterDelete(selection.end, old_cursor);
1881 // don't delete anything if this is the ONLY paragraph!
1882 if (ownerParagraphs().size() == 1)
1885 // Do not delete empty paragraphs with keepempty set.
1886 if (old_cursor.par()->allowEmpty())
1889 // only do our magic if we changed paragraph
1890 if (old_cursor.par() == cursor.par())
1893 // record if we have deleted a paragraph
1894 // we can't possibly have deleted a paragraph before this point
1895 bool deleted = false;
1897 if (old_cursor.par()->empty() ||
1898 (old_cursor.par()->size() == 1 &&
1899 old_cursor.par()->isLineSeparator(0))) {
1900 // ok, we will delete anything
1901 LyXCursor tmpcursor;
1905 bool selection_position_was_oldcursor_position = (
1906 selection.cursor.par() == old_cursor.par()
1907 && selection.cursor.pos() == old_cursor.pos());
1910 cursor = old_cursor; // that undo can restore the right cursor position
1912 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1913 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1916 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1920 ownerParagraphs().erase(old_cursor.par());
1924 setCursorIntern(cursor.par(), cursor.pos());
1926 if (selection_position_was_oldcursor_position) {
1927 // correct selection
1928 selection.cursor = cursor;
1932 if (old_cursor.par()->stripLeadingSpaces()) {
1933 redoParagraph(old_cursor.par());
1935 setCursorIntern(cursor.par(), cursor.pos());
1936 selection.cursor = cursor;
1943 ParagraphList & LyXText::ownerParagraphs() const
1949 bool LyXText::isInInset() const
1951 // Sub-level has non-null bv owner and non-null inset owner.
1952 return inset_owner != 0;
1956 int defaultRowHeight()
1958 LyXFont const font(LyXFont::ALL_SANE);
1959 return int(font_metrics::maxAscent(font)
1960 + font_metrics::maxDescent(font) * 1.5);