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());
607 updateRowPositions();
610 dim.asc = firstRow()->ascent_of_text();
611 dim.des = height - dim.asc;
612 dim.wid = std::max(mi.base.textwidth, int(width));
616 // important for the screen
619 // the cursor set functions have a special mechanism. When they
620 // realize, that you left an empty paragraph, they will delete it.
621 // They also delete the corresponding row
623 // need the selection cursor:
624 void LyXText::setSelection()
626 TextCursor::setSelection();
631 void LyXText::clearSelection()
633 TextCursor::clearSelection();
635 // reset this in the bv_owner!
636 if (bv_owner && bv_owner->text)
637 bv_owner->text->xsel_cache.set(false);
641 void LyXText::cursorHome()
643 setCursor(cursor.par(), cursorRow()->pos());
647 void LyXText::cursorEnd()
649 if (cursor.par()->empty())
652 RowList::iterator rit = cursorRow();
653 RowList::iterator next_rit = boost::next(rit);
654 RowList::iterator end = boost::next(rit);
655 ParagraphList::iterator pit = cursor.par();
656 pos_type last_pos = lastPos(*pit, rit);
658 if (next_rit == end) {
662 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
667 setCursor(pit, last_pos);
671 void LyXText::cursorTop()
673 setCursor(ownerParagraphs().begin(), 0);
677 void LyXText::cursorBottom()
679 ParagraphList::iterator lastpit =
680 boost::prior(ownerParagraphs().end());
681 setCursor(lastpit, lastpit->size());
685 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
687 // If the mask is completely neutral, tell user
688 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
689 // Could only happen with user style
690 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
694 // Try implicit word selection
695 // If there is a change in the language the implicit word selection
697 LyXCursor resetCursor = cursor;
698 bool implicitSelection = (font.language() == ignore_language
699 && font.number() == LyXFont::IGNORE)
700 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
703 setFont(font, toggleall);
705 // Implicit selections are cleared afterwards
706 //and cursor is set to the original position.
707 if (implicitSelection) {
709 cursor = resetCursor;
710 setCursor(cursor.par(), cursor.pos());
711 selection.cursor = cursor;
716 string LyXText::getStringToIndex()
718 // Try implicit word selection
719 // If there is a change in the language the implicit word selection
721 LyXCursor const reset_cursor = cursor;
722 bool const implicitSelection =
723 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
726 if (!selection.set())
727 bv()->owner()->message(_("Nothing to index!"));
728 else if (selection.start.par() != selection.end.par())
729 bv()->owner()->message(_("Cannot index more than one paragraph!"));
731 idxstring = selectionAsString(bv()->buffer(), false);
733 // Reset cursors to their original position.
734 cursor = reset_cursor;
735 setCursor(cursor.par(), cursor.pos());
736 selection.cursor = cursor;
738 // Clear the implicit selection.
739 if (implicitSelection)
746 // the DTP switches for paragraphs. LyX will store them in the first
747 // physical paragraph. When a paragraph is broken, the top settings rest,
748 // the bottom settings are given to the new one. So I can make sure,
749 // they do not duplicate themself and you cannnot make dirty things with
752 void LyXText::setParagraph(bool line_top, bool line_bottom,
753 bool pagebreak_top, bool pagebreak_bottom,
754 VSpace const & space_top,
755 VSpace const & space_bottom,
756 Spacing const & spacing,
758 string const & labelwidthstring,
761 LyXCursor tmpcursor = cursor;
762 if (!selection.set()) {
763 selection.start = cursor;
764 selection.end = cursor;
767 // make sure that the depth behind the selection are restored, too
768 ParagraphList::iterator endpit = boost::next(selection.end.par());
769 ParagraphList::iterator undoendpit = endpit;
770 ParagraphList::iterator pars_end = ownerParagraphs().end();
772 if (endpit != pars_end && endpit->getDepth()) {
773 while (endpit != pars_end && endpit->getDepth()) {
777 } else if (endpit != pars_end) {
778 // because of parindents etc.
782 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
783 boost::prior(undoendpit));
786 ParagraphList::iterator tmppit = selection.end.par();
788 while (tmppit != boost::prior(selection.start.par())) {
789 setCursor(tmppit, 0);
791 ParagraphList::iterator pit = cursor.par();
792 ParagraphParameters & params = pit->params();
794 params.lineTop(line_top);
795 params.lineBottom(line_bottom);
796 params.pagebreakTop(pagebreak_top);
797 params.pagebreakBottom(pagebreak_bottom);
798 params.spaceTop(space_top);
799 params.spaceBottom(space_bottom);
800 params.spacing(spacing);
801 // does the layout allow the new alignment?
802 LyXLayout_ptr const & layout = pit->layout();
804 if (align == LYX_ALIGN_LAYOUT)
805 align = layout->align;
806 if (align & layout->alignpossible) {
807 if (align == layout->align)
808 params.align(LYX_ALIGN_LAYOUT);
812 pit->setLabelWidthString(labelwidthstring);
813 params.noindent(noindent);
814 tmppit = boost::prior(pit);
817 redoParagraphs(selection.start.par(), endpit);
820 setCursor(selection.start.par(), selection.start.pos());
821 selection.cursor = cursor;
822 setCursor(selection.end.par(), selection.end.pos());
824 setCursor(tmpcursor.par(), tmpcursor.pos());
830 // set the counter of a paragraph. This includes the labels
831 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
833 LyXTextClass const & textclass = buf->params.getLyXTextClass();
834 LyXLayout_ptr const & layout = pit->layout();
836 if (pit != ownerParagraphs().begin()) {
838 pit->params().appendix(boost::prior(pit)->params().appendix());
839 if (!pit->params().appendix() &&
840 pit->params().startOfAppendix()) {
841 pit->params().appendix(true);
842 textclass.counters().reset();
844 pit->enumdepth = boost::prior(pit)->enumdepth;
845 pit->itemdepth = boost::prior(pit)->itemdepth;
847 pit->params().appendix(pit->params().startOfAppendix());
852 // Maybe we have to increment the enumeration depth.
853 // BUT, enumeration in a footnote is considered in isolation from its
854 // surrounding paragraph so don't increment if this is the
855 // first line of the footnote
856 // AND, bibliographies can't have their depth changed ie. they
857 // are always of depth 0
858 if (pit != ownerParagraphs().begin()
859 && boost::prior(pit)->getDepth() < pit->getDepth()
860 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
861 && pit->enumdepth < 3
862 && layout->labeltype != LABEL_BIBLIO) {
866 // Maybe we have to decrement the enumeration depth, see note above
867 if (pit != ownerParagraphs().begin()
868 && boost::prior(pit)->getDepth() > pit->getDepth()
869 && layout->labeltype != LABEL_BIBLIO) {
870 pit->enumdepth = depthHook(pit, ownerParagraphs(),
871 pit->getDepth())->enumdepth;
874 if (!pit->params().labelString().empty()) {
875 pit->params().labelString(string());
878 if (layout->margintype == MARGIN_MANUAL) {
879 if (pit->params().labelWidthString().empty())
880 pit->setLabelWidthString(layout->labelstring());
882 pit->setLabelWidthString(string());
885 // is it a layout that has an automatic label?
886 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
887 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
891 if (i >= 0 && i <= buf->params.secnumdepth) {
895 textclass.counters().step(layout->latexname());
897 // Is there a label? Useful for Chapter layout
898 if (!pit->params().appendix()) {
899 s << buf->B_(layout->labelstring());
901 s << buf->B_(layout->labelstring_appendix());
904 // Use of an integer is here less than elegant. For now.
905 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
906 if (!pit->params().appendix()) {
907 numbertype = "sectioning";
909 numbertype = "appendix";
910 if (pit->isRightToLeftPar(buf->params))
917 << textclass.counters()
918 .numberLabel(layout->latexname(),
919 numbertype, langtype, head);
921 pit->params().labelString(STRCONV(s.str()));
923 // reset enum counters
924 textclass.counters().reset("enum");
925 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
926 textclass.counters().reset("enum");
927 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
929 // Yes I know this is a really, really! bad solution
931 string enumcounter("enum");
933 switch (pit->enumdepth) {
945 // not a valid enumdepth...
949 textclass.counters().step(enumcounter);
951 s << textclass.counters()
952 .numberLabel(enumcounter, "enumeration");
953 pit->params().labelString(STRCONV(s.str()));
955 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
956 textclass.counters().step("bibitem");
957 int number = textclass.counters().value("bibitem");
958 if (pit->bibitem()) {
959 pit->bibitem()->setCounter(number);
960 pit->params().labelString(layout->labelstring());
962 // In biblio should't be following counters but...
964 string s = buf->B_(layout->labelstring());
967 if (layout->labeltype == LABEL_SENSITIVE) {
968 ParagraphList::iterator end = ownerParagraphs().end();
969 ParagraphList::iterator tmppit = pit;
972 while (tmppit != end && tmppit->inInset()
973 // the single '=' is intended below
974 && (in = tmppit->inInset()->owner()))
976 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
977 in->lyxCode() == InsetOld::WRAP_CODE) {
981 tmppit = ownerParagraphs().begin();
982 for ( ; tmppit != end; ++tmppit)
983 if (&*tmppit == in->parOwner())
991 if (in->lyxCode() == InsetOld::FLOAT_CODE)
992 type = static_cast<InsetFloat*>(in)->params().type;
993 else if (in->lyxCode() == InsetOld::WRAP_CODE)
994 type = static_cast<InsetWrap*>(in)->params().type;
998 Floating const & fl = textclass.floats().getType(type);
1000 textclass.counters().step(fl.type());
1002 // Doesn't work... yet.
1003 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1005 // par->SetLayout(0);
1006 // s = layout->labelstring;
1007 s = _("Senseless: ");
1010 pit->params().labelString(s);
1012 // reset the enumeration counter. They are always reset
1013 // when there is any other layout between
1014 // Just fall-through between the cases so that all
1015 // enum counters deeper than enumdepth is also reset.
1016 switch (pit->enumdepth) {
1018 textclass.counters().reset("enumi");
1020 textclass.counters().reset("enumii");
1022 textclass.counters().reset("enumiii");
1024 textclass.counters().reset("enumiv");
1030 // Updates all counters. Paragraphs with changed label string will be rebroken
1031 void LyXText::updateCounters()
1034 bv()->buffer()->params.getLyXTextClass().counters().reset();
1036 ParagraphList::iterator beg = ownerParagraphs().begin();
1037 ParagraphList::iterator end = ownerParagraphs().end();
1038 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1039 string const oldLabel = pit->params().labelString();
1041 size_t maxdepth = 0;
1043 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1045 if (pit->params().depth() > maxdepth)
1046 pit->params().depth(maxdepth);
1048 // setCounter can potentially change the labelString.
1049 setCounter(bv()->buffer(), pit);
1051 string const & newLabel = pit->params().labelString();
1053 if (oldLabel != newLabel)
1059 void LyXText::insertInset(InsetOld * inset)
1061 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1063 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1065 cursor.par()->insertInset(cursor.pos(), inset);
1066 // Just to rebreak and refresh correctly.
1067 // The character will not be inserted a second time
1068 insertChar(Paragraph::META_INSET);
1069 // If we enter a highly editable inset the cursor should be before
1070 // the inset. After an Undo LyX tries to call inset->edit(...)
1071 // and fails if the cursor is behind the inset and getInset
1072 // does not return the inset!
1073 if (isHighlyEditableInset(inset))
1079 void LyXText::cutSelection(bool doclear, bool realcut)
1081 // Stuff what we got on the clipboard. Even if there is no selection.
1083 // There is a problem with having the stuffing here in that the
1084 // larger the selection the slower LyX will get. This can be
1085 // solved by running the line below only when the selection has
1086 // finished. The solution used currently just works, to make it
1087 // faster we need to be more clever and probably also have more
1088 // calls to stuffClipboard. (Lgb)
1089 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1091 // This doesn't make sense, if there is no selection
1092 if (!selection.set())
1095 // OK, we have a selection. This is always between selection.start
1096 // and selection.end
1098 // make sure that the depth behind the selection are restored, too
1099 ParagraphList::iterator endpit = boost::next(selection.end.par());
1100 ParagraphList::iterator undoendpit = endpit;
1101 ParagraphList::iterator pars_end = ownerParagraphs().end();
1103 if (endpit != pars_end && endpit->getDepth()) {
1104 while (endpit != pars_end && endpit->getDepth()) {
1106 undoendpit = endpit;
1108 } else if (endpit != pars_end) {
1109 // because of parindents etc.
1113 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1114 boost::prior(undoendpit));
1116 endpit = selection.end.par();
1117 int endpos = selection.end.pos();
1119 boost::tie(endpit, endpos) = realcut ?
1120 CutAndPaste::cutSelection(bv()->buffer()->params,
1122 selection.start.par(), endpit,
1123 selection.start.pos(), endpos,
1124 bv()->buffer()->params.textclass,
1126 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1128 selection.start.par(), endpit,
1129 selection.start.pos(), endpos,
1131 // sometimes necessary
1133 selection.start.par()->stripLeadingSpaces();
1135 redoParagraphs(selection.start.par(), boost::next(endpit));
1136 // cutSelection can invalidate the cursor so we need to set
1138 // we prefer the end for when tracking changes
1142 // need a valid cursor. (Lgb)
1145 setCursor(cursor.par(), cursor.pos());
1146 selection.cursor = cursor;
1151 void LyXText::copySelection()
1153 // stuff the selection onto the X clipboard, from an explicit copy request
1154 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1156 // this doesnt make sense, if there is no selection
1157 if (!selection.set())
1160 // ok we have a selection. This is always between selection.start
1161 // and sel_end cursor
1163 // copy behind a space if there is one
1164 while (selection.start.par()->size() > selection.start.pos()
1165 && selection.start.par()->isLineSeparator(selection.start.pos())
1166 && (selection.start.par() != selection.end.par()
1167 || selection.start.pos() < selection.end.pos()))
1168 selection.start.pos(selection.start.pos() + 1);
1170 CutAndPaste::copySelection(selection.start.par(),
1171 selection.end.par(),
1172 selection.start.pos(), selection.end.pos(),
1173 bv()->buffer()->params.textclass);
1177 void LyXText::pasteSelection(size_t sel_index)
1179 // this does not make sense, if there is nothing to paste
1180 if (!CutAndPaste::checkPastePossible())
1183 recordUndo(bv(), Undo::INSERT, cursor.par());
1185 ParagraphList::iterator endpit;
1190 boost::tie(ppp, endpit) =
1191 CutAndPaste::pasteSelection(*bv()->buffer(),
1193 cursor.par(), cursor.pos(),
1194 bv()->buffer()->params.textclass,
1196 bufferErrors(*bv()->buffer(), el);
1197 bv()->showErrorList(_("Paste"));
1199 redoParagraphs(cursor.par(), endpit);
1201 setCursor(cursor.par(), cursor.pos());
1204 selection.cursor = cursor;
1205 setCursor(ppp.first, ppp.second);
1211 void LyXText::setSelectionRange(lyx::pos_type length)
1216 selection.cursor = cursor;
1223 // simple replacing. The font of the first selected character is used
1224 void LyXText::replaceSelectionWithString(string const & str)
1226 recordUndo(bv(), Undo::ATOMIC);
1229 if (!selection.set()) { // create a dummy selection
1230 selection.end = cursor;
1231 selection.start = cursor;
1234 // Get font setting before we cut
1235 pos_type pos = selection.end.pos();
1236 LyXFont const font = selection.start.par()
1237 ->getFontSettings(bv()->buffer()->params,
1238 selection.start.pos());
1240 // Insert the new string
1241 string::const_iterator cit = str.begin();
1242 string::const_iterator end = str.end();
1243 for (; cit != end; ++cit) {
1244 selection.end.par()->insertChar(pos, (*cit), font);
1248 // Cut the selection
1249 cutSelection(true, false);
1255 // needed to insert the selection
1256 void LyXText::insertStringAsLines(string const & str)
1258 ParagraphList::iterator pit = cursor.par();
1259 pos_type pos = cursor.pos();
1260 ParagraphList::iterator endpit = boost::next(cursor.par());
1262 recordUndo(bv(), Undo::ATOMIC);
1264 // only to be sure, should not be neccessary
1267 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1269 redoParagraphs(cursor.par(), endpit);
1270 setCursor(cursor.par(), cursor.pos());
1271 selection.cursor = cursor;
1272 setCursor(pit, pos);
1277 // turns double-CR to single CR, others where converted into one
1278 // blank. Then InsertStringAsLines is called
1279 void LyXText::insertStringAsParagraphs(string const & str)
1281 string linestr(str);
1282 bool newline_inserted = false;
1283 string::size_type const siz = linestr.length();
1285 for (string::size_type i = 0; i < siz; ++i) {
1286 if (linestr[i] == '\n') {
1287 if (newline_inserted) {
1288 // we know that \r will be ignored by
1289 // InsertStringA. Of course, it is a dirty
1290 // trick, but it works...
1291 linestr[i - 1] = '\r';
1295 newline_inserted = true;
1297 } else if (IsPrintable(linestr[i])) {
1298 newline_inserted = false;
1301 insertStringAsLines(linestr);
1305 bool LyXText::setCursor(ParagraphList::iterator pit,
1307 bool setfont, bool boundary)
1309 LyXCursor old_cursor = cursor;
1310 setCursorIntern(pit, pos, setfont, boundary);
1311 return deleteEmptyParagraphMechanism(old_cursor);
1315 void LyXText::redoCursor()
1317 #warning maybe the same for selections?
1318 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1322 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1323 pos_type pos, bool boundary)
1325 Assert(pit != ownerParagraphs().end());
1329 cur.boundary(boundary);
1333 // get the cursor y position in text
1335 RowList::iterator row = getRow(pit, pos, y);
1336 RowList::iterator old_row = row;
1337 // if we are before the first char of this row and are still in the
1338 // same paragraph and there is a previous row then put the cursor on
1339 // the end of the previous row
1340 cur.iy(y + row->baseline());
1341 if (row != pit->rows.begin()
1343 && pos < pit->size()
1344 && pit->getChar(pos) == Paragraph::META_INSET) {
1345 InsetOld * ins = pit->getInset(pos);
1346 if (ins && (ins->needFullRow() || ins->display())) {
1352 // y is now the beginning of the cursor row
1353 y += row->baseline();
1354 // y is now the cursor baseline
1357 pos_type last = lastPrintablePos(*pit, old_row);
1359 // None of these should happen, but we're scaredy-cats
1360 if (pos > pit->size()) {
1361 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1364 } else if (pos > last + 1) {
1365 lyxerr << "dont like 2 please report" << endl;
1366 // This shouldn't happen.
1369 } else if (pos < row->pos()) {
1370 lyxerr << "dont like 3 please report" << endl;
1375 // now get the cursors x position
1376 float x = getCursorX(pit, row, pos, last, boundary);
1379 if (old_row != row) {
1380 x = getCursorX(pit, old_row, pos, last, boundary);
1384 /* We take out this for the time being because 1) the redraw code is not
1385 prepared to this yet and 2) because some good policy has yet to be decided
1386 while editting: for instance how to act on rows being created/deleted
1390 //if the cursor is in a visible row, anchor to it
1392 if (topy < y && y < topy + bv()->workHeight())
1398 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1399 pos_type pos, pos_type last, bool boundary) const
1401 pos_type cursor_vpos = 0;
1402 double x = rit->x();
1403 double fill_separator = rit->fill_separator();
1404 double fill_hfill = rit->fill_hfill();
1405 double fill_label_hfill = rit->fill_label_hfill();
1406 pos_type const rit_pos = rit->pos();
1409 cursor_vpos = rit_pos;
1410 else if (pos > last && !boundary)
1411 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1412 ? rit_pos : last + 1;
1413 else if (pos > rit_pos && (pos > last || boundary))
1414 // Place cursor after char at (logical) position pos - 1
1415 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1416 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1418 // Place cursor before char at (logical) position pos
1419 cursor_vpos = (bidi_level(pos) % 2 == 0)
1420 ? log2vis(pos) : log2vis(pos) + 1;
1422 pos_type body_pos = pit->beginningOfBody();
1424 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1427 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1428 pos_type pos = vis2log(vpos);
1429 if (body_pos > 0 && pos == body_pos - 1) {
1430 x += fill_label_hfill +
1431 font_metrics::width(
1432 pit->layout()->labelsep, getLabelFont(pit));
1433 if (pit->isLineSeparator(body_pos - 1))
1434 x -= singleWidth(pit, body_pos - 1);
1437 if (hfillExpansion(*pit, rit, pos)) {
1438 x += singleWidth(pit, pos);
1439 if (pos >= body_pos)
1442 x += fill_label_hfill;
1443 } else if (pit->isSeparator(pos)) {
1444 x += singleWidth(pit, pos);
1445 if (pos >= body_pos)
1446 x += fill_separator;
1448 x += singleWidth(pit, pos);
1454 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1455 pos_type pos, bool setfont, bool boundary)
1457 setCursor(cursor, pit, pos, boundary);
1463 void LyXText::setCurrentFont()
1465 pos_type pos = cursor.pos();
1466 ParagraphList::iterator pit = cursor.par();
1468 if (cursor.boundary() && pos > 0)
1472 if (pos == pit->size())
1474 else // potentional bug... BUG (Lgb)
1475 if (pit->isSeparator(pos)) {
1476 if (pos > cursorRow()->pos() &&
1477 bidi_level(pos) % 2 ==
1478 bidi_level(pos - 1) % 2)
1480 else if (pos + 1 < pit->size())
1485 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1486 real_current_font = getFont(pit, pos);
1488 if (cursor.pos() == pit->size() &&
1489 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1490 !cursor.boundary()) {
1491 Language const * lang =
1492 pit->getParLanguage(bv()->buffer()->params);
1493 current_font.setLanguage(lang);
1494 current_font.setNumber(LyXFont::OFF);
1495 real_current_font.setLanguage(lang);
1496 real_current_font.setNumber(LyXFont::OFF);
1501 // returns the column near the specified x-coordinate of the row
1502 // x is set to the real beginning of this column
1503 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1504 RowList::iterator rit, int & x, bool & boundary) const
1506 double tmpx = rit->x();
1507 double fill_separator = rit->fill_separator();
1508 double fill_hfill = rit->fill_hfill();
1509 double fill_label_hfill = rit->fill_label_hfill();
1511 pos_type vc = rit->pos();
1512 pos_type last = lastPrintablePos(*pit, rit);
1514 LyXLayout_ptr const & layout = pit->layout();
1516 bool left_side = false;
1518 pos_type body_pos = pit->beginningOfBody();
1519 double last_tmpx = tmpx;
1522 (body_pos - 1 > last ||
1523 !pit->isLineSeparator(body_pos - 1)))
1526 // check for empty row
1532 while (vc <= last && tmpx <= x) {
1535 if (body_pos > 0 && c == body_pos - 1) {
1536 tmpx += fill_label_hfill +
1537 font_metrics::width(layout->labelsep, getLabelFont(pit));
1538 if (pit->isLineSeparator(body_pos - 1))
1539 tmpx -= singleWidth(pit, body_pos - 1);
1542 if (hfillExpansion(*pit, rit, c)) {
1543 tmpx += singleWidth(pit, c);
1547 tmpx += fill_label_hfill;
1548 } else if (pit->isSeparator(c)) {
1549 tmpx += singleWidth(pit, c);
1551 tmpx += fill_separator;
1553 tmpx += singleWidth(pit, c);
1558 if ((tmpx + last_tmpx) / 2 > x) {
1563 if (vc > last + 1) // This shouldn't happen.
1567 // This (rtl_support test) is not needed, but gives
1568 // some speedup if rtl_support == false
1569 bool const lastrow = lyxrc.rtl_support
1570 && boost::next(rit) == pit->rows.end();
1572 // If lastrow is false, we don't need to compute
1573 // the value of rtl.
1574 bool const rtl = (lastrow)
1575 ? pit->isRightToLeftPar(bv()->buffer()->params)
1578 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1579 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1581 else if (vc == rit->pos()) {
1583 if (bidi_level(c) % 2 == 1)
1586 c = vis2log(vc - 1);
1587 bool const rtl = (bidi_level(c) % 2 == 1);
1588 if (left_side == rtl) {
1590 boundary = isBoundary(bv()->buffer(), *pit, c);
1594 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1595 if (bidi_level(last) % 2 == 0)
1596 tmpx -= singleWidth(pit, last);
1598 tmpx += singleWidth(pit, last);
1608 void LyXText::setCursorFromCoordinates(int x, int y)
1610 LyXCursor old_cursor = cursor;
1611 setCursorFromCoordinates(cursor, x, y);
1613 deleteEmptyParagraphMechanism(old_cursor);
1620 * return true if the cursor given is at the end of a row,
1621 * and the next row is filled by an inset that spans an entire
1624 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1626 RowList::iterator row = lt.getRow(cur);
1627 RowList::iterator next = boost::next(row);
1629 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1632 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1635 InsetOld const * inset = cur.par()->getInset(cur.pos());
1636 if (inset->needFullRow() || inset->display())
1644 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1646 // Get the row first.
1647 ParagraphList::iterator pit;
1648 RowList::iterator rit = getRowNearY(y, pit);
1650 pos_type const column = getColumnNearX(pit, rit, x, bound);
1652 cur.pos(rit->pos() + column);
1654 cur.y(y + rit->baseline());
1656 if (beforeFullRowInset(*this, cur)) {
1657 pos_type const last = lastPrintablePos(*pit, rit);
1658 RowList::iterator next_rit = rit;
1659 ParagraphList::iterator next_pit = pit;
1660 nextRow(next_pit, next_rit);
1661 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1662 cur.iy(y + rit->height() + next_rit->baseline());
1667 cur.boundary(bound);
1671 void LyXText::cursorLeft(bool internal)
1673 if (cursor.pos() > 0) {
1674 bool boundary = cursor.boundary();
1675 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1676 if (!internal && !boundary &&
1677 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1678 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1679 } else if (cursor.par() != ownerParagraphs().begin()) {
1680 // steps into the paragraph above
1681 ParagraphList::iterator pit = boost::prior(cursor.par());
1682 setCursor(pit, pit->size());
1687 void LyXText::cursorRight(bool internal)
1689 bool const at_end = (cursor.pos() == cursor.par()->size());
1690 bool const at_newline = !at_end &&
1691 cursor.par()->isNewline(cursor.pos());
1693 if (!internal && cursor.boundary() && !at_newline)
1694 setCursor(cursor.par(), cursor.pos(), true, false);
1696 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1698 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1699 setCursor(cursor.par(), cursor.pos(), true, true);
1700 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1701 setCursor(boost::next(cursor.par()), 0);
1705 void LyXText::cursorUp(bool selecting)
1708 int x = cursor.x_fix();
1709 int y = cursor.y() - cursorRow()->baseline() - 1;
1710 setCursorFromCoordinates(x, y);
1713 int y1 = cursor.iy() - topy;
1716 InsetOld * inset_hit = checkInsetHit(x, y1);
1717 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1718 inset_hit->localDispatch(
1719 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1723 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1724 cursorRow()->baseline() << endl;
1725 setCursorFromCoordinates(cursor.x_fix(),
1726 cursor.y() - cursorRow()->baseline() - 1);
1731 void LyXText::cursorDown(bool selecting)
1734 int x = cursor.x_fix();
1735 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1736 setCursorFromCoordinates(x, y);
1737 if (!selecting && cursorRow() == cursorIRow()) {
1739 int y1 = cursor.iy() - topy;
1742 InsetOld * inset_hit = checkInsetHit(x, y1);
1743 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1744 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1745 inset_hit->localDispatch(cmd);
1749 setCursorFromCoordinates(cursor.x_fix(),
1750 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1755 void LyXText::cursorUpParagraph()
1757 if (cursor.pos() > 0)
1758 setCursor(cursor.par(), 0);
1759 else if (cursor.par() != ownerParagraphs().begin())
1760 setCursor(boost::prior(cursor.par()), 0);
1764 void LyXText::cursorDownParagraph()
1766 ParagraphList::iterator par = cursor.par();
1767 ParagraphList::iterator next_par = boost::next(par);
1769 if (next_par != ownerParagraphs().end())
1770 setCursor(next_par, 0);
1772 setCursor(par, par->size());
1776 // fix the cursor `cur' after a characters has been deleted at `where'
1777 // position. Called by deleteEmptyParagraphMechanism
1778 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1780 // if cursor is not in the paragraph where the delete occured,
1782 if (cur.par() != where.par())
1785 // if cursor position is after the place where the delete occured,
1787 if (cur.pos() > where.pos())
1788 cur.pos(cur.pos()-1);
1790 // check also if we don't want to set the cursor on a spot behind the
1791 // pagragraph because we erased the last character.
1792 if (cur.pos() > cur.par()->size())
1793 cur.pos(cur.par()->size());
1795 // recompute row et al. for this cursor
1796 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1800 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1802 // Would be wrong to delete anything if we have a selection.
1803 if (selection.set())
1806 // We allow all kinds of "mumbo-jumbo" when freespacing.
1807 if (old_cursor.par()->isFreeSpacing())
1810 /* Ok I'll put some comments here about what is missing.
1811 I have fixed BackSpace (and thus Delete) to not delete
1812 double-spaces automagically. I have also changed Cut,
1813 Copy and Paste to hopefully do some sensible things.
1814 There are still some small problems that can lead to
1815 double spaces stored in the document file or space at
1816 the beginning of paragraphs. This happens if you have
1817 the cursor between to spaces and then save. Or if you
1818 cut and paste and the selection have a space at the
1819 beginning and then save right after the paste. I am
1820 sure none of these are very hard to fix, but I will
1821 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1822 that I can get some feedback. (Lgb)
1825 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1826 // delete the LineSeparator.
1829 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1830 // delete the LineSeparator.
1833 // If the pos around the old_cursor were spaces, delete one of them.
1834 if (old_cursor.par() != cursor.par()
1835 || old_cursor.pos() != cursor.pos()) {
1837 // Only if the cursor has really moved
1838 if (old_cursor.pos() > 0
1839 && old_cursor.pos() < old_cursor.par()->size()
1840 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1841 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1842 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1843 redoParagraph(old_cursor.par());
1847 #ifdef WITH_WARNINGS
1848 #warning This will not work anymore when we have multiple views of the same buffer
1849 // In this case, we will have to correct also the cursors held by
1850 // other bufferviews. It will probably be easier to do that in a more
1851 // automated way in LyXCursor code. (JMarc 26/09/2001)
1853 // correct all cursors held by the LyXText
1854 fixCursorAfterDelete(cursor, old_cursor);
1855 fixCursorAfterDelete(selection.cursor, old_cursor);
1856 fixCursorAfterDelete(selection.start, old_cursor);
1857 fixCursorAfterDelete(selection.end, old_cursor);
1862 // don't delete anything if this is the ONLY paragraph!
1863 if (ownerParagraphs().size() == 1)
1866 // Do not delete empty paragraphs with keepempty set.
1867 if (old_cursor.par()->allowEmpty())
1870 // only do our magic if we changed paragraph
1871 if (old_cursor.par() == cursor.par())
1874 // record if we have deleted a paragraph
1875 // we can't possibly have deleted a paragraph before this point
1876 bool deleted = false;
1878 if (old_cursor.par()->empty() ||
1879 (old_cursor.par()->size() == 1 &&
1880 old_cursor.par()->isLineSeparator(0))) {
1881 // ok, we will delete something
1882 LyXCursor tmpcursor;
1886 bool selection_position_was_oldcursor_position = (
1887 selection.cursor.par() == old_cursor.par()
1888 && selection.cursor.pos() == old_cursor.pos());
1891 cursor = old_cursor; // that undo can restore the right cursor position
1893 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1894 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1897 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1901 ownerParagraphs().erase(old_cursor.par());
1905 setCursorIntern(cursor.par(), cursor.pos());
1907 if (selection_position_was_oldcursor_position) {
1908 // correct selection
1909 selection.cursor = cursor;
1913 if (old_cursor.par()->stripLeadingSpaces()) {
1914 redoParagraph(old_cursor.par());
1916 setCursorIntern(cursor.par(), cursor.pos());
1917 selection.cursor = cursor;
1924 ParagraphList & LyXText::ownerParagraphs() const
1930 bool LyXText::isInInset() const
1932 // Sub-level has non-null bv owner and non-null inset owner.
1933 return inset_owner != 0;
1937 int defaultRowHeight()
1939 LyXFont const font(LyXFont::ALL_SANE);
1940 return int(font_metrics::maxAscent(font)
1941 + font_metrics::maxDescent(font) * 1.5);