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)
63 : height(0), width(0), anchor_y_(0),
64 inset_owner(0), the_locking_inset(0), bv_owner(bv)
68 LyXText::LyXText(BufferView * bv, InsetText * inset)
69 : height(0), width(0), anchor_y_(0),
70 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
74 void LyXText::init(BufferView * bview)
78 ParagraphList::iterator const beg = ownerParagraphs().begin();
79 ParagraphList::iterator const end = ownerParagraphs().end();
80 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
88 current_font = getFont(beg, 0);
90 redoParagraphs(beg, end);
91 setCursorIntern(beg, 0);
92 selection.cursor = cursor;
98 // Gets the fully instantiated font at a given position in a paragraph
99 // Basically the same routine as Paragraph::getFont() in paragraph.C.
100 // The difference is that this one is used for displaying, and thus we
101 // are allowed to make cosmetic improvements. For instance make footnotes
103 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
107 LyXLayout_ptr const & layout = pit->layout();
109 BufferParams const & params = bv()->buffer()->params;
111 // We specialize the 95% common case:
112 if (!pit->getDepth()) {
113 if (layout->labeltype == LABEL_MANUAL
114 && pos < pit->beginningOfBody()) {
116 LyXFont f = pit->getFontSettings(params, pos);
118 pit->inInset()->getDrawFont(f);
119 return f.realize(layout->reslabelfont);
121 LyXFont f = pit->getFontSettings(params, pos);
123 pit->inInset()->getDrawFont(f);
124 return f.realize(layout->resfont);
128 // The uncommon case need not be optimized as much
132 if (pos < pit->beginningOfBody()) {
134 layoutfont = layout->labelfont;
137 layoutfont = layout->font;
140 LyXFont tmpfont = pit->getFontSettings(params, pos);
141 tmpfont.realize(layoutfont);
144 pit->inInset()->getDrawFont(tmpfont);
146 // Realize with the fonts of lesser depth.
147 tmpfont.realize(outerFont(pit, ownerParagraphs()));
148 tmpfont.realize(defaultfont_);
154 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
156 LyXLayout_ptr const & layout = pit->layout();
158 if (!pit->getDepth())
159 return layout->resfont;
161 LyXFont font = layout->font;
162 // Realize with the fonts of lesser depth.
163 font.realize(outerFont(pit, ownerParagraphs()));
164 font.realize(defaultfont_);
170 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
172 LyXLayout_ptr const & layout = pit->layout();
174 if (!pit->getDepth())
175 return layout->reslabelfont;
177 LyXFont font = layout->labelfont;
178 // Realize with the fonts of lesser depth.
179 font.realize(outerFont(pit, ownerParagraphs()));
180 font.realize(defaultfont_);
186 void LyXText::setCharFont(ParagraphList::iterator pit,
187 pos_type pos, LyXFont const & fnt,
190 BufferParams const & params = bv()->buffer()->params;
191 LyXFont font = getFont(pit, pos);
192 font.update(fnt, params.language, toggleall);
193 // Let the insets convert their font
194 if (pit->isInset(pos)) {
195 InsetOld * inset = pit->getInset(pos);
196 if (isEditableInset(inset)) {
197 static_cast<UpdatableInset *>(inset)
198 ->setFont(bv(), fnt, toggleall, true);
202 // Plug through to version below:
203 setCharFont(pit, pos, font);
207 void LyXText::setCharFont(
208 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
211 LyXLayout_ptr const & layout = pit->layout();
213 // Get concrete layout font to reduce against
216 if (pos < pit->beginningOfBody())
217 layoutfont = layout->labelfont;
219 layoutfont = layout->font;
221 // Realize against environment font information
222 if (pit->getDepth()) {
223 ParagraphList::iterator tp = pit;
224 while (!layoutfont.resolved() &&
225 tp != ownerParagraphs().end() &&
227 tp = outerHook(tp, ownerParagraphs());
228 if (tp != ownerParagraphs().end())
229 layoutfont.realize(tp->layout()->font);
233 layoutfont.realize(defaultfont_);
235 // Now, reduce font against full layout font
236 font.reduce(layoutfont);
238 pit->setFont(pos, font);
242 InsetOld * LyXText::getInset() const
244 ParagraphList::iterator pit = cursor.par();
245 pos_type const pos = cursor.pos();
247 if (pos < pit->size() && pit->isInset(pos)) {
248 return pit->getInset(pos);
254 void LyXText::toggleInset()
256 InsetOld * inset = getInset();
257 // is there an editable inset at cursor position?
258 if (!isEditableInset(inset)) {
259 // No, try to see if we are inside a collapsable inset
260 if (inset_owner && inset_owner->owner()
261 && inset_owner->owner()->isOpen()) {
262 bv()->unlockInset(inset_owner->owner());
263 inset_owner->owner()->close(bv());
264 bv()->getLyXText()->cursorRight(bv());
268 //bv()->owner()->message(inset->editMessage());
270 // do we want to keep this?? (JMarc)
271 if (!isHighlyEditableInset(inset))
272 recordUndo(bv(), Undo::ATOMIC);
283 /* used in setlayout */
284 // Asger is not sure we want to do this...
285 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
288 LyXLayout_ptr const & layout = par.layout();
289 pos_type const psize = par.size();
292 for (pos_type pos = 0; pos < psize; ++pos) {
293 if (pos < par.beginningOfBody())
294 layoutfont = layout->labelfont;
296 layoutfont = layout->font;
298 LyXFont tmpfont = par.getFontSettings(params, pos);
299 tmpfont.reduce(layoutfont);
300 par.setFont(pos, tmpfont);
305 ParagraphList::iterator
306 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
307 LyXCursor & send_cur,
308 string const & layout)
310 ParagraphList::iterator endpit = boost::next(send_cur.par());
311 ParagraphList::iterator undoendpit = endpit;
312 ParagraphList::iterator pars_end = ownerParagraphs().end();
314 if (endpit != pars_end && endpit->getDepth()) {
315 while (endpit != pars_end && endpit->getDepth()) {
319 } else if (endpit != pars_end) {
320 // because of parindents etc.
324 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
326 // ok we have a selection. This is always between sstart_cur
327 // and sel_end cursor
329 ParagraphList::iterator pit = sstart_cur.par();
330 ParagraphList::iterator epit = boost::next(send_cur.par());
332 LyXLayout_ptr const & lyxlayout =
333 bv()->buffer()->params.getLyXTextClass()[layout];
336 pit->applyLayout(lyxlayout);
337 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
338 pit->params().spaceTop(lyxlayout->fill_top ?
339 VSpace(VSpace::VFILL)
340 : VSpace(VSpace::NONE));
341 pit->params().spaceBottom(lyxlayout->fill_bottom ?
342 VSpace(VSpace::VFILL)
343 : VSpace(VSpace::NONE));
344 if (lyxlayout->margintype == MARGIN_MANUAL)
345 pit->setLabelWidthString(lyxlayout->labelstring());
348 } while (pit != epit);
354 // set layout over selection and make a total rebreak of those paragraphs
355 void LyXText::setLayout(string const & layout)
357 LyXCursor tmpcursor = cursor; // store the current cursor
359 // if there is no selection just set the layout
360 // of the current paragraph
361 if (!selection.set()) {
362 selection.start = cursor; // dummy selection
363 selection.end = cursor;
366 // special handling of new environment insets
367 BufferParams const & params = bv()->buffer()->params;
368 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
369 if (lyxlayout->is_environment) {
370 // move everything in a new environment inset
371 lyxerr << "setting layout " << layout << endl;
372 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
373 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
374 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
375 InsetOld * inset = new InsetEnvironment(params, layout);
376 if (bv()->insertInset(inset)) {
378 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
385 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
386 selection.end, layout);
387 redoParagraphs(selection.start.par(), endpit);
389 // we have to reset the selection, because the
390 // geometry could have changed
391 setCursor(selection.start.par(), selection.start.pos(), false);
392 selection.cursor = cursor;
393 setCursor(selection.end.par(), selection.end.pos(), false);
397 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
401 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
403 ParagraphList::iterator pit = cursor.par();
404 ParagraphList::iterator end = cursor.par();
405 ParagraphList::iterator start = pit;
407 if (selection.set()) {
408 pit = selection.start.par();
409 end = selection.end.par();
413 ParagraphList::iterator pastend = boost::next(end);
416 recordUndo(bv(), Undo::ATOMIC, start, end);
418 bool changed = false;
420 int prev_after_depth = 0;
421 #warning parlist ... could be nicer ?
422 if (start != ownerParagraphs().begin()) {
423 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
427 int const depth = pit->params().depth();
428 if (type == bv_funcs::INC_DEPTH) {
429 if (depth < prev_after_depth
430 && pit->layout()->labeltype != LABEL_BIBLIO) {
433 pit->params().depth(depth + 1);
438 pit->params().depth(depth - 1);
441 prev_after_depth = pit->getMaxDepthAfter();
453 redoParagraphs(start, pastend);
455 // We need to actually move the text->cursor. I don't
456 // understand why ...
457 LyXCursor tmpcursor = cursor;
459 // we have to reset the visual selection because the
460 // geometry could have changed
461 if (selection.set()) {
462 setCursor(selection.start.par(), selection.start.pos());
463 selection.cursor = cursor;
464 setCursor(selection.end.par(), selection.end.pos());
467 // this handles the counter labels, and also fixes up
468 // depth values for follow-on (child) paragraphs
472 setCursor(tmpcursor.par(), tmpcursor.pos());
478 // set font over selection and make a total rebreak of those paragraphs
479 void LyXText::setFont(LyXFont const & font, bool toggleall)
481 // if there is no selection just set the current_font
482 if (!selection.set()) {
483 // Determine basis font
485 if (cursor.pos() < cursor.par()->beginningOfBody()) {
486 layoutfont = getLabelFont(cursor.par());
488 layoutfont = getLayoutFont(cursor.par());
490 // Update current font
491 real_current_font.update(font,
492 bv()->buffer()->params.language,
495 // Reduce to implicit settings
496 current_font = real_current_font;
497 current_font.reduce(layoutfont);
498 // And resolve it completely
499 real_current_font.realize(layoutfont);
504 LyXCursor tmpcursor = cursor; // store the current cursor
506 // ok we have a selection. This is always between sel_start_cursor
507 // and sel_end cursor
509 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
511 cursor = selection.start;
512 while (cursor.par() != selection.end.par() ||
513 cursor.pos() < selection.end.pos())
515 if (cursor.pos() < cursor.par()->size()) {
516 // an open footnote should behave like a closed one
517 setCharFont(cursor.par(), cursor.pos(),
519 cursor.pos(cursor.pos() + 1);
522 cursor.par(boost::next(cursor.par()));
527 redoParagraph(selection.start.par());
529 // we have to reset the selection, because the
530 // geometry could have changed, but we keep
531 // it for user convenience
532 setCursor(selection.start.par(), selection.start.pos());
533 selection.cursor = cursor;
534 setCursor(selection.end.par(), selection.end.pos());
536 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
537 tmpcursor.boundary());
541 // rebreaks all paragraphs between the specified pars
542 // This function is needed after SetLayout and SetFont etc.
543 void LyXText::redoParagraphs(ParagraphList::iterator start,
544 ParagraphList::iterator end)
546 for ( ; start != end; ++start)
547 redoParagraph(start);
551 void LyXText::redoParagraph(ParagraphList::iterator pit)
553 RowList::iterator rit = pit->rows.begin();
554 RowList::iterator end = pit->rows.end();
556 // remove rows of paragraph
557 for (int i = 0; rit != end; ++rit, ++i)
558 height -= rit->height();
562 // rebreak the paragraph
563 // insert a new row, starting at position 0
566 pit->rows.push_back(Row(z));
569 z = rowBreakPoint(pit, pit->rows.back());
571 RowList::iterator tmprow = boost::prior(pit->rows.end());
573 if (z >= pit->size())
577 pit->rows.push_back(Row(z));
580 tmprow->fill(fill(pit, tmprow, workWidth()));
581 setHeightOfRow(pit, tmprow);
584 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
588 void LyXText::fullRebreak()
590 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
592 selection.cursor = cursor;
596 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
598 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
599 //Assert(mi.base.textwidth);
607 ParagraphList::iterator pit = ownerParagraphs().begin();
608 ParagraphList::iterator end = ownerParagraphs().end();
610 for (; pit != end; ++pit) {
613 InsetList::iterator ii = pit->insetlist.begin();
614 InsetList::iterator iend = pit->insetlist.end();
615 for (; ii != iend; ++ii) {
618 #warning FIXME: pos != 0
619 m.base.font = getFont(pit, 0);
620 ii->inset->metrics(m, dim);
627 dim.asc = firstRow()->ascent_of_text();
628 dim.des = height - dim.asc;
629 dim.wid = std::max(mi.base.textwidth, int(width));
633 // important for the screen
636 // the cursor set functions have a special mechanism. When they
637 // realize, that you left an empty paragraph, they will delete it.
638 // They also delete the corresponding row
640 // need the selection cursor:
641 void LyXText::setSelection()
643 TextCursor::setSelection();
648 void LyXText::clearSelection()
650 TextCursor::clearSelection();
652 // reset this in the bv_owner!
653 if (bv_owner && bv_owner->text)
654 bv_owner->text->xsel_cache.set(false);
658 void LyXText::cursorHome()
660 setCursor(cursor.par(), cursorRow()->pos());
664 void LyXText::cursorEnd()
666 if (cursor.par()->empty())
669 RowList::iterator rit = cursorRow();
670 RowList::iterator next_rit = boost::next(rit);
671 RowList::iterator end = boost::next(rit);
672 ParagraphList::iterator pit = cursor.par();
673 pos_type last_pos = lastPos(*pit, rit);
675 if (next_rit == end) {
679 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
684 setCursor(pit, last_pos);
688 void LyXText::cursorTop()
690 setCursor(ownerParagraphs().begin(), 0);
694 void LyXText::cursorBottom()
696 ParagraphList::iterator lastpit =
697 boost::prior(ownerParagraphs().end());
698 setCursor(lastpit, lastpit->size());
702 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
704 // If the mask is completely neutral, tell user
705 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
706 // Could only happen with user style
707 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
711 // Try implicit word selection
712 // If there is a change in the language the implicit word selection
714 LyXCursor resetCursor = cursor;
715 bool implicitSelection = (font.language() == ignore_language
716 && font.number() == LyXFont::IGNORE)
717 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
720 setFont(font, toggleall);
722 // Implicit selections are cleared afterwards
723 //and cursor is set to the original position.
724 if (implicitSelection) {
726 cursor = resetCursor;
727 setCursor(cursor.par(), cursor.pos());
728 selection.cursor = cursor;
733 string LyXText::getStringToIndex()
735 // Try implicit word selection
736 // If there is a change in the language the implicit word selection
738 LyXCursor const reset_cursor = cursor;
739 bool const implicitSelection =
740 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
743 if (!selection.set())
744 bv()->owner()->message(_("Nothing to index!"));
745 else if (selection.start.par() != selection.end.par())
746 bv()->owner()->message(_("Cannot index more than one paragraph!"));
748 idxstring = selectionAsString(bv()->buffer(), false);
750 // Reset cursors to their original position.
751 cursor = reset_cursor;
752 setCursor(cursor.par(), cursor.pos());
753 selection.cursor = cursor;
755 // Clear the implicit selection.
756 if (implicitSelection)
763 // the DTP switches for paragraphs. LyX will store them in the first
764 // physical paragraph. When a paragraph is broken, the top settings rest,
765 // the bottom settings are given to the new one. So I can make sure,
766 // they do not duplicate themself and you cannnot make dirty things with
769 void LyXText::setParagraph(bool line_top, bool line_bottom,
770 bool pagebreak_top, bool pagebreak_bottom,
771 VSpace const & space_top,
772 VSpace const & space_bottom,
773 Spacing const & spacing,
775 string const & labelwidthstring,
778 LyXCursor tmpcursor = cursor;
779 if (!selection.set()) {
780 selection.start = cursor;
781 selection.end = cursor;
784 // make sure that the depth behind the selection are restored, too
785 ParagraphList::iterator endpit = boost::next(selection.end.par());
786 ParagraphList::iterator undoendpit = endpit;
787 ParagraphList::iterator pars_end = ownerParagraphs().end();
789 if (endpit != pars_end && endpit->getDepth()) {
790 while (endpit != pars_end && endpit->getDepth()) {
794 } else if (endpit != pars_end) {
795 // because of parindents etc.
799 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
800 boost::prior(undoendpit));
803 ParagraphList::iterator tmppit = selection.end.par();
805 while (tmppit != boost::prior(selection.start.par())) {
806 setCursor(tmppit, 0);
808 ParagraphList::iterator pit = cursor.par();
809 ParagraphParameters & params = pit->params();
811 params.lineTop(line_top);
812 params.lineBottom(line_bottom);
813 params.pagebreakTop(pagebreak_top);
814 params.pagebreakBottom(pagebreak_bottom);
815 params.spaceTop(space_top);
816 params.spaceBottom(space_bottom);
817 params.spacing(spacing);
818 // does the layout allow the new alignment?
819 LyXLayout_ptr const & layout = pit->layout();
821 if (align == LYX_ALIGN_LAYOUT)
822 align = layout->align;
823 if (align & layout->alignpossible) {
824 if (align == layout->align)
825 params.align(LYX_ALIGN_LAYOUT);
829 pit->setLabelWidthString(labelwidthstring);
830 params.noindent(noindent);
831 tmppit = boost::prior(pit);
834 redoParagraphs(selection.start.par(), endpit);
837 setCursor(selection.start.par(), selection.start.pos());
838 selection.cursor = cursor;
839 setCursor(selection.end.par(), selection.end.pos());
841 setCursor(tmpcursor.par(), tmpcursor.pos());
847 // set the counter of a paragraph. This includes the labels
848 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
850 LyXTextClass const & textclass = buf->params.getLyXTextClass();
851 LyXLayout_ptr const & layout = pit->layout();
853 if (pit != ownerParagraphs().begin()) {
855 pit->params().appendix(boost::prior(pit)->params().appendix());
856 if (!pit->params().appendix() &&
857 pit->params().startOfAppendix()) {
858 pit->params().appendix(true);
859 textclass.counters().reset();
861 pit->enumdepth = boost::prior(pit)->enumdepth;
862 pit->itemdepth = boost::prior(pit)->itemdepth;
864 pit->params().appendix(pit->params().startOfAppendix());
869 // Maybe we have to increment the enumeration depth.
870 // BUT, enumeration in a footnote is considered in isolation from its
871 // surrounding paragraph so don't increment if this is the
872 // first line of the footnote
873 // AND, bibliographies can't have their depth changed ie. they
874 // are always of depth 0
875 if (pit != ownerParagraphs().begin()
876 && boost::prior(pit)->getDepth() < pit->getDepth()
877 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
878 && pit->enumdepth < 3
879 && layout->labeltype != LABEL_BIBLIO) {
883 // Maybe we have to decrement the enumeration depth, see note above
884 if (pit != ownerParagraphs().begin()
885 && boost::prior(pit)->getDepth() > pit->getDepth()
886 && layout->labeltype != LABEL_BIBLIO) {
887 pit->enumdepth = depthHook(pit, ownerParagraphs(),
888 pit->getDepth())->enumdepth;
891 if (!pit->params().labelString().empty()) {
892 pit->params().labelString(string());
895 if (layout->margintype == MARGIN_MANUAL) {
896 if (pit->params().labelWidthString().empty())
897 pit->setLabelWidthString(layout->labelstring());
899 pit->setLabelWidthString(string());
902 // is it a layout that has an automatic label?
903 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
904 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
908 if (i >= 0 && i <= buf->params.secnumdepth) {
912 textclass.counters().step(layout->latexname());
914 // Is there a label? Useful for Chapter layout
915 if (!pit->params().appendix()) {
916 s << buf->B_(layout->labelstring());
918 s << buf->B_(layout->labelstring_appendix());
921 // Use of an integer is here less than elegant. For now.
922 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
923 if (!pit->params().appendix()) {
924 numbertype = "sectioning";
926 numbertype = "appendix";
927 if (pit->isRightToLeftPar(buf->params))
934 << textclass.counters()
935 .numberLabel(layout->latexname(),
936 numbertype, langtype, head);
938 pit->params().labelString(STRCONV(s.str()));
940 // reset enum counters
941 textclass.counters().reset("enum");
942 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
943 textclass.counters().reset("enum");
944 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
946 // Yes I know this is a really, really! bad solution
948 string enumcounter("enum");
950 switch (pit->enumdepth) {
962 // not a valid enumdepth...
966 textclass.counters().step(enumcounter);
968 s << textclass.counters()
969 .numberLabel(enumcounter, "enumeration");
970 pit->params().labelString(STRCONV(s.str()));
972 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
973 textclass.counters().step("bibitem");
974 int number = textclass.counters().value("bibitem");
975 if (pit->bibitem()) {
976 pit->bibitem()->setCounter(number);
977 pit->params().labelString(layout->labelstring());
979 // In biblio should't be following counters but...
981 string s = buf->B_(layout->labelstring());
984 if (layout->labeltype == LABEL_SENSITIVE) {
985 ParagraphList::iterator end = ownerParagraphs().end();
986 ParagraphList::iterator tmppit = pit;
989 while (tmppit != end && tmppit->inInset()
990 // the single '=' is intended below
991 && (in = tmppit->inInset()->owner()))
993 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
994 in->lyxCode() == InsetOld::WRAP_CODE) {
998 tmppit = ownerParagraphs().begin();
999 for ( ; tmppit != end; ++tmppit)
1000 if (&*tmppit == in->parOwner())
1008 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1009 type = static_cast<InsetFloat*>(in)->params().type;
1010 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1011 type = static_cast<InsetWrap*>(in)->params().type;
1015 Floating const & fl = textclass.floats().getType(type);
1017 textclass.counters().step(fl.type());
1019 // Doesn't work... yet.
1020 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1022 // par->SetLayout(0);
1023 // s = layout->labelstring;
1024 s = _("Senseless: ");
1027 pit->params().labelString(s);
1029 // reset the enumeration counter. They are always reset
1030 // when there is any other layout between
1031 // Just fall-through between the cases so that all
1032 // enum counters deeper than enumdepth is also reset.
1033 switch (pit->enumdepth) {
1035 textclass.counters().reset("enumi");
1037 textclass.counters().reset("enumii");
1039 textclass.counters().reset("enumiii");
1041 textclass.counters().reset("enumiv");
1047 // Updates all counters. Paragraphs with changed label string will be rebroken
1048 void LyXText::updateCounters()
1051 bv()->buffer()->params.getLyXTextClass().counters().reset();
1053 ParagraphList::iterator beg = ownerParagraphs().begin();
1054 ParagraphList::iterator end = ownerParagraphs().end();
1055 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1056 string const oldLabel = pit->params().labelString();
1058 size_t maxdepth = 0;
1060 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1062 if (pit->params().depth() > maxdepth)
1063 pit->params().depth(maxdepth);
1065 // setCounter can potentially change the labelString.
1066 setCounter(bv()->buffer(), pit);
1068 string const & newLabel = pit->params().labelString();
1070 if (oldLabel != newLabel)
1076 void LyXText::insertInset(InsetOld * inset)
1078 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1080 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1082 cursor.par()->insertInset(cursor.pos(), inset);
1083 // Just to rebreak and refresh correctly.
1084 // The character will not be inserted a second time
1085 insertChar(Paragraph::META_INSET);
1086 // If we enter a highly editable inset the cursor should be before
1087 // the inset. After an Undo LyX tries to call inset->edit(...)
1088 // and fails if the cursor is behind the inset and getInset
1089 // does not return the inset!
1090 if (isHighlyEditableInset(inset))
1096 void LyXText::cutSelection(bool doclear, bool realcut)
1098 // Stuff what we got on the clipboard. Even if there is no selection.
1100 // There is a problem with having the stuffing here in that the
1101 // larger the selection the slower LyX will get. This can be
1102 // solved by running the line below only when the selection has
1103 // finished. The solution used currently just works, to make it
1104 // faster we need to be more clever and probably also have more
1105 // calls to stuffClipboard. (Lgb)
1106 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1108 // This doesn't make sense, if there is no selection
1109 if (!selection.set())
1112 // OK, we have a selection. This is always between selection.start
1113 // and selection.end
1115 // make sure that the depth behind the selection are restored, too
1116 ParagraphList::iterator endpit = boost::next(selection.end.par());
1117 ParagraphList::iterator undoendpit = endpit;
1118 ParagraphList::iterator pars_end = ownerParagraphs().end();
1120 if (endpit != pars_end && endpit->getDepth()) {
1121 while (endpit != pars_end && endpit->getDepth()) {
1123 undoendpit = endpit;
1125 } else if (endpit != pars_end) {
1126 // because of parindents etc.
1130 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1131 boost::prior(undoendpit));
1133 endpit = selection.end.par();
1134 int endpos = selection.end.pos();
1136 boost::tie(endpit, endpos) = realcut ?
1137 CutAndPaste::cutSelection(bv()->buffer()->params,
1139 selection.start.par(), endpit,
1140 selection.start.pos(), endpos,
1141 bv()->buffer()->params.textclass,
1143 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1145 selection.start.par(), endpit,
1146 selection.start.pos(), endpos,
1148 // sometimes necessary
1150 selection.start.par()->stripLeadingSpaces();
1152 redoParagraphs(selection.start.par(), boost::next(endpit));
1153 // cutSelection can invalidate the cursor so we need to set
1155 // we prefer the end for when tracking changes
1159 // need a valid cursor. (Lgb)
1162 setCursor(cursor.par(), cursor.pos());
1163 selection.cursor = cursor;
1168 void LyXText::copySelection()
1170 // stuff the selection onto the X clipboard, from an explicit copy request
1171 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1173 // this doesnt make sense, if there is no selection
1174 if (!selection.set())
1177 // ok we have a selection. This is always between selection.start
1178 // and sel_end cursor
1180 // copy behind a space if there is one
1181 while (selection.start.par()->size() > selection.start.pos()
1182 && selection.start.par()->isLineSeparator(selection.start.pos())
1183 && (selection.start.par() != selection.end.par()
1184 || selection.start.pos() < selection.end.pos()))
1185 selection.start.pos(selection.start.pos() + 1);
1187 CutAndPaste::copySelection(selection.start.par(),
1188 selection.end.par(),
1189 selection.start.pos(), selection.end.pos(),
1190 bv()->buffer()->params.textclass);
1194 void LyXText::pasteSelection(size_t sel_index)
1196 // this does not make sense, if there is nothing to paste
1197 if (!CutAndPaste::checkPastePossible())
1200 recordUndo(bv(), Undo::INSERT, cursor.par());
1202 ParagraphList::iterator endpit;
1207 boost::tie(ppp, endpit) =
1208 CutAndPaste::pasteSelection(*bv()->buffer(),
1210 cursor.par(), cursor.pos(),
1211 bv()->buffer()->params.textclass,
1213 bufferErrors(*bv()->buffer(), el);
1214 bv()->showErrorList(_("Paste"));
1216 redoParagraphs(cursor.par(), endpit);
1218 setCursor(cursor.par(), cursor.pos());
1221 selection.cursor = cursor;
1222 setCursor(ppp.first, ppp.second);
1228 void LyXText::setSelectionRange(lyx::pos_type length)
1233 selection.cursor = cursor;
1240 // simple replacing. The font of the first selected character is used
1241 void LyXText::replaceSelectionWithString(string const & str)
1243 recordUndo(bv(), Undo::ATOMIC);
1246 if (!selection.set()) { // create a dummy selection
1247 selection.end = cursor;
1248 selection.start = cursor;
1251 // Get font setting before we cut
1252 pos_type pos = selection.end.pos();
1253 LyXFont const font = selection.start.par()
1254 ->getFontSettings(bv()->buffer()->params,
1255 selection.start.pos());
1257 // Insert the new string
1258 string::const_iterator cit = str.begin();
1259 string::const_iterator end = str.end();
1260 for (; cit != end; ++cit) {
1261 selection.end.par()->insertChar(pos, (*cit), font);
1265 // Cut the selection
1266 cutSelection(true, false);
1272 // needed to insert the selection
1273 void LyXText::insertStringAsLines(string const & str)
1275 ParagraphList::iterator pit = cursor.par();
1276 pos_type pos = cursor.pos();
1277 ParagraphList::iterator endpit = boost::next(cursor.par());
1279 recordUndo(bv(), Undo::ATOMIC);
1281 // only to be sure, should not be neccessary
1284 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1286 redoParagraphs(cursor.par(), endpit);
1287 setCursor(cursor.par(), cursor.pos());
1288 selection.cursor = cursor;
1289 setCursor(pit, pos);
1294 // turns double-CR to single CR, others where converted into one
1295 // blank. Then InsertStringAsLines is called
1296 void LyXText::insertStringAsParagraphs(string const & str)
1298 string linestr(str);
1299 bool newline_inserted = false;
1300 string::size_type const siz = linestr.length();
1302 for (string::size_type i = 0; i < siz; ++i) {
1303 if (linestr[i] == '\n') {
1304 if (newline_inserted) {
1305 // we know that \r will be ignored by
1306 // InsertStringA. Of course, it is a dirty
1307 // trick, but it works...
1308 linestr[i - 1] = '\r';
1312 newline_inserted = true;
1314 } else if (IsPrintable(linestr[i])) {
1315 newline_inserted = false;
1318 insertStringAsLines(linestr);
1322 bool LyXText::setCursor(ParagraphList::iterator pit,
1324 bool setfont, bool boundary)
1326 LyXCursor old_cursor = cursor;
1327 setCursorIntern(pit, pos, setfont, boundary);
1328 return deleteEmptyParagraphMechanism(old_cursor);
1332 void LyXText::redoCursor()
1334 #warning maybe the same for selections?
1335 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1339 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1340 pos_type pos, bool boundary)
1342 Assert(pit != ownerParagraphs().end());
1346 cur.boundary(boundary);
1350 // get the cursor y position in text
1352 RowList::iterator row = getRow(pit, pos, y);
1353 RowList::iterator old_row = row;
1354 // if we are before the first char of this row and are still in the
1355 // same paragraph and there is a previous row then put the cursor on
1356 // the end of the previous row
1357 cur.iy(y + row->baseline());
1358 if (row != pit->rows.begin()
1360 && pos < pit->size()
1361 && pit->getChar(pos) == Paragraph::META_INSET) {
1362 InsetOld * ins = pit->getInset(pos);
1363 if (ins && (ins->needFullRow() || ins->display())) {
1369 // y is now the beginning of the cursor row
1370 y += row->baseline();
1371 // y is now the cursor baseline
1374 pos_type last = lastPrintablePos(*pit, old_row);
1376 // None of these should happen, but we're scaredy-cats
1377 if (pos > pit->size()) {
1378 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1381 } else if (pos > last + 1) {
1382 lyxerr << "dont like 2 please report" << endl;
1383 // This shouldn't happen.
1386 } else if (pos < row->pos()) {
1387 lyxerr << "dont like 3 please report" << endl;
1392 // now get the cursors x position
1393 float x = getCursorX(pit, row, pos, last, boundary);
1396 if (old_row != row) {
1397 x = getCursorX(pit, old_row, pos, last, boundary);
1401 /* We take out this for the time being because 1) the redraw code is not
1402 prepared to this yet and 2) because some good policy has yet to be decided
1403 while editting: for instance how to act on rows being created/deleted
1407 //if the cursor is in a visible row, anchor to it
1409 if (topy < y && y < topy + bv()->workHeight())
1415 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1416 pos_type pos, pos_type last, bool boundary) const
1418 pos_type cursor_vpos = 0;
1420 double fill_separator;
1422 double fill_label_hfill;
1423 // This call HAS to be here because of the BidiTables!!!
1424 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1427 pos_type const rit_pos = rit->pos();
1430 cursor_vpos = rit_pos;
1431 else if (pos > last && !boundary)
1432 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1433 ? rit_pos : last + 1;
1434 else if (pos > rit_pos && (pos > last || boundary))
1435 // Place cursor after char at (logical) position pos - 1
1436 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1437 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1439 // Place cursor before char at (logical) position pos
1440 cursor_vpos = (bidi_level(pos) % 2 == 0)
1441 ? log2vis(pos) : log2vis(pos) + 1;
1443 pos_type body_pos = pit->beginningOfBody();
1445 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1448 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1449 pos_type pos = vis2log(vpos);
1450 if (body_pos > 0 && pos == body_pos - 1) {
1451 x += fill_label_hfill +
1452 font_metrics::width(
1453 pit->layout()->labelsep, getLabelFont(pit));
1454 if (pit->isLineSeparator(body_pos - 1))
1455 x -= singleWidth(pit, body_pos - 1);
1458 if (hfillExpansion(*pit, rit, pos)) {
1459 x += singleWidth(pit, pos);
1460 if (pos >= body_pos)
1463 x += fill_label_hfill;
1464 } else if (pit->isSeparator(pos)) {
1465 x += singleWidth(pit, pos);
1466 if (pos >= body_pos)
1467 x += fill_separator;
1469 x += singleWidth(pit, pos);
1475 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1476 pos_type pos, bool setfont, bool boundary)
1478 setCursor(cursor, pit, pos, boundary);
1484 void LyXText::setCurrentFont()
1486 pos_type pos = cursor.pos();
1487 ParagraphList::iterator pit = cursor.par();
1489 if (cursor.boundary() && pos > 0)
1493 if (pos == pit->size())
1495 else // potentional bug... BUG (Lgb)
1496 if (pit->isSeparator(pos)) {
1497 if (pos > cursorRow()->pos() &&
1498 bidi_level(pos) % 2 ==
1499 bidi_level(pos - 1) % 2)
1501 else if (pos + 1 < pit->size())
1506 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1507 real_current_font = getFont(pit, pos);
1509 if (cursor.pos() == pit->size() &&
1510 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1511 !cursor.boundary()) {
1512 Language const * lang =
1513 pit->getParLanguage(bv()->buffer()->params);
1514 current_font.setLanguage(lang);
1515 current_font.setNumber(LyXFont::OFF);
1516 real_current_font.setLanguage(lang);
1517 real_current_font.setNumber(LyXFont::OFF);
1522 // returns the column near the specified x-coordinate of the row
1523 // x is set to the real beginning of this column
1524 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1525 RowList::iterator rit, int & x, bool & boundary) const
1528 double fill_separator;
1530 double fill_label_hfill;
1532 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1534 pos_type vc = rit->pos();
1535 pos_type last = lastPrintablePos(*pit, rit);
1537 LyXLayout_ptr const & layout = pit->layout();
1539 bool left_side = false;
1541 pos_type body_pos = pit->beginningOfBody();
1542 double last_tmpx = tmpx;
1545 (body_pos - 1 > last ||
1546 !pit->isLineSeparator(body_pos - 1)))
1549 // check for empty row
1555 while (vc <= last && tmpx <= x) {
1558 if (body_pos > 0 && c == body_pos - 1) {
1559 tmpx += fill_label_hfill +
1560 font_metrics::width(layout->labelsep, getLabelFont(pit));
1561 if (pit->isLineSeparator(body_pos - 1))
1562 tmpx -= singleWidth(pit, body_pos - 1);
1565 if (hfillExpansion(*pit, rit, c)) {
1566 tmpx += singleWidth(pit, c);
1570 tmpx += fill_label_hfill;
1571 } else if (pit->isSeparator(c)) {
1572 tmpx += singleWidth(pit, c);
1574 tmpx += fill_separator;
1576 tmpx += singleWidth(pit, c);
1581 if ((tmpx + last_tmpx) / 2 > x) {
1586 if (vc > last + 1) // This shouldn't happen.
1590 // This (rtl_support test) is not needed, but gives
1591 // some speedup if rtl_support == false
1592 bool const lastrow = lyxrc.rtl_support
1593 && boost::next(rit) == pit->rows.end();
1595 // If lastrow is false, we don't need to compute
1596 // the value of rtl.
1597 bool const rtl = (lastrow)
1598 ? pit->isRightToLeftPar(bv()->buffer()->params)
1601 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1602 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1604 else if (vc == rit->pos()) {
1606 if (bidi_level(c) % 2 == 1)
1609 c = vis2log(vc - 1);
1610 bool const rtl = (bidi_level(c) % 2 == 1);
1611 if (left_side == rtl) {
1613 boundary = isBoundary(bv()->buffer(), *pit, c);
1617 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1618 if (bidi_level(last) % 2 == 0)
1619 tmpx -= singleWidth(pit, last);
1621 tmpx += singleWidth(pit, last);
1631 void LyXText::setCursorFromCoordinates(int x, int y)
1633 LyXCursor old_cursor = cursor;
1634 setCursorFromCoordinates(cursor, x, y);
1636 deleteEmptyParagraphMechanism(old_cursor);
1643 * return true if the cursor given is at the end of a row,
1644 * and the next row is filled by an inset that spans an entire
1647 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1649 RowList::iterator row = lt.getRow(cur);
1650 RowList::iterator next = boost::next(row);
1652 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1655 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1658 InsetOld const * inset = cur.par()->getInset(cur.pos());
1659 if (inset->needFullRow() || inset->display())
1667 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1669 // Get the row first.
1670 ParagraphList::iterator pit;
1671 RowList::iterator row = getRowNearY(y, pit);
1673 pos_type const column = getColumnNearX(pit, row, x, bound);
1675 cur.pos(row->pos() + column);
1677 cur.y(y + row->baseline());
1679 // if (beforeFullRowInset(*this, cur)) {
1680 // pos_type const last = lastPrintablePos(*this, pit, row);
1681 // RowList::iterator next_row = nextRow(row);
1682 // cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1683 // cur.iy(y + row->height() + next_row->baseline());
1688 cur.boundary(bound);
1692 void LyXText::cursorLeft(bool internal)
1694 if (cursor.pos() > 0) {
1695 bool boundary = cursor.boundary();
1696 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1697 if (!internal && !boundary &&
1698 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1699 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1700 } else if (cursor.par() != ownerParagraphs().begin()) {
1701 // steps into the paragraph above
1702 ParagraphList::iterator pit = boost::prior(cursor.par());
1703 setCursor(pit, pit->size());
1708 void LyXText::cursorRight(bool internal)
1710 bool const at_end = (cursor.pos() == cursor.par()->size());
1711 bool const at_newline = !at_end &&
1712 cursor.par()->isNewline(cursor.pos());
1714 if (!internal && cursor.boundary() && !at_newline)
1715 setCursor(cursor.par(), cursor.pos(), true, false);
1717 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1719 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1720 setCursor(cursor.par(), cursor.pos(), true, true);
1721 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1722 setCursor(boost::next(cursor.par()), 0);
1726 void LyXText::cursorUp(bool selecting)
1729 int x = cursor.x_fix();
1730 int y = cursor.y() - cursorRow()->baseline() - 1;
1731 setCursorFromCoordinates(x, y);
1734 int y1 = cursor.iy() - topy;
1737 InsetOld * inset_hit = checkInsetHit(x, y1);
1738 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1739 inset_hit->localDispatch(
1740 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1744 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1745 cursorRow()->baseline() << endl;
1746 setCursorFromCoordinates(cursor.x_fix(),
1747 cursor.y() - cursorRow()->baseline() - 1);
1752 void LyXText::cursorDown(bool selecting)
1755 int x = cursor.x_fix();
1756 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1757 setCursorFromCoordinates(x, y);
1758 if (!selecting && cursorRow() == cursorIRow()) {
1760 int y1 = cursor.iy() - topy;
1763 InsetOld * inset_hit = checkInsetHit(x, y1);
1764 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1765 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1766 inset_hit->localDispatch(cmd);
1770 setCursorFromCoordinates(cursor.x_fix(),
1771 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1776 void LyXText::cursorUpParagraph()
1778 if (cursor.pos() > 0)
1779 setCursor(cursor.par(), 0);
1780 else if (cursor.par() != ownerParagraphs().begin())
1781 setCursor(boost::prior(cursor.par()), 0);
1785 void LyXText::cursorDownParagraph()
1787 ParagraphList::iterator par = cursor.par();
1788 ParagraphList::iterator next_par = boost::next(par);
1790 if (next_par != ownerParagraphs().end())
1791 setCursor(next_par, 0);
1793 setCursor(par, par->size());
1797 // fix the cursor `cur' after a characters has been deleted at `where'
1798 // position. Called by deleteEmptyParagraphMechanism
1799 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1801 // if cursor is not in the paragraph where the delete occured,
1803 if (cur.par() != where.par())
1806 // if cursor position is after the place where the delete occured,
1808 if (cur.pos() > where.pos())
1809 cur.pos(cur.pos()-1);
1811 // check also if we don't want to set the cursor on a spot behind the
1812 // pagragraph because we erased the last character.
1813 if (cur.pos() > cur.par()->size())
1814 cur.pos(cur.par()->size());
1816 // recompute row et al. for this cursor
1817 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1821 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1823 // Would be wrong to delete anything if we have a selection.
1824 if (selection.set())
1827 // We allow all kinds of "mumbo-jumbo" when freespacing.
1828 if (old_cursor.par()->isFreeSpacing())
1831 /* Ok I'll put some comments here about what is missing.
1832 I have fixed BackSpace (and thus Delete) to not delete
1833 double-spaces automagically. I have also changed Cut,
1834 Copy and Paste to hopefully do some sensible things.
1835 There are still some small problems that can lead to
1836 double spaces stored in the document file or space at
1837 the beginning of paragraphs. This happens if you have
1838 the cursor betwenn to spaces and then save. Or if you
1839 cut and paste and the selection have a space at the
1840 beginning and then save right after the paste. I am
1841 sure none of these are very hard to fix, but I will
1842 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1843 that I can get some feedback. (Lgb)
1846 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1847 // delete the LineSeparator.
1850 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1851 // delete the LineSeparator.
1854 // If the pos around the old_cursor were spaces, delete one of them.
1855 if (old_cursor.par() != cursor.par()
1856 || old_cursor.pos() != cursor.pos()) {
1858 // Only if the cursor has really moved
1859 if (old_cursor.pos() > 0
1860 && old_cursor.pos() < old_cursor.par()->size()
1861 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1862 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1863 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1864 redoParagraph(old_cursor.par());
1868 #ifdef WITH_WARNINGS
1869 #warning This will not work anymore when we have multiple views of the same buffer
1870 // In this case, we will have to correct also the cursors held by
1871 // other bufferviews. It will probably be easier to do that in a more
1872 // automated way in LyXCursor code. (JMarc 26/09/2001)
1874 // correct all cursors held by the LyXText
1875 fixCursorAfterDelete(cursor, old_cursor);
1876 fixCursorAfterDelete(selection.cursor, old_cursor);
1877 fixCursorAfterDelete(selection.start, old_cursor);
1878 fixCursorAfterDelete(selection.end, old_cursor);
1883 // don't delete anything if this is the ONLY paragraph!
1884 if (ownerParagraphs().size() == 1)
1887 // Do not delete empty paragraphs with keepempty set.
1888 if (old_cursor.par()->allowEmpty())
1891 // only do our magic if we changed paragraph
1892 if (old_cursor.par() == cursor.par())
1895 // record if we have deleted a paragraph
1896 // we can't possibly have deleted a paragraph before this point
1897 bool deleted = false;
1899 if (old_cursor.par()->empty() ||
1900 (old_cursor.par()->size() == 1 &&
1901 old_cursor.par()->isLineSeparator(0))) {
1902 // ok, we will delete anything
1903 LyXCursor tmpcursor;
1907 bool selection_position_was_oldcursor_position = (
1908 selection.cursor.par() == old_cursor.par()
1909 && selection.cursor.pos() == old_cursor.pos());
1912 cursor = old_cursor; // that undo can restore the right cursor position
1914 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1915 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1918 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1922 ownerParagraphs().erase(old_cursor.par());
1926 setCursorIntern(cursor.par(), cursor.pos());
1928 if (selection_position_was_oldcursor_position) {
1929 // correct selection
1930 selection.cursor = cursor;
1934 if (old_cursor.par()->stripLeadingSpaces()) {
1935 redoParagraph(old_cursor.par());
1937 setCursorIntern(cursor.par(), cursor.pos());
1938 selection.cursor = cursor;
1945 ParagraphList & LyXText::ownerParagraphs() const
1948 return inset_owner->paragraphs;
1950 return bv_owner->buffer()->paragraphs;
1954 bool LyXText::isInInset() const
1956 // Sub-level has non-null bv owner and non-null inset owner.
1957 return inset_owner != 0 && bv_owner != 0;
1961 int defaultRowHeight()
1963 LyXFont const font(LyXFont::ALL_SANE);
1964 return int(font_metrics::maxAscent(font)
1965 + font_metrics::maxDescent(font) * 1.5);