1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
31 #include "FloatList.h"
33 #include "ParagraphParameters.h"
35 #include "lyxrow_funcs.h"
36 #include "metricsinfo.h"
37 #include "paragraph_funcs.h"
39 #include "insets/insetbibitem.h"
40 #include "insets/insetenv.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
48 #include <boost/tuple/tuple.hpp>
52 using namespace lyx::support;
62 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
63 ParagraphList & paragraphs)
64 : height(0), width(0), anchor_y_(0),
65 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
66 in_inset_(ininset), paragraphs_(paragraphs)
71 void LyXText::init(BufferView * bview)
75 ParagraphList::iterator const beg = ownerParagraphs().begin();
76 ParagraphList::iterator const end = ownerParagraphs().end();
77 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
85 current_font = getFont(beg, 0);
87 redoParagraphs(beg, end);
88 setCursorIntern(beg, 0);
89 selection.cursor = cursor;
95 // Gets the fully instantiated font at a given position in a paragraph
96 // Basically the same routine as Paragraph::getFont() in paragraph.C.
97 // The difference is that this one is used for displaying, and thus we
98 // are allowed to make cosmetic improvements. For instance make footnotes
100 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
104 LyXLayout_ptr const & layout = pit->layout();
106 BufferParams const & params = bv()->buffer()->params;
108 // We specialize the 95% common case:
109 if (!pit->getDepth()) {
110 if (layout->labeltype == LABEL_MANUAL
111 && pos < pit->beginningOfBody()) {
113 LyXFont f = pit->getFontSettings(params, pos);
115 pit->inInset()->getDrawFont(f);
116 return f.realize(layout->reslabelfont);
118 LyXFont f = pit->getFontSettings(params, pos);
120 pit->inInset()->getDrawFont(f);
121 return f.realize(layout->resfont);
125 // The uncommon case need not be optimized as much
129 if (pos < pit->beginningOfBody()) {
131 layoutfont = layout->labelfont;
134 layoutfont = layout->font;
137 LyXFont tmpfont = pit->getFontSettings(params, pos);
138 tmpfont.realize(layoutfont);
141 pit->inInset()->getDrawFont(tmpfont);
143 // Realize with the fonts of lesser depth.
144 tmpfont.realize(outerFont(pit, ownerParagraphs()));
145 tmpfont.realize(defaultfont_);
151 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
153 LyXLayout_ptr const & layout = pit->layout();
155 if (!pit->getDepth())
156 return layout->resfont;
158 LyXFont font = layout->font;
159 // Realize with the fonts of lesser depth.
160 font.realize(outerFont(pit, ownerParagraphs()));
161 font.realize(defaultfont_);
167 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
169 LyXLayout_ptr const & layout = pit->layout();
171 if (!pit->getDepth())
172 return layout->reslabelfont;
174 LyXFont font = layout->labelfont;
175 // Realize with the fonts of lesser depth.
176 font.realize(outerFont(pit, ownerParagraphs()));
177 font.realize(defaultfont_);
183 void LyXText::setCharFont(ParagraphList::iterator pit,
184 pos_type pos, LyXFont const & fnt,
187 BufferParams const & params = bv()->buffer()->params;
188 LyXFont font = getFont(pit, pos);
189 font.update(fnt, params.language, toggleall);
190 // Let the insets convert their font
191 if (pit->isInset(pos)) {
192 InsetOld * inset = pit->getInset(pos);
193 if (isEditableInset(inset)) {
194 static_cast<UpdatableInset *>(inset)
195 ->setFont(bv(), fnt, toggleall, true);
199 // Plug through to version below:
200 setCharFont(pit, pos, font);
204 void LyXText::setCharFont(
205 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
208 LyXLayout_ptr const & layout = pit->layout();
210 // Get concrete layout font to reduce against
213 if (pos < pit->beginningOfBody())
214 layoutfont = layout->labelfont;
216 layoutfont = layout->font;
218 // Realize against environment font information
219 if (pit->getDepth()) {
220 ParagraphList::iterator tp = pit;
221 while (!layoutfont.resolved() &&
222 tp != ownerParagraphs().end() &&
224 tp = outerHook(tp, ownerParagraphs());
225 if (tp != ownerParagraphs().end())
226 layoutfont.realize(tp->layout()->font);
230 layoutfont.realize(defaultfont_);
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 pit->setFont(pos, font);
239 InsetOld * LyXText::getInset() const
241 ParagraphList::iterator pit = cursor.par();
242 pos_type const pos = cursor.pos();
244 if (pos < pit->size() && pit->isInset(pos)) {
245 return pit->getInset(pos);
251 void LyXText::toggleInset()
253 InsetOld * inset = getInset();
254 // is there an editable inset at cursor position?
255 if (!isEditableInset(inset)) {
256 // No, try to see if we are inside a collapsable inset
257 if (inset_owner && inset_owner->owner()
258 && inset_owner->owner()->isOpen()) {
259 bv()->unlockInset(inset_owner->owner());
260 inset_owner->owner()->close(bv());
261 bv()->getLyXText()->cursorRight(bv());
265 //bv()->owner()->message(inset->editMessage());
267 // do we want to keep this?? (JMarc)
268 if (!isHighlyEditableInset(inset))
269 recordUndo(bv(), Undo::ATOMIC);
280 /* used in setlayout */
281 // Asger is not sure we want to do this...
282 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
285 LyXLayout_ptr const & layout = par.layout();
286 pos_type const psize = par.size();
289 for (pos_type pos = 0; pos < psize; ++pos) {
290 if (pos < par.beginningOfBody())
291 layoutfont = layout->labelfont;
293 layoutfont = layout->font;
295 LyXFont tmpfont = par.getFontSettings(params, pos);
296 tmpfont.reduce(layoutfont);
297 par.setFont(pos, tmpfont);
302 ParagraphList::iterator
303 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
304 LyXCursor & send_cur,
305 string const & layout)
307 ParagraphList::iterator endpit = boost::next(send_cur.par());
308 ParagraphList::iterator undoendpit = endpit;
309 ParagraphList::iterator pars_end = ownerParagraphs().end();
311 if (endpit != pars_end && endpit->getDepth()) {
312 while (endpit != pars_end && endpit->getDepth()) {
316 } else if (endpit != pars_end) {
317 // because of parindents etc.
321 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
323 // ok we have a selection. This is always between sstart_cur
324 // and sel_end cursor
326 ParagraphList::iterator pit = sstart_cur.par();
327 ParagraphList::iterator epit = boost::next(send_cur.par());
329 LyXLayout_ptr const & lyxlayout =
330 bv()->buffer()->params.getLyXTextClass()[layout];
333 pit->applyLayout(lyxlayout);
334 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
335 pit->params().spaceTop(lyxlayout->fill_top ?
336 VSpace(VSpace::VFILL)
337 : VSpace(VSpace::NONE));
338 pit->params().spaceBottom(lyxlayout->fill_bottom ?
339 VSpace(VSpace::VFILL)
340 : VSpace(VSpace::NONE));
341 if (lyxlayout->margintype == MARGIN_MANUAL)
342 pit->setLabelWidthString(lyxlayout->labelstring());
345 } while (pit != epit);
351 // set layout over selection and make a total rebreak of those paragraphs
352 void LyXText::setLayout(string const & layout)
354 LyXCursor tmpcursor = cursor; // store the current cursor
356 // if there is no selection just set the layout
357 // of the current paragraph
358 if (!selection.set()) {
359 selection.start = cursor; // dummy selection
360 selection.end = cursor;
363 // special handling of new environment insets
364 BufferParams const & params = bv()->buffer()->params;
365 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
366 if (lyxlayout->is_environment) {
367 // move everything in a new environment inset
368 lyxerr << "setting layout " << layout << endl;
369 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
370 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
371 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
372 InsetOld * inset = new InsetEnvironment(params, layout);
373 if (bv()->insertInset(inset)) {
375 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
382 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
383 selection.end, layout);
384 redoParagraphs(selection.start.par(), endpit);
386 // we have to reset the selection, because the
387 // geometry could have changed
388 setCursor(selection.start.par(), selection.start.pos(), false);
389 selection.cursor = cursor;
390 setCursor(selection.end.par(), selection.end.pos(), false);
394 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
398 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
400 ParagraphList::iterator pit = cursor.par();
401 ParagraphList::iterator end = cursor.par();
402 ParagraphList::iterator start = pit;
404 if (selection.set()) {
405 pit = selection.start.par();
406 end = selection.end.par();
410 ParagraphList::iterator pastend = boost::next(end);
413 recordUndo(bv(), Undo::ATOMIC, start, end);
415 bool changed = false;
417 int prev_after_depth = 0;
418 #warning parlist ... could be nicer ?
419 if (start != ownerParagraphs().begin()) {
420 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
424 int const depth = pit->params().depth();
425 if (type == bv_funcs::INC_DEPTH) {
426 if (depth < prev_after_depth
427 && pit->layout()->labeltype != LABEL_BIBLIO) {
430 pit->params().depth(depth + 1);
435 pit->params().depth(depth - 1);
438 prev_after_depth = pit->getMaxDepthAfter();
450 redoParagraphs(start, pastend);
452 // We need to actually move the text->cursor. I don't
453 // understand why ...
454 LyXCursor tmpcursor = cursor;
456 // we have to reset the visual selection because the
457 // geometry could have changed
458 if (selection.set()) {
459 setCursor(selection.start.par(), selection.start.pos());
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos());
464 // this handles the counter labels, and also fixes up
465 // depth values for follow-on (child) paragraphs
469 setCursor(tmpcursor.par(), tmpcursor.pos());
475 // set font over selection and make a total rebreak of those paragraphs
476 void LyXText::setFont(LyXFont const & font, bool toggleall)
478 // if there is no selection just set the current_font
479 if (!selection.set()) {
480 // Determine basis font
482 if (cursor.pos() < cursor.par()->beginningOfBody()) {
483 layoutfont = getLabelFont(cursor.par());
485 layoutfont = getLayoutFont(cursor.par());
487 // Update current font
488 real_current_font.update(font,
489 bv()->buffer()->params.language,
492 // Reduce to implicit settings
493 current_font = real_current_font;
494 current_font.reduce(layoutfont);
495 // And resolve it completely
496 real_current_font.realize(layoutfont);
501 LyXCursor tmpcursor = cursor; // store the current cursor
503 // ok we have a selection. This is always between sel_start_cursor
504 // and sel_end cursor
506 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
508 cursor = selection.start;
509 while (cursor.par() != selection.end.par() ||
510 cursor.pos() < selection.end.pos())
512 if (cursor.pos() < cursor.par()->size()) {
513 // an open footnote should behave like a closed one
514 setCharFont(cursor.par(), cursor.pos(),
516 cursor.pos(cursor.pos() + 1);
519 cursor.par(boost::next(cursor.par()));
524 redoParagraph(selection.start.par());
526 // we have to reset the selection, because the
527 // geometry could have changed, but we keep
528 // it for user convenience
529 setCursor(selection.start.par(), selection.start.pos());
530 selection.cursor = cursor;
531 setCursor(selection.end.par(), selection.end.pos());
533 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
534 tmpcursor.boundary());
538 // rebreaks all paragraphs between the specified pars
539 // This function is needed after SetLayout and SetFont etc.
540 void LyXText::redoParagraphs(ParagraphList::iterator start,
541 ParagraphList::iterator end)
543 for ( ; start != end; ++start)
544 redoParagraph(start);
548 void LyXText::redoParagraph(ParagraphList::iterator pit)
550 RowList::iterator rit = pit->rows.begin();
551 RowList::iterator end = pit->rows.end();
553 // remove rows of paragraph, keep track of height changes
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
559 InsetList::iterator ii = pit->insetlist.begin();
560 InsetList::iterator iend = pit->insetlist.end();
561 for (; ii != iend; ++ii) {
563 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
564 ii->inset->metrics(mi, dim);
567 // rebreak the paragraph
568 for (pos_type z = 0; z < pit->size() + 1; ) {
570 z = rowBreakPoint(pit, row) + 1;
572 pit->rows.push_back(row);
575 // set height and fill of rows
576 for (rit = pit->rows.begin(); rit != end; ++rit) {
577 rit->fill(fill(pit, rit, workWidth()));
578 prepareToPrint(pit, rit);
579 setHeightOfRow(pit, rit);
580 height += rit->height();
583 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
587 void LyXText::fullRebreak()
589 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
591 selection.cursor = cursor;
595 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
597 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
598 // << " workWidth: " << workWidth() << endl;
599 //Assert(mi.base.textwidth);
606 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
609 dim.asc = firstRow()->ascent_of_text();
610 dim.des = height - dim.asc;
611 dim.wid = std::max(mi.base.textwidth, int(width));
615 // important for the screen
618 // the cursor set functions have a special mechanism. When they
619 // realize, that you left an empty paragraph, they will delete it.
620 // They also delete the corresponding row
622 // need the selection cursor:
623 void LyXText::setSelection()
625 TextCursor::setSelection();
630 void LyXText::clearSelection()
632 TextCursor::clearSelection();
634 // reset this in the bv_owner!
635 if (bv_owner && bv_owner->text)
636 bv_owner->text->xsel_cache.set(false);
640 void LyXText::cursorHome()
642 setCursor(cursor.par(), cursorRow()->pos());
646 void LyXText::cursorEnd()
648 if (cursor.par()->empty())
651 RowList::iterator rit = cursorRow();
652 RowList::iterator next_rit = boost::next(rit);
653 RowList::iterator end = boost::next(rit);
654 ParagraphList::iterator pit = cursor.par();
655 pos_type last_pos = lastPos(*pit, rit);
657 if (next_rit == end) {
661 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
666 setCursor(pit, last_pos);
670 void LyXText::cursorTop()
672 setCursor(ownerParagraphs().begin(), 0);
676 void LyXText::cursorBottom()
678 ParagraphList::iterator lastpit =
679 boost::prior(ownerParagraphs().end());
680 setCursor(lastpit, lastpit->size());
684 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
686 // If the mask is completely neutral, tell user
687 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
688 // Could only happen with user style
689 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
693 // Try implicit word selection
694 // If there is a change in the language the implicit word selection
696 LyXCursor resetCursor = cursor;
697 bool implicitSelection = (font.language() == ignore_language
698 && font.number() == LyXFont::IGNORE)
699 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
702 setFont(font, toggleall);
704 // Implicit selections are cleared afterwards
705 //and cursor is set to the original position.
706 if (implicitSelection) {
708 cursor = resetCursor;
709 setCursor(cursor.par(), cursor.pos());
710 selection.cursor = cursor;
715 string LyXText::getStringToIndex()
717 // Try implicit word selection
718 // If there is a change in the language the implicit word selection
720 LyXCursor const reset_cursor = cursor;
721 bool const implicitSelection =
722 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
725 if (!selection.set())
726 bv()->owner()->message(_("Nothing to index!"));
727 else if (selection.start.par() != selection.end.par())
728 bv()->owner()->message(_("Cannot index more than one paragraph!"));
730 idxstring = selectionAsString(bv()->buffer(), false);
732 // Reset cursors to their original position.
733 cursor = reset_cursor;
734 setCursor(cursor.par(), cursor.pos());
735 selection.cursor = cursor;
737 // Clear the implicit selection.
738 if (implicitSelection)
745 // the DTP switches for paragraphs. LyX will store them in the first
746 // physical paragraph. When a paragraph is broken, the top settings rest,
747 // the bottom settings are given to the new one. So I can make sure,
748 // they do not duplicate themself and you cannnot make dirty things with
751 void LyXText::setParagraph(bool line_top, bool line_bottom,
752 bool pagebreak_top, bool pagebreak_bottom,
753 VSpace const & space_top,
754 VSpace const & space_bottom,
755 Spacing const & spacing,
757 string const & labelwidthstring,
760 LyXCursor tmpcursor = cursor;
761 if (!selection.set()) {
762 selection.start = cursor;
763 selection.end = cursor;
766 // make sure that the depth behind the selection are restored, too
767 ParagraphList::iterator endpit = boost::next(selection.end.par());
768 ParagraphList::iterator undoendpit = endpit;
769 ParagraphList::iterator pars_end = ownerParagraphs().end();
771 if (endpit != pars_end && endpit->getDepth()) {
772 while (endpit != pars_end && endpit->getDepth()) {
776 } else if (endpit != pars_end) {
777 // because of parindents etc.
781 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
782 boost::prior(undoendpit));
785 ParagraphList::iterator tmppit = selection.end.par();
787 while (tmppit != boost::prior(selection.start.par())) {
788 setCursor(tmppit, 0);
790 ParagraphList::iterator pit = cursor.par();
791 ParagraphParameters & params = pit->params();
793 params.lineTop(line_top);
794 params.lineBottom(line_bottom);
795 params.pagebreakTop(pagebreak_top);
796 params.pagebreakBottom(pagebreak_bottom);
797 params.spaceTop(space_top);
798 params.spaceBottom(space_bottom);
799 params.spacing(spacing);
800 // does the layout allow the new alignment?
801 LyXLayout_ptr const & layout = pit->layout();
803 if (align == LYX_ALIGN_LAYOUT)
804 align = layout->align;
805 if (align & layout->alignpossible) {
806 if (align == layout->align)
807 params.align(LYX_ALIGN_LAYOUT);
811 pit->setLabelWidthString(labelwidthstring);
812 params.noindent(noindent);
813 tmppit = boost::prior(pit);
816 redoParagraphs(selection.start.par(), endpit);
819 setCursor(selection.start.par(), selection.start.pos());
820 selection.cursor = cursor;
821 setCursor(selection.end.par(), selection.end.pos());
823 setCursor(tmpcursor.par(), tmpcursor.pos());
829 // set the counter of a paragraph. This includes the labels
830 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
832 LyXTextClass const & textclass = buf->params.getLyXTextClass();
833 LyXLayout_ptr const & layout = pit->layout();
835 if (pit != ownerParagraphs().begin()) {
837 pit->params().appendix(boost::prior(pit)->params().appendix());
838 if (!pit->params().appendix() &&
839 pit->params().startOfAppendix()) {
840 pit->params().appendix(true);
841 textclass.counters().reset();
843 pit->enumdepth = boost::prior(pit)->enumdepth;
844 pit->itemdepth = boost::prior(pit)->itemdepth;
846 pit->params().appendix(pit->params().startOfAppendix());
851 // Maybe we have to increment the enumeration depth.
852 // BUT, enumeration in a footnote is considered in isolation from its
853 // surrounding paragraph so don't increment if this is the
854 // first line of the footnote
855 // AND, bibliographies can't have their depth changed ie. they
856 // are always of depth 0
857 if (pit != ownerParagraphs().begin()
858 && boost::prior(pit)->getDepth() < pit->getDepth()
859 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
860 && pit->enumdepth < 3
861 && layout->labeltype != LABEL_BIBLIO) {
865 // Maybe we have to decrement the enumeration depth, see note above
866 if (pit != ownerParagraphs().begin()
867 && boost::prior(pit)->getDepth() > pit->getDepth()
868 && layout->labeltype != LABEL_BIBLIO) {
869 pit->enumdepth = depthHook(pit, ownerParagraphs(),
870 pit->getDepth())->enumdepth;
873 if (!pit->params().labelString().empty()) {
874 pit->params().labelString(string());
877 if (layout->margintype == MARGIN_MANUAL) {
878 if (pit->params().labelWidthString().empty())
879 pit->setLabelWidthString(layout->labelstring());
881 pit->setLabelWidthString(string());
884 // is it a layout that has an automatic label?
885 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
886 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
890 if (i >= 0 && i <= buf->params.secnumdepth) {
894 textclass.counters().step(layout->latexname());
896 // Is there a label? Useful for Chapter layout
897 if (!pit->params().appendix()) {
898 s << buf->B_(layout->labelstring());
900 s << buf->B_(layout->labelstring_appendix());
903 // Use of an integer is here less than elegant. For now.
904 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
905 if (!pit->params().appendix()) {
906 numbertype = "sectioning";
908 numbertype = "appendix";
909 if (pit->isRightToLeftPar(buf->params))
916 << textclass.counters()
917 .numberLabel(layout->latexname(),
918 numbertype, langtype, head);
920 pit->params().labelString(STRCONV(s.str()));
922 // reset enum counters
923 textclass.counters().reset("enum");
924 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
925 textclass.counters().reset("enum");
926 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
928 // Yes I know this is a really, really! bad solution
930 string enumcounter("enum");
932 switch (pit->enumdepth) {
944 // not a valid enumdepth...
948 textclass.counters().step(enumcounter);
950 s << textclass.counters()
951 .numberLabel(enumcounter, "enumeration");
952 pit->params().labelString(STRCONV(s.str()));
954 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
955 textclass.counters().step("bibitem");
956 int number = textclass.counters().value("bibitem");
957 if (pit->bibitem()) {
958 pit->bibitem()->setCounter(number);
959 pit->params().labelString(layout->labelstring());
961 // In biblio should't be following counters but...
963 string s = buf->B_(layout->labelstring());
966 if (layout->labeltype == LABEL_SENSITIVE) {
967 ParagraphList::iterator end = ownerParagraphs().end();
968 ParagraphList::iterator tmppit = pit;
971 while (tmppit != end && tmppit->inInset()
972 // the single '=' is intended below
973 && (in = tmppit->inInset()->owner()))
975 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
976 in->lyxCode() == InsetOld::WRAP_CODE) {
980 tmppit = ownerParagraphs().begin();
981 for ( ; tmppit != end; ++tmppit)
982 if (&*tmppit == in->parOwner())
990 if (in->lyxCode() == InsetOld::FLOAT_CODE)
991 type = static_cast<InsetFloat*>(in)->params().type;
992 else if (in->lyxCode() == InsetOld::WRAP_CODE)
993 type = static_cast<InsetWrap*>(in)->params().type;
997 Floating const & fl = textclass.floats().getType(type);
999 textclass.counters().step(fl.type());
1001 // Doesn't work... yet.
1002 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1004 // par->SetLayout(0);
1005 // s = layout->labelstring;
1006 s = _("Senseless: ");
1009 pit->params().labelString(s);
1011 // reset the enumeration counter. They are always reset
1012 // when there is any other layout between
1013 // Just fall-through between the cases so that all
1014 // enum counters deeper than enumdepth is also reset.
1015 switch (pit->enumdepth) {
1017 textclass.counters().reset("enumi");
1019 textclass.counters().reset("enumii");
1021 textclass.counters().reset("enumiii");
1023 textclass.counters().reset("enumiv");
1029 // Updates all counters. Paragraphs with changed label string will be rebroken
1030 void LyXText::updateCounters()
1033 bv()->buffer()->params.getLyXTextClass().counters().reset();
1035 ParagraphList::iterator beg = ownerParagraphs().begin();
1036 ParagraphList::iterator end = ownerParagraphs().end();
1037 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1038 string const oldLabel = pit->params().labelString();
1040 size_t maxdepth = 0;
1042 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1044 if (pit->params().depth() > maxdepth)
1045 pit->params().depth(maxdepth);
1047 // setCounter can potentially change the labelString.
1048 setCounter(bv()->buffer(), pit);
1050 string const & newLabel = pit->params().labelString();
1052 if (oldLabel != newLabel)
1058 void LyXText::insertInset(InsetOld * inset)
1060 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1062 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1064 cursor.par()->insertInset(cursor.pos(), inset);
1065 // Just to rebreak and refresh correctly.
1066 // The character will not be inserted a second time
1067 insertChar(Paragraph::META_INSET);
1068 // If we enter a highly editable inset the cursor should be before
1069 // the inset. After an Undo LyX tries to call inset->edit(...)
1070 // and fails if the cursor is behind the inset and getInset
1071 // does not return the inset!
1072 if (isHighlyEditableInset(inset))
1078 void LyXText::cutSelection(bool doclear, bool realcut)
1080 // Stuff what we got on the clipboard. Even if there is no selection.
1082 // There is a problem with having the stuffing here in that the
1083 // larger the selection the slower LyX will get. This can be
1084 // solved by running the line below only when the selection has
1085 // finished. The solution used currently just works, to make it
1086 // faster we need to be more clever and probably also have more
1087 // calls to stuffClipboard. (Lgb)
1088 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1090 // This doesn't make sense, if there is no selection
1091 if (!selection.set())
1094 // OK, we have a selection. This is always between selection.start
1095 // and selection.end
1097 // make sure that the depth behind the selection are restored, too
1098 ParagraphList::iterator endpit = boost::next(selection.end.par());
1099 ParagraphList::iterator undoendpit = endpit;
1100 ParagraphList::iterator pars_end = ownerParagraphs().end();
1102 if (endpit != pars_end && endpit->getDepth()) {
1103 while (endpit != pars_end && endpit->getDepth()) {
1105 undoendpit = endpit;
1107 } else if (endpit != pars_end) {
1108 // because of parindents etc.
1112 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1113 boost::prior(undoendpit));
1115 endpit = selection.end.par();
1116 int endpos = selection.end.pos();
1118 boost::tie(endpit, endpos) = realcut ?
1119 CutAndPaste::cutSelection(bv()->buffer()->params,
1121 selection.start.par(), endpit,
1122 selection.start.pos(), endpos,
1123 bv()->buffer()->params.textclass,
1125 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1127 selection.start.par(), endpit,
1128 selection.start.pos(), endpos,
1130 // sometimes necessary
1132 selection.start.par()->stripLeadingSpaces();
1134 redoParagraphs(selection.start.par(), boost::next(endpit));
1135 // cutSelection can invalidate the cursor so we need to set
1137 // we prefer the end for when tracking changes
1141 // need a valid cursor. (Lgb)
1144 setCursor(cursor.par(), cursor.pos());
1145 selection.cursor = cursor;
1150 void LyXText::copySelection()
1152 // stuff the selection onto the X clipboard, from an explicit copy request
1153 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1155 // this doesnt make sense, if there is no selection
1156 if (!selection.set())
1159 // ok we have a selection. This is always between selection.start
1160 // and sel_end cursor
1162 // copy behind a space if there is one
1163 while (selection.start.par()->size() > selection.start.pos()
1164 && selection.start.par()->isLineSeparator(selection.start.pos())
1165 && (selection.start.par() != selection.end.par()
1166 || selection.start.pos() < selection.end.pos()))
1167 selection.start.pos(selection.start.pos() + 1);
1169 CutAndPaste::copySelection(selection.start.par(),
1170 selection.end.par(),
1171 selection.start.pos(), selection.end.pos(),
1172 bv()->buffer()->params.textclass);
1176 void LyXText::pasteSelection(size_t sel_index)
1178 // this does not make sense, if there is nothing to paste
1179 if (!CutAndPaste::checkPastePossible())
1182 recordUndo(bv(), Undo::INSERT, cursor.par());
1184 ParagraphList::iterator endpit;
1189 boost::tie(ppp, endpit) =
1190 CutAndPaste::pasteSelection(*bv()->buffer(),
1192 cursor.par(), cursor.pos(),
1193 bv()->buffer()->params.textclass,
1195 bufferErrors(*bv()->buffer(), el);
1196 bv()->showErrorList(_("Paste"));
1198 redoParagraphs(cursor.par(), endpit);
1200 setCursor(cursor.par(), cursor.pos());
1203 selection.cursor = cursor;
1204 setCursor(ppp.first, ppp.second);
1210 void LyXText::setSelectionRange(lyx::pos_type length)
1215 selection.cursor = cursor;
1222 // simple replacing. The font of the first selected character is used
1223 void LyXText::replaceSelectionWithString(string const & str)
1225 recordUndo(bv(), Undo::ATOMIC);
1228 if (!selection.set()) { // create a dummy selection
1229 selection.end = cursor;
1230 selection.start = cursor;
1233 // Get font setting before we cut
1234 pos_type pos = selection.end.pos();
1235 LyXFont const font = selection.start.par()
1236 ->getFontSettings(bv()->buffer()->params,
1237 selection.start.pos());
1239 // Insert the new string
1240 string::const_iterator cit = str.begin();
1241 string::const_iterator end = str.end();
1242 for (; cit != end; ++cit) {
1243 selection.end.par()->insertChar(pos, (*cit), font);
1247 // Cut the selection
1248 cutSelection(true, false);
1254 // needed to insert the selection
1255 void LyXText::insertStringAsLines(string const & str)
1257 ParagraphList::iterator pit = cursor.par();
1258 pos_type pos = cursor.pos();
1259 ParagraphList::iterator endpit = boost::next(cursor.par());
1261 recordUndo(bv(), Undo::ATOMIC);
1263 // only to be sure, should not be neccessary
1266 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1268 redoParagraphs(cursor.par(), endpit);
1269 setCursor(cursor.par(), cursor.pos());
1270 selection.cursor = cursor;
1271 setCursor(pit, pos);
1276 // turns double-CR to single CR, others where converted into one
1277 // blank. Then InsertStringAsLines is called
1278 void LyXText::insertStringAsParagraphs(string const & str)
1280 string linestr(str);
1281 bool newline_inserted = false;
1282 string::size_type const siz = linestr.length();
1284 for (string::size_type i = 0; i < siz; ++i) {
1285 if (linestr[i] == '\n') {
1286 if (newline_inserted) {
1287 // we know that \r will be ignored by
1288 // InsertStringA. Of course, it is a dirty
1289 // trick, but it works...
1290 linestr[i - 1] = '\r';
1294 newline_inserted = true;
1296 } else if (IsPrintable(linestr[i])) {
1297 newline_inserted = false;
1300 insertStringAsLines(linestr);
1304 bool LyXText::setCursor(ParagraphList::iterator pit,
1306 bool setfont, bool boundary)
1308 LyXCursor old_cursor = cursor;
1309 setCursorIntern(pit, pos, setfont, boundary);
1310 return deleteEmptyParagraphMechanism(old_cursor);
1314 void LyXText::redoCursor()
1316 #warning maybe the same for selections?
1317 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1321 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1322 pos_type pos, bool boundary)
1324 Assert(pit != ownerParagraphs().end());
1328 cur.boundary(boundary);
1332 // get the cursor y position in text
1334 RowList::iterator row = getRow(pit, pos, y);
1335 RowList::iterator old_row = row;
1336 // if we are before the first char of this row and are still in the
1337 // same paragraph and there is a previous row then put the cursor on
1338 // the end of the previous row
1339 cur.iy(y + row->baseline());
1340 if (row != pit->rows.begin()
1342 && pos < pit->size()
1343 && pit->getChar(pos) == Paragraph::META_INSET) {
1344 InsetOld * ins = pit->getInset(pos);
1345 if (ins && (ins->needFullRow() || ins->display())) {
1351 // y is now the beginning of the cursor row
1352 y += row->baseline();
1353 // y is now the cursor baseline
1356 pos_type last = lastPrintablePos(*pit, old_row);
1358 // None of these should happen, but we're scaredy-cats
1359 if (pos > pit->size()) {
1360 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1363 } else if (pos > last + 1) {
1364 lyxerr << "dont like 2 please report" << endl;
1365 // This shouldn't happen.
1368 } else if (pos < row->pos()) {
1369 lyxerr << "dont like 3 please report" << endl;
1374 // now get the cursors x position
1375 float x = getCursorX(pit, row, pos, last, boundary);
1378 if (old_row != row) {
1379 x = getCursorX(pit, old_row, pos, last, boundary);
1383 /* We take out this for the time being because 1) the redraw code is not
1384 prepared to this yet and 2) because some good policy has yet to be decided
1385 while editting: for instance how to act on rows being created/deleted
1389 //if the cursor is in a visible row, anchor to it
1391 if (topy < y && y < topy + bv()->workHeight())
1397 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1398 pos_type pos, pos_type last, bool boundary) const
1400 pos_type cursor_vpos = 0;
1401 double x = rit->x();
1402 double fill_separator = rit->fill_separator();
1403 double fill_hfill = rit->fill_hfill();
1404 double fill_label_hfill = rit->fill_label_hfill();
1405 pos_type const rit_pos = rit->pos();
1408 cursor_vpos = rit_pos;
1409 else if (pos > last && !boundary)
1410 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1411 ? rit_pos : last + 1;
1412 else if (pos > rit_pos && (pos > last || boundary))
1413 // Place cursor after char at (logical) position pos - 1
1414 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1415 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1417 // Place cursor before char at (logical) position pos
1418 cursor_vpos = (bidi_level(pos) % 2 == 0)
1419 ? log2vis(pos) : log2vis(pos) + 1;
1421 pos_type body_pos = pit->beginningOfBody();
1423 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1426 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1427 pos_type pos = vis2log(vpos);
1428 if (body_pos > 0 && pos == body_pos - 1) {
1429 x += fill_label_hfill +
1430 font_metrics::width(
1431 pit->layout()->labelsep, getLabelFont(pit));
1432 if (pit->isLineSeparator(body_pos - 1))
1433 x -= singleWidth(pit, body_pos - 1);
1436 if (hfillExpansion(*pit, rit, pos)) {
1437 x += singleWidth(pit, pos);
1438 if (pos >= body_pos)
1441 x += fill_label_hfill;
1442 } else if (pit->isSeparator(pos)) {
1443 x += singleWidth(pit, pos);
1444 if (pos >= body_pos)
1445 x += fill_separator;
1447 x += singleWidth(pit, pos);
1453 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1454 pos_type pos, bool setfont, bool boundary)
1456 setCursor(cursor, pit, pos, boundary);
1462 void LyXText::setCurrentFont()
1464 pos_type pos = cursor.pos();
1465 ParagraphList::iterator pit = cursor.par();
1467 if (cursor.boundary() && pos > 0)
1471 if (pos == pit->size())
1473 else // potentional bug... BUG (Lgb)
1474 if (pit->isSeparator(pos)) {
1475 if (pos > cursorRow()->pos() &&
1476 bidi_level(pos) % 2 ==
1477 bidi_level(pos - 1) % 2)
1479 else if (pos + 1 < pit->size())
1484 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1485 real_current_font = getFont(pit, pos);
1487 if (cursor.pos() == pit->size() &&
1488 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1489 !cursor.boundary()) {
1490 Language const * lang =
1491 pit->getParLanguage(bv()->buffer()->params);
1492 current_font.setLanguage(lang);
1493 current_font.setNumber(LyXFont::OFF);
1494 real_current_font.setLanguage(lang);
1495 real_current_font.setNumber(LyXFont::OFF);
1500 // returns the column near the specified x-coordinate of the row
1501 // x is set to the real beginning of this column
1502 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1503 RowList::iterator rit, int & x, bool & boundary) const
1505 double tmpx = rit->x();
1506 double fill_separator = rit->fill_separator();
1507 double fill_hfill = rit->fill_hfill();
1508 double fill_label_hfill = rit->fill_label_hfill();
1510 pos_type vc = rit->pos();
1511 pos_type last = lastPrintablePos(*pit, rit);
1513 LyXLayout_ptr const & layout = pit->layout();
1515 bool left_side = false;
1517 pos_type body_pos = pit->beginningOfBody();
1518 double last_tmpx = tmpx;
1521 (body_pos - 1 > last ||
1522 !pit->isLineSeparator(body_pos - 1)))
1525 // check for empty row
1531 while (vc <= last && tmpx <= x) {
1534 if (body_pos > 0 && c == body_pos - 1) {
1535 tmpx += fill_label_hfill +
1536 font_metrics::width(layout->labelsep, getLabelFont(pit));
1537 if (pit->isLineSeparator(body_pos - 1))
1538 tmpx -= singleWidth(pit, body_pos - 1);
1541 if (hfillExpansion(*pit, rit, c)) {
1542 tmpx += singleWidth(pit, c);
1546 tmpx += fill_label_hfill;
1547 } else if (pit->isSeparator(c)) {
1548 tmpx += singleWidth(pit, c);
1550 tmpx += fill_separator;
1552 tmpx += singleWidth(pit, c);
1557 if ((tmpx + last_tmpx) / 2 > x) {
1562 if (vc > last + 1) // This shouldn't happen.
1566 // This (rtl_support test) is not needed, but gives
1567 // some speedup if rtl_support == false
1568 bool const lastrow = lyxrc.rtl_support
1569 && boost::next(rit) == pit->rows.end();
1571 // If lastrow is false, we don't need to compute
1572 // the value of rtl.
1573 bool const rtl = (lastrow)
1574 ? pit->isRightToLeftPar(bv()->buffer()->params)
1577 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1578 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1580 else if (vc == rit->pos()) {
1582 if (bidi_level(c) % 2 == 1)
1585 c = vis2log(vc - 1);
1586 bool const rtl = (bidi_level(c) % 2 == 1);
1587 if (left_side == rtl) {
1589 boundary = isBoundary(bv()->buffer(), *pit, c);
1593 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1594 if (bidi_level(last) % 2 == 0)
1595 tmpx -= singleWidth(pit, last);
1597 tmpx += singleWidth(pit, last);
1607 void LyXText::setCursorFromCoordinates(int x, int y)
1609 LyXCursor old_cursor = cursor;
1610 setCursorFromCoordinates(cursor, x, y);
1612 deleteEmptyParagraphMechanism(old_cursor);
1619 * return true if the cursor given is at the end of a row,
1620 * and the next row is filled by an inset that spans an entire
1623 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1625 RowList::iterator row = lt.getRow(cur);
1626 RowList::iterator next = boost::next(row);
1628 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1631 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1634 InsetOld const * inset = cur.par()->getInset(cur.pos());
1635 if (inset->needFullRow() || inset->display())
1643 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1645 // Get the row first.
1646 ParagraphList::iterator pit;
1647 RowList::iterator rit = getRowNearY(y, pit);
1649 pos_type const column = getColumnNearX(pit, rit, x, bound);
1651 cur.pos(rit->pos() + column);
1653 cur.y(y + rit->baseline());
1655 if (beforeFullRowInset(*this, cur)) {
1656 pos_type const last = lastPrintablePos(*pit, rit);
1657 RowList::iterator next_rit = rit;
1658 ParagraphList::iterator next_pit = pit;
1659 nextRow(next_pit, next_rit);
1660 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1661 cur.iy(y + rit->height() + next_rit->baseline());
1666 cur.boundary(bound);
1670 void LyXText::cursorLeft(bool internal)
1672 if (cursor.pos() > 0) {
1673 bool boundary = cursor.boundary();
1674 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1675 if (!internal && !boundary &&
1676 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1677 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1678 } else if (cursor.par() != ownerParagraphs().begin()) {
1679 // steps into the paragraph above
1680 ParagraphList::iterator pit = boost::prior(cursor.par());
1681 setCursor(pit, pit->size());
1686 void LyXText::cursorRight(bool internal)
1688 bool const at_end = (cursor.pos() == cursor.par()->size());
1689 bool const at_newline = !at_end &&
1690 cursor.par()->isNewline(cursor.pos());
1692 if (!internal && cursor.boundary() && !at_newline)
1693 setCursor(cursor.par(), cursor.pos(), true, false);
1695 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1697 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1698 setCursor(cursor.par(), cursor.pos(), true, true);
1699 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1700 setCursor(boost::next(cursor.par()), 0);
1704 void LyXText::cursorUp(bool selecting)
1707 int x = cursor.x_fix();
1708 int y = cursor.y() - cursorRow()->baseline() - 1;
1709 setCursorFromCoordinates(x, y);
1712 int y1 = cursor.iy() - topy;
1715 InsetOld * inset_hit = checkInsetHit(x, y1);
1716 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1717 inset_hit->localDispatch(
1718 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1722 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1723 cursorRow()->baseline() << endl;
1724 setCursorFromCoordinates(cursor.x_fix(),
1725 cursor.y() - cursorRow()->baseline() - 1);
1730 void LyXText::cursorDown(bool selecting)
1733 int x = cursor.x_fix();
1734 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1735 setCursorFromCoordinates(x, y);
1736 if (!selecting && cursorRow() == cursorIRow()) {
1738 int y1 = cursor.iy() - topy;
1741 InsetOld * inset_hit = checkInsetHit(x, y1);
1742 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1743 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1744 inset_hit->localDispatch(cmd);
1748 setCursorFromCoordinates(cursor.x_fix(),
1749 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1754 void LyXText::cursorUpParagraph()
1756 if (cursor.pos() > 0)
1757 setCursor(cursor.par(), 0);
1758 else if (cursor.par() != ownerParagraphs().begin())
1759 setCursor(boost::prior(cursor.par()), 0);
1763 void LyXText::cursorDownParagraph()
1765 ParagraphList::iterator par = cursor.par();
1766 ParagraphList::iterator next_par = boost::next(par);
1768 if (next_par != ownerParagraphs().end())
1769 setCursor(next_par, 0);
1771 setCursor(par, par->size());
1775 // fix the cursor `cur' after a characters has been deleted at `where'
1776 // position. Called by deleteEmptyParagraphMechanism
1777 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1779 // if cursor is not in the paragraph where the delete occured,
1781 if (cur.par() != where.par())
1784 // if cursor position is after the place where the delete occured,
1786 if (cur.pos() > where.pos())
1787 cur.pos(cur.pos()-1);
1789 // check also if we don't want to set the cursor on a spot behind the
1790 // pagragraph because we erased the last character.
1791 if (cur.pos() > cur.par()->size())
1792 cur.pos(cur.par()->size());
1794 // recompute row et al. for this cursor
1795 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1799 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1801 // Would be wrong to delete anything if we have a selection.
1802 if (selection.set())
1805 // We allow all kinds of "mumbo-jumbo" when freespacing.
1806 if (old_cursor.par()->isFreeSpacing())
1809 /* Ok I'll put some comments here about what is missing.
1810 I have fixed BackSpace (and thus Delete) to not delete
1811 double-spaces automagically. I have also changed Cut,
1812 Copy and Paste to hopefully do some sensible things.
1813 There are still some small problems that can lead to
1814 double spaces stored in the document file or space at
1815 the beginning of paragraphs. This happens if you have
1816 the cursor between to spaces and then save. Or if you
1817 cut and paste and the selection have a space at the
1818 beginning and then save right after the paste. I am
1819 sure none of these are very hard to fix, but I will
1820 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1821 that I can get some feedback. (Lgb)
1824 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1825 // delete the LineSeparator.
1828 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1829 // delete the LineSeparator.
1832 // If the pos around the old_cursor were spaces, delete one of them.
1833 if (old_cursor.par() != cursor.par()
1834 || old_cursor.pos() != cursor.pos()) {
1836 // Only if the cursor has really moved
1837 if (old_cursor.pos() > 0
1838 && old_cursor.pos() < old_cursor.par()->size()
1839 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1840 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1841 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1842 redoParagraph(old_cursor.par());
1846 #ifdef WITH_WARNINGS
1847 #warning This will not work anymore when we have multiple views of the same buffer
1848 // In this case, we will have to correct also the cursors held by
1849 // other bufferviews. It will probably be easier to do that in a more
1850 // automated way in LyXCursor code. (JMarc 26/09/2001)
1852 // correct all cursors held by the LyXText
1853 fixCursorAfterDelete(cursor, old_cursor);
1854 fixCursorAfterDelete(selection.cursor, old_cursor);
1855 fixCursorAfterDelete(selection.start, old_cursor);
1856 fixCursorAfterDelete(selection.end, old_cursor);
1861 // don't delete anything if this is the ONLY paragraph!
1862 if (ownerParagraphs().size() == 1)
1865 // Do not delete empty paragraphs with keepempty set.
1866 if (old_cursor.par()->allowEmpty())
1869 // only do our magic if we changed paragraph
1870 if (old_cursor.par() == cursor.par())
1873 // record if we have deleted a paragraph
1874 // we can't possibly have deleted a paragraph before this point
1875 bool deleted = false;
1877 if (old_cursor.par()->empty() ||
1878 (old_cursor.par()->size() == 1 &&
1879 old_cursor.par()->isLineSeparator(0))) {
1880 // ok, we will delete something
1881 LyXCursor tmpcursor;
1885 bool selection_position_was_oldcursor_position = (
1886 selection.cursor.par() == old_cursor.par()
1887 && selection.cursor.pos() == old_cursor.pos());
1890 cursor = old_cursor; // that undo can restore the right cursor position
1892 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1893 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1896 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1900 ownerParagraphs().erase(old_cursor.par());
1904 setCursorIntern(cursor.par(), cursor.pos());
1906 if (selection_position_was_oldcursor_position) {
1907 // correct selection
1908 selection.cursor = cursor;
1912 if (old_cursor.par()->stripLeadingSpaces()) {
1913 redoParagraph(old_cursor.par());
1915 setCursorIntern(cursor.par(), cursor.pos());
1916 selection.cursor = cursor;
1923 ParagraphList & LyXText::ownerParagraphs() const
1929 bool LyXText::isInInset() const
1931 // Sub-level has non-null bv owner and non-null inset owner.
1932 return inset_owner != 0;
1936 int defaultRowHeight()
1938 LyXFont const font(LyXFont::ALL_SANE);
1939 return int(font_metrics::maxAscent(font)
1940 + font_metrics::maxDescent(font) * 1.5);