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();
558 // rebreak the paragraph
559 for (pos_type z = 0; z < pit->size() + 1; ) {
561 z = rowBreakPoint(pit, row) + 1;
563 pit->rows.push_back(row);
566 // set height and fill of rows
567 for (rit = pit->rows.begin(); rit != end; ++rit) {
568 rit->fill(fill(pit, rit, workWidth()));
569 setHeightOfRow(pit, rit);
572 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
576 void LyXText::fullRebreak()
578 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
580 selection.cursor = cursor;
584 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
586 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
587 // << " workWidth: " << workWidth() << endl;
588 //Assert(mi.base.textwidth);
596 ParagraphList::iterator pit = ownerParagraphs().begin();
597 ParagraphList::iterator end = ownerParagraphs().end();
599 for (; pit != end; ++pit) {
602 InsetList::iterator ii = pit->insetlist.begin();
603 InsetList::iterator iend = pit->insetlist.end();
604 for (; ii != iend; ++ii) {
607 #warning FIXME: pos != 0
608 m.base.font = getFont(pit, 0);
609 ii->inset->metrics(m, dim);
616 dim.asc = firstRow()->ascent_of_text();
617 dim.des = height - dim.asc;
618 dim.wid = std::max(mi.base.textwidth, int(width));
622 // important for the screen
625 // the cursor set functions have a special mechanism. When they
626 // realize, that you left an empty paragraph, they will delete it.
627 // They also delete the corresponding row
629 // need the selection cursor:
630 void LyXText::setSelection()
632 TextCursor::setSelection();
637 void LyXText::clearSelection()
639 TextCursor::clearSelection();
641 // reset this in the bv_owner!
642 if (bv_owner && bv_owner->text)
643 bv_owner->text->xsel_cache.set(false);
647 void LyXText::cursorHome()
649 setCursor(cursor.par(), cursorRow()->pos());
653 void LyXText::cursorEnd()
655 if (cursor.par()->empty())
658 RowList::iterator rit = cursorRow();
659 RowList::iterator next_rit = boost::next(rit);
660 RowList::iterator end = boost::next(rit);
661 ParagraphList::iterator pit = cursor.par();
662 pos_type last_pos = lastPos(*pit, rit);
664 if (next_rit == end) {
668 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
673 setCursor(pit, last_pos);
677 void LyXText::cursorTop()
679 setCursor(ownerParagraphs().begin(), 0);
683 void LyXText::cursorBottom()
685 ParagraphList::iterator lastpit =
686 boost::prior(ownerParagraphs().end());
687 setCursor(lastpit, lastpit->size());
691 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
693 // If the mask is completely neutral, tell user
694 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
695 // Could only happen with user style
696 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
700 // Try implicit word selection
701 // If there is a change in the language the implicit word selection
703 LyXCursor resetCursor = cursor;
704 bool implicitSelection = (font.language() == ignore_language
705 && font.number() == LyXFont::IGNORE)
706 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
709 setFont(font, toggleall);
711 // Implicit selections are cleared afterwards
712 //and cursor is set to the original position.
713 if (implicitSelection) {
715 cursor = resetCursor;
716 setCursor(cursor.par(), cursor.pos());
717 selection.cursor = cursor;
722 string LyXText::getStringToIndex()
724 // Try implicit word selection
725 // If there is a change in the language the implicit word selection
727 LyXCursor const reset_cursor = cursor;
728 bool const implicitSelection =
729 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
732 if (!selection.set())
733 bv()->owner()->message(_("Nothing to index!"));
734 else if (selection.start.par() != selection.end.par())
735 bv()->owner()->message(_("Cannot index more than one paragraph!"));
737 idxstring = selectionAsString(bv()->buffer(), false);
739 // Reset cursors to their original position.
740 cursor = reset_cursor;
741 setCursor(cursor.par(), cursor.pos());
742 selection.cursor = cursor;
744 // Clear the implicit selection.
745 if (implicitSelection)
752 // the DTP switches for paragraphs. LyX will store them in the first
753 // physical paragraph. When a paragraph is broken, the top settings rest,
754 // the bottom settings are given to the new one. So I can make sure,
755 // they do not duplicate themself and you cannnot make dirty things with
758 void LyXText::setParagraph(bool line_top, bool line_bottom,
759 bool pagebreak_top, bool pagebreak_bottom,
760 VSpace const & space_top,
761 VSpace const & space_bottom,
762 Spacing const & spacing,
764 string const & labelwidthstring,
767 LyXCursor tmpcursor = cursor;
768 if (!selection.set()) {
769 selection.start = cursor;
770 selection.end = cursor;
773 // make sure that the depth behind the selection are restored, too
774 ParagraphList::iterator endpit = boost::next(selection.end.par());
775 ParagraphList::iterator undoendpit = endpit;
776 ParagraphList::iterator pars_end = ownerParagraphs().end();
778 if (endpit != pars_end && endpit->getDepth()) {
779 while (endpit != pars_end && endpit->getDepth()) {
783 } else if (endpit != pars_end) {
784 // because of parindents etc.
788 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
789 boost::prior(undoendpit));
792 ParagraphList::iterator tmppit = selection.end.par();
794 while (tmppit != boost::prior(selection.start.par())) {
795 setCursor(tmppit, 0);
797 ParagraphList::iterator pit = cursor.par();
798 ParagraphParameters & params = pit->params();
800 params.lineTop(line_top);
801 params.lineBottom(line_bottom);
802 params.pagebreakTop(pagebreak_top);
803 params.pagebreakBottom(pagebreak_bottom);
804 params.spaceTop(space_top);
805 params.spaceBottom(space_bottom);
806 params.spacing(spacing);
807 // does the layout allow the new alignment?
808 LyXLayout_ptr const & layout = pit->layout();
810 if (align == LYX_ALIGN_LAYOUT)
811 align = layout->align;
812 if (align & layout->alignpossible) {
813 if (align == layout->align)
814 params.align(LYX_ALIGN_LAYOUT);
818 pit->setLabelWidthString(labelwidthstring);
819 params.noindent(noindent);
820 tmppit = boost::prior(pit);
823 redoParagraphs(selection.start.par(), endpit);
826 setCursor(selection.start.par(), selection.start.pos());
827 selection.cursor = cursor;
828 setCursor(selection.end.par(), selection.end.pos());
830 setCursor(tmpcursor.par(), tmpcursor.pos());
836 // set the counter of a paragraph. This includes the labels
837 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
839 LyXTextClass const & textclass = buf->params.getLyXTextClass();
840 LyXLayout_ptr const & layout = pit->layout();
842 if (pit != ownerParagraphs().begin()) {
844 pit->params().appendix(boost::prior(pit)->params().appendix());
845 if (!pit->params().appendix() &&
846 pit->params().startOfAppendix()) {
847 pit->params().appendix(true);
848 textclass.counters().reset();
850 pit->enumdepth = boost::prior(pit)->enumdepth;
851 pit->itemdepth = boost::prior(pit)->itemdepth;
853 pit->params().appendix(pit->params().startOfAppendix());
858 // Maybe we have to increment the enumeration depth.
859 // BUT, enumeration in a footnote is considered in isolation from its
860 // surrounding paragraph so don't increment if this is the
861 // first line of the footnote
862 // AND, bibliographies can't have their depth changed ie. they
863 // are always of depth 0
864 if (pit != ownerParagraphs().begin()
865 && boost::prior(pit)->getDepth() < pit->getDepth()
866 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
867 && pit->enumdepth < 3
868 && layout->labeltype != LABEL_BIBLIO) {
872 // Maybe we have to decrement the enumeration depth, see note above
873 if (pit != ownerParagraphs().begin()
874 && boost::prior(pit)->getDepth() > pit->getDepth()
875 && layout->labeltype != LABEL_BIBLIO) {
876 pit->enumdepth = depthHook(pit, ownerParagraphs(),
877 pit->getDepth())->enumdepth;
880 if (!pit->params().labelString().empty()) {
881 pit->params().labelString(string());
884 if (layout->margintype == MARGIN_MANUAL) {
885 if (pit->params().labelWidthString().empty())
886 pit->setLabelWidthString(layout->labelstring());
888 pit->setLabelWidthString(string());
891 // is it a layout that has an automatic label?
892 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
893 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
897 if (i >= 0 && i <= buf->params.secnumdepth) {
901 textclass.counters().step(layout->latexname());
903 // Is there a label? Useful for Chapter layout
904 if (!pit->params().appendix()) {
905 s << buf->B_(layout->labelstring());
907 s << buf->B_(layout->labelstring_appendix());
910 // Use of an integer is here less than elegant. For now.
911 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
912 if (!pit->params().appendix()) {
913 numbertype = "sectioning";
915 numbertype = "appendix";
916 if (pit->isRightToLeftPar(buf->params))
923 << textclass.counters()
924 .numberLabel(layout->latexname(),
925 numbertype, langtype, head);
927 pit->params().labelString(STRCONV(s.str()));
929 // reset enum counters
930 textclass.counters().reset("enum");
931 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
932 textclass.counters().reset("enum");
933 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
935 // Yes I know this is a really, really! bad solution
937 string enumcounter("enum");
939 switch (pit->enumdepth) {
951 // not a valid enumdepth...
955 textclass.counters().step(enumcounter);
957 s << textclass.counters()
958 .numberLabel(enumcounter, "enumeration");
959 pit->params().labelString(STRCONV(s.str()));
961 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
962 textclass.counters().step("bibitem");
963 int number = textclass.counters().value("bibitem");
964 if (pit->bibitem()) {
965 pit->bibitem()->setCounter(number);
966 pit->params().labelString(layout->labelstring());
968 // In biblio should't be following counters but...
970 string s = buf->B_(layout->labelstring());
973 if (layout->labeltype == LABEL_SENSITIVE) {
974 ParagraphList::iterator end = ownerParagraphs().end();
975 ParagraphList::iterator tmppit = pit;
978 while (tmppit != end && tmppit->inInset()
979 // the single '=' is intended below
980 && (in = tmppit->inInset()->owner()))
982 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
983 in->lyxCode() == InsetOld::WRAP_CODE) {
987 tmppit = ownerParagraphs().begin();
988 for ( ; tmppit != end; ++tmppit)
989 if (&*tmppit == in->parOwner())
997 if (in->lyxCode() == InsetOld::FLOAT_CODE)
998 type = static_cast<InsetFloat*>(in)->params().type;
999 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1000 type = static_cast<InsetWrap*>(in)->params().type;
1004 Floating const & fl = textclass.floats().getType(type);
1006 textclass.counters().step(fl.type());
1008 // Doesn't work... yet.
1009 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1011 // par->SetLayout(0);
1012 // s = layout->labelstring;
1013 s = _("Senseless: ");
1016 pit->params().labelString(s);
1018 // reset the enumeration counter. They are always reset
1019 // when there is any other layout between
1020 // Just fall-through between the cases so that all
1021 // enum counters deeper than enumdepth is also reset.
1022 switch (pit->enumdepth) {
1024 textclass.counters().reset("enumi");
1026 textclass.counters().reset("enumii");
1028 textclass.counters().reset("enumiii");
1030 textclass.counters().reset("enumiv");
1036 // Updates all counters. Paragraphs with changed label string will be rebroken
1037 void LyXText::updateCounters()
1040 bv()->buffer()->params.getLyXTextClass().counters().reset();
1042 ParagraphList::iterator beg = ownerParagraphs().begin();
1043 ParagraphList::iterator end = ownerParagraphs().end();
1044 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1045 string const oldLabel = pit->params().labelString();
1047 size_t maxdepth = 0;
1049 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1051 if (pit->params().depth() > maxdepth)
1052 pit->params().depth(maxdepth);
1054 // setCounter can potentially change the labelString.
1055 setCounter(bv()->buffer(), pit);
1057 string const & newLabel = pit->params().labelString();
1059 if (oldLabel != newLabel)
1065 void LyXText::insertInset(InsetOld * inset)
1067 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1069 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1071 cursor.par()->insertInset(cursor.pos(), inset);
1072 // Just to rebreak and refresh correctly.
1073 // The character will not be inserted a second time
1074 insertChar(Paragraph::META_INSET);
1075 // If we enter a highly editable inset the cursor should be before
1076 // the inset. After an Undo LyX tries to call inset->edit(...)
1077 // and fails if the cursor is behind the inset and getInset
1078 // does not return the inset!
1079 if (isHighlyEditableInset(inset))
1085 void LyXText::cutSelection(bool doclear, bool realcut)
1087 // Stuff what we got on the clipboard. Even if there is no selection.
1089 // There is a problem with having the stuffing here in that the
1090 // larger the selection the slower LyX will get. This can be
1091 // solved by running the line below only when the selection has
1092 // finished. The solution used currently just works, to make it
1093 // faster we need to be more clever and probably also have more
1094 // calls to stuffClipboard. (Lgb)
1095 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1097 // This doesn't make sense, if there is no selection
1098 if (!selection.set())
1101 // OK, we have a selection. This is always between selection.start
1102 // and selection.end
1104 // make sure that the depth behind the selection are restored, too
1105 ParagraphList::iterator endpit = boost::next(selection.end.par());
1106 ParagraphList::iterator undoendpit = endpit;
1107 ParagraphList::iterator pars_end = ownerParagraphs().end();
1109 if (endpit != pars_end && endpit->getDepth()) {
1110 while (endpit != pars_end && endpit->getDepth()) {
1112 undoendpit = endpit;
1114 } else if (endpit != pars_end) {
1115 // because of parindents etc.
1119 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1120 boost::prior(undoendpit));
1122 endpit = selection.end.par();
1123 int endpos = selection.end.pos();
1125 boost::tie(endpit, endpos) = realcut ?
1126 CutAndPaste::cutSelection(bv()->buffer()->params,
1128 selection.start.par(), endpit,
1129 selection.start.pos(), endpos,
1130 bv()->buffer()->params.textclass,
1132 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1134 selection.start.par(), endpit,
1135 selection.start.pos(), endpos,
1137 // sometimes necessary
1139 selection.start.par()->stripLeadingSpaces();
1141 redoParagraphs(selection.start.par(), boost::next(endpit));
1142 // cutSelection can invalidate the cursor so we need to set
1144 // we prefer the end for when tracking changes
1148 // need a valid cursor. (Lgb)
1151 setCursor(cursor.par(), cursor.pos());
1152 selection.cursor = cursor;
1157 void LyXText::copySelection()
1159 // stuff the selection onto the X clipboard, from an explicit copy request
1160 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1162 // this doesnt make sense, if there is no selection
1163 if (!selection.set())
1166 // ok we have a selection. This is always between selection.start
1167 // and sel_end cursor
1169 // copy behind a space if there is one
1170 while (selection.start.par()->size() > selection.start.pos()
1171 && selection.start.par()->isLineSeparator(selection.start.pos())
1172 && (selection.start.par() != selection.end.par()
1173 || selection.start.pos() < selection.end.pos()))
1174 selection.start.pos(selection.start.pos() + 1);
1176 CutAndPaste::copySelection(selection.start.par(),
1177 selection.end.par(),
1178 selection.start.pos(), selection.end.pos(),
1179 bv()->buffer()->params.textclass);
1183 void LyXText::pasteSelection(size_t sel_index)
1185 // this does not make sense, if there is nothing to paste
1186 if (!CutAndPaste::checkPastePossible())
1189 recordUndo(bv(), Undo::INSERT, cursor.par());
1191 ParagraphList::iterator endpit;
1196 boost::tie(ppp, endpit) =
1197 CutAndPaste::pasteSelection(*bv()->buffer(),
1199 cursor.par(), cursor.pos(),
1200 bv()->buffer()->params.textclass,
1202 bufferErrors(*bv()->buffer(), el);
1203 bv()->showErrorList(_("Paste"));
1205 redoParagraphs(cursor.par(), endpit);
1207 setCursor(cursor.par(), cursor.pos());
1210 selection.cursor = cursor;
1211 setCursor(ppp.first, ppp.second);
1217 void LyXText::setSelectionRange(lyx::pos_type length)
1222 selection.cursor = cursor;
1229 // simple replacing. The font of the first selected character is used
1230 void LyXText::replaceSelectionWithString(string const & str)
1232 recordUndo(bv(), Undo::ATOMIC);
1235 if (!selection.set()) { // create a dummy selection
1236 selection.end = cursor;
1237 selection.start = cursor;
1240 // Get font setting before we cut
1241 pos_type pos = selection.end.pos();
1242 LyXFont const font = selection.start.par()
1243 ->getFontSettings(bv()->buffer()->params,
1244 selection.start.pos());
1246 // Insert the new string
1247 string::const_iterator cit = str.begin();
1248 string::const_iterator end = str.end();
1249 for (; cit != end; ++cit) {
1250 selection.end.par()->insertChar(pos, (*cit), font);
1254 // Cut the selection
1255 cutSelection(true, false);
1261 // needed to insert the selection
1262 void LyXText::insertStringAsLines(string const & str)
1264 ParagraphList::iterator pit = cursor.par();
1265 pos_type pos = cursor.pos();
1266 ParagraphList::iterator endpit = boost::next(cursor.par());
1268 recordUndo(bv(), Undo::ATOMIC);
1270 // only to be sure, should not be neccessary
1273 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1275 redoParagraphs(cursor.par(), endpit);
1276 setCursor(cursor.par(), cursor.pos());
1277 selection.cursor = cursor;
1278 setCursor(pit, pos);
1283 // turns double-CR to single CR, others where converted into one
1284 // blank. Then InsertStringAsLines is called
1285 void LyXText::insertStringAsParagraphs(string const & str)
1287 string linestr(str);
1288 bool newline_inserted = false;
1289 string::size_type const siz = linestr.length();
1291 for (string::size_type i = 0; i < siz; ++i) {
1292 if (linestr[i] == '\n') {
1293 if (newline_inserted) {
1294 // we know that \r will be ignored by
1295 // InsertStringA. Of course, it is a dirty
1296 // trick, but it works...
1297 linestr[i - 1] = '\r';
1301 newline_inserted = true;
1303 } else if (IsPrintable(linestr[i])) {
1304 newline_inserted = false;
1307 insertStringAsLines(linestr);
1311 bool LyXText::setCursor(ParagraphList::iterator pit,
1313 bool setfont, bool boundary)
1315 LyXCursor old_cursor = cursor;
1316 setCursorIntern(pit, pos, setfont, boundary);
1317 return deleteEmptyParagraphMechanism(old_cursor);
1321 void LyXText::redoCursor()
1323 #warning maybe the same for selections?
1324 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1328 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1329 pos_type pos, bool boundary)
1331 Assert(pit != ownerParagraphs().end());
1335 cur.boundary(boundary);
1339 // get the cursor y position in text
1341 RowList::iterator row = getRow(pit, pos, y);
1342 RowList::iterator old_row = row;
1343 // if we are before the first char of this row and are still in the
1344 // same paragraph and there is a previous row then put the cursor on
1345 // the end of the previous row
1346 cur.iy(y + row->baseline());
1347 if (row != pit->rows.begin()
1349 && pos < pit->size()
1350 && pit->getChar(pos) == Paragraph::META_INSET) {
1351 InsetOld * ins = pit->getInset(pos);
1352 if (ins && (ins->needFullRow() || ins->display())) {
1358 // y is now the beginning of the cursor row
1359 y += row->baseline();
1360 // y is now the cursor baseline
1363 pos_type last = lastPrintablePos(*pit, old_row);
1365 // None of these should happen, but we're scaredy-cats
1366 if (pos > pit->size()) {
1367 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1370 } else if (pos > last + 1) {
1371 lyxerr << "dont like 2 please report" << endl;
1372 // This shouldn't happen.
1375 } else if (pos < row->pos()) {
1376 lyxerr << "dont like 3 please report" << endl;
1381 // now get the cursors x position
1382 float x = getCursorX(pit, row, pos, last, boundary);
1385 if (old_row != row) {
1386 x = getCursorX(pit, old_row, pos, last, boundary);
1390 /* We take out this for the time being because 1) the redraw code is not
1391 prepared to this yet and 2) because some good policy has yet to be decided
1392 while editting: for instance how to act on rows being created/deleted
1396 //if the cursor is in a visible row, anchor to it
1398 if (topy < y && y < topy + bv()->workHeight())
1404 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1405 pos_type pos, pos_type last, bool boundary) const
1407 pos_type cursor_vpos = 0;
1409 double fill_separator;
1411 double fill_label_hfill;
1412 // This call HAS to be here because of the BidiTables!!!
1413 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1416 pos_type const rit_pos = rit->pos();
1419 cursor_vpos = rit_pos;
1420 else if (pos > last && !boundary)
1421 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1422 ? rit_pos : last + 1;
1423 else if (pos > rit_pos && (pos > last || boundary))
1424 // Place cursor after char at (logical) position pos - 1
1425 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1426 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1428 // Place cursor before char at (logical) position pos
1429 cursor_vpos = (bidi_level(pos) % 2 == 0)
1430 ? log2vis(pos) : log2vis(pos) + 1;
1432 pos_type body_pos = pit->beginningOfBody();
1434 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1437 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1438 pos_type pos = vis2log(vpos);
1439 if (body_pos > 0 && pos == body_pos - 1) {
1440 x += fill_label_hfill +
1441 font_metrics::width(
1442 pit->layout()->labelsep, getLabelFont(pit));
1443 if (pit->isLineSeparator(body_pos - 1))
1444 x -= singleWidth(pit, body_pos - 1);
1447 if (hfillExpansion(*pit, rit, pos)) {
1448 x += singleWidth(pit, pos);
1449 if (pos >= body_pos)
1452 x += fill_label_hfill;
1453 } else if (pit->isSeparator(pos)) {
1454 x += singleWidth(pit, pos);
1455 if (pos >= body_pos)
1456 x += fill_separator;
1458 x += singleWidth(pit, pos);
1464 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1465 pos_type pos, bool setfont, bool boundary)
1467 setCursor(cursor, pit, pos, boundary);
1473 void LyXText::setCurrentFont()
1475 pos_type pos = cursor.pos();
1476 ParagraphList::iterator pit = cursor.par();
1478 if (cursor.boundary() && pos > 0)
1482 if (pos == pit->size())
1484 else // potentional bug... BUG (Lgb)
1485 if (pit->isSeparator(pos)) {
1486 if (pos > cursorRow()->pos() &&
1487 bidi_level(pos) % 2 ==
1488 bidi_level(pos - 1) % 2)
1490 else if (pos + 1 < pit->size())
1495 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1496 real_current_font = getFont(pit, pos);
1498 if (cursor.pos() == pit->size() &&
1499 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1500 !cursor.boundary()) {
1501 Language const * lang =
1502 pit->getParLanguage(bv()->buffer()->params);
1503 current_font.setLanguage(lang);
1504 current_font.setNumber(LyXFont::OFF);
1505 real_current_font.setLanguage(lang);
1506 real_current_font.setNumber(LyXFont::OFF);
1511 // returns the column near the specified x-coordinate of the row
1512 // x is set to the real beginning of this column
1513 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1514 RowList::iterator rit, int & x, bool & boundary) const
1517 double fill_separator;
1519 double fill_label_hfill;
1521 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1523 pos_type vc = rit->pos();
1524 pos_type last = lastPrintablePos(*pit, rit);
1526 LyXLayout_ptr const & layout = pit->layout();
1528 bool left_side = false;
1530 pos_type body_pos = pit->beginningOfBody();
1531 double last_tmpx = tmpx;
1534 (body_pos - 1 > last ||
1535 !pit->isLineSeparator(body_pos - 1)))
1538 // check for empty row
1544 while (vc <= last && tmpx <= x) {
1547 if (body_pos > 0 && c == body_pos - 1) {
1548 tmpx += fill_label_hfill +
1549 font_metrics::width(layout->labelsep, getLabelFont(pit));
1550 if (pit->isLineSeparator(body_pos - 1))
1551 tmpx -= singleWidth(pit, body_pos - 1);
1554 if (hfillExpansion(*pit, rit, c)) {
1555 tmpx += singleWidth(pit, c);
1559 tmpx += fill_label_hfill;
1560 } else if (pit->isSeparator(c)) {
1561 tmpx += singleWidth(pit, c);
1563 tmpx += fill_separator;
1565 tmpx += singleWidth(pit, c);
1570 if ((tmpx + last_tmpx) / 2 > x) {
1575 if (vc > last + 1) // This shouldn't happen.
1579 // This (rtl_support test) is not needed, but gives
1580 // some speedup if rtl_support == false
1581 bool const lastrow = lyxrc.rtl_support
1582 && boost::next(rit) == pit->rows.end();
1584 // If lastrow is false, we don't need to compute
1585 // the value of rtl.
1586 bool const rtl = (lastrow)
1587 ? pit->isRightToLeftPar(bv()->buffer()->params)
1590 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1591 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1593 else if (vc == rit->pos()) {
1595 if (bidi_level(c) % 2 == 1)
1598 c = vis2log(vc - 1);
1599 bool const rtl = (bidi_level(c) % 2 == 1);
1600 if (left_side == rtl) {
1602 boundary = isBoundary(bv()->buffer(), *pit, c);
1606 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1607 if (bidi_level(last) % 2 == 0)
1608 tmpx -= singleWidth(pit, last);
1610 tmpx += singleWidth(pit, last);
1620 void LyXText::setCursorFromCoordinates(int x, int y)
1622 LyXCursor old_cursor = cursor;
1623 setCursorFromCoordinates(cursor, x, y);
1625 deleteEmptyParagraphMechanism(old_cursor);
1632 * return true if the cursor given is at the end of a row,
1633 * and the next row is filled by an inset that spans an entire
1636 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1638 RowList::iterator row = lt.getRow(cur);
1639 RowList::iterator next = boost::next(row);
1641 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1644 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1647 InsetOld const * inset = cur.par()->getInset(cur.pos());
1648 if (inset->needFullRow() || inset->display())
1656 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1658 // Get the row first.
1659 ParagraphList::iterator pit;
1660 RowList::iterator rit = getRowNearY(y, pit);
1662 pos_type const column = getColumnNearX(pit, rit, x, bound);
1664 cur.pos(rit->pos() + column);
1666 cur.y(y + rit->baseline());
1668 if (beforeFullRowInset(*this, cur)) {
1669 pos_type const last = lastPrintablePos(*pit, rit);
1670 RowList::iterator next_rit = rit;
1671 ParagraphList::iterator next_pit = pit;
1672 nextRow(next_pit, next_rit);
1673 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1674 cur.iy(y + rit->height() + next_rit->baseline());
1679 cur.boundary(bound);
1683 void LyXText::cursorLeft(bool internal)
1685 if (cursor.pos() > 0) {
1686 bool boundary = cursor.boundary();
1687 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1688 if (!internal && !boundary &&
1689 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1690 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1691 } else if (cursor.par() != ownerParagraphs().begin()) {
1692 // steps into the paragraph above
1693 ParagraphList::iterator pit = boost::prior(cursor.par());
1694 setCursor(pit, pit->size());
1699 void LyXText::cursorRight(bool internal)
1701 bool const at_end = (cursor.pos() == cursor.par()->size());
1702 bool const at_newline = !at_end &&
1703 cursor.par()->isNewline(cursor.pos());
1705 if (!internal && cursor.boundary() && !at_newline)
1706 setCursor(cursor.par(), cursor.pos(), true, false);
1708 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1710 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1711 setCursor(cursor.par(), cursor.pos(), true, true);
1712 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1713 setCursor(boost::next(cursor.par()), 0);
1717 void LyXText::cursorUp(bool selecting)
1720 int x = cursor.x_fix();
1721 int y = cursor.y() - cursorRow()->baseline() - 1;
1722 setCursorFromCoordinates(x, y);
1725 int y1 = cursor.iy() - topy;
1728 InsetOld * inset_hit = checkInsetHit(x, y1);
1729 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1730 inset_hit->localDispatch(
1731 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1735 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1736 cursorRow()->baseline() << endl;
1737 setCursorFromCoordinates(cursor.x_fix(),
1738 cursor.y() - cursorRow()->baseline() - 1);
1743 void LyXText::cursorDown(bool selecting)
1746 int x = cursor.x_fix();
1747 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1748 setCursorFromCoordinates(x, y);
1749 if (!selecting && cursorRow() == cursorIRow()) {
1751 int y1 = cursor.iy() - topy;
1754 InsetOld * inset_hit = checkInsetHit(x, y1);
1755 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1756 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1757 inset_hit->localDispatch(cmd);
1761 setCursorFromCoordinates(cursor.x_fix(),
1762 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1767 void LyXText::cursorUpParagraph()
1769 if (cursor.pos() > 0)
1770 setCursor(cursor.par(), 0);
1771 else if (cursor.par() != ownerParagraphs().begin())
1772 setCursor(boost::prior(cursor.par()), 0);
1776 void LyXText::cursorDownParagraph()
1778 ParagraphList::iterator par = cursor.par();
1779 ParagraphList::iterator next_par = boost::next(par);
1781 if (next_par != ownerParagraphs().end())
1782 setCursor(next_par, 0);
1784 setCursor(par, par->size());
1788 // fix the cursor `cur' after a characters has been deleted at `where'
1789 // position. Called by deleteEmptyParagraphMechanism
1790 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1792 // if cursor is not in the paragraph where the delete occured,
1794 if (cur.par() != where.par())
1797 // if cursor position is after the place where the delete occured,
1799 if (cur.pos() > where.pos())
1800 cur.pos(cur.pos()-1);
1802 // check also if we don't want to set the cursor on a spot behind the
1803 // pagragraph because we erased the last character.
1804 if (cur.pos() > cur.par()->size())
1805 cur.pos(cur.par()->size());
1807 // recompute row et al. for this cursor
1808 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1812 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1814 // Would be wrong to delete anything if we have a selection.
1815 if (selection.set())
1818 // We allow all kinds of "mumbo-jumbo" when freespacing.
1819 if (old_cursor.par()->isFreeSpacing())
1822 /* Ok I'll put some comments here about what is missing.
1823 I have fixed BackSpace (and thus Delete) to not delete
1824 double-spaces automagically. I have also changed Cut,
1825 Copy and Paste to hopefully do some sensible things.
1826 There are still some small problems that can lead to
1827 double spaces stored in the document file or space at
1828 the beginning of paragraphs. This happens if you have
1829 the cursor between to spaces and then save. Or if you
1830 cut and paste and the selection have a space at the
1831 beginning and then save right after the paste. I am
1832 sure none of these are very hard to fix, but I will
1833 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1834 that I can get some feedback. (Lgb)
1837 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1838 // delete the LineSeparator.
1841 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1842 // delete the LineSeparator.
1845 // If the pos around the old_cursor were spaces, delete one of them.
1846 if (old_cursor.par() != cursor.par()
1847 || old_cursor.pos() != cursor.pos()) {
1849 // Only if the cursor has really moved
1850 if (old_cursor.pos() > 0
1851 && old_cursor.pos() < old_cursor.par()->size()
1852 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1853 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1854 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1855 redoParagraph(old_cursor.par());
1859 #ifdef WITH_WARNINGS
1860 #warning This will not work anymore when we have multiple views of the same buffer
1861 // In this case, we will have to correct also the cursors held by
1862 // other bufferviews. It will probably be easier to do that in a more
1863 // automated way in LyXCursor code. (JMarc 26/09/2001)
1865 // correct all cursors held by the LyXText
1866 fixCursorAfterDelete(cursor, old_cursor);
1867 fixCursorAfterDelete(selection.cursor, old_cursor);
1868 fixCursorAfterDelete(selection.start, old_cursor);
1869 fixCursorAfterDelete(selection.end, old_cursor);
1874 // don't delete anything if this is the ONLY paragraph!
1875 if (ownerParagraphs().size() == 1)
1878 // Do not delete empty paragraphs with keepempty set.
1879 if (old_cursor.par()->allowEmpty())
1882 // only do our magic if we changed paragraph
1883 if (old_cursor.par() == cursor.par())
1886 // record if we have deleted a paragraph
1887 // we can't possibly have deleted a paragraph before this point
1888 bool deleted = false;
1890 if (old_cursor.par()->empty() ||
1891 (old_cursor.par()->size() == 1 &&
1892 old_cursor.par()->isLineSeparator(0))) {
1893 // ok, we will delete something
1894 LyXCursor tmpcursor;
1898 bool selection_position_was_oldcursor_position = (
1899 selection.cursor.par() == old_cursor.par()
1900 && selection.cursor.pos() == old_cursor.pos());
1903 cursor = old_cursor; // that undo can restore the right cursor position
1905 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1906 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1909 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1913 ownerParagraphs().erase(old_cursor.par());
1917 setCursorIntern(cursor.par(), cursor.pos());
1919 if (selection_position_was_oldcursor_position) {
1920 // correct selection
1921 selection.cursor = cursor;
1925 if (old_cursor.par()->stripLeadingSpaces()) {
1926 redoParagraph(old_cursor.par());
1928 setCursorIntern(cursor.par(), cursor.pos());
1929 selection.cursor = cursor;
1936 ParagraphList & LyXText::ownerParagraphs() const
1942 bool LyXText::isInInset() const
1944 // Sub-level has non-null bv owner and non-null inset owner.
1945 return inset_owner != 0;
1949 int defaultRowHeight()
1951 LyXFont const font(LyXFont::ALL_SANE);
1952 return int(font_metrics::maxAscent(font)
1953 + font_metrics::maxDescent(font) * 1.5);