3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
30 #include "CutAndPaste.h"
32 #include "errorlist.h"
34 #include "FloatList.h"
35 #include "funcrequest.h"
40 #include "lyxrow_funcs.h"
41 #include "metricsinfo.h"
42 #include "paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
45 #include "undo_funcs.h"
48 #include "frontends/font_metrics.h"
49 #include "frontends/LyXView.h"
51 #include "insets/insetbibitem.h"
52 #include "insets/insetenv.h"
53 #include "insets/insetfloat.h"
54 #include "insets/insetwrap.h"
56 #include "support/lstrings.h"
57 #include "support/textutils.h"
58 #include "support/tostr.h"
59 #include "support/std_sstream.h"
61 #include <boost/tuple/tuple.hpp>
64 using lyx::support::bformat;
67 using std::ostringstream;
71 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
72 ParagraphList & paragraphs)
73 : height(0), width(0), anchor_y_(0),
74 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
75 in_inset_(ininset), paragraphs_(paragraphs)
80 void LyXText::init(BufferView * bview)
84 ParagraphList::iterator const beg = ownerParagraphs().begin();
85 ParagraphList::iterator const end = ownerParagraphs().end();
86 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
94 current_font = getFont(beg, 0);
96 redoParagraphs(beg, end);
97 setCursorIntern(beg, 0);
98 selection.cursor = cursor;
104 // Gets the fully instantiated font at a given position in a paragraph
105 // Basically the same routine as Paragraph::getFont() in paragraph.C.
106 // The difference is that this one is used for displaying, and thus we
107 // are allowed to make cosmetic improvements. For instance make footnotes
109 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
111 BOOST_ASSERT(pos >= 0);
113 LyXLayout_ptr const & layout = pit->layout();
115 BufferParams const & params = bv()->buffer()->params();
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 if (layout->labeltype == LABEL_MANUAL
120 && pos < pit->beginningOfBody()) {
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->reslabelfont);
127 LyXFont f = pit->getFontSettings(params, pos);
129 pit->inInset()->getDrawFont(f);
130 return f.realize(layout->resfont);
134 // The uncommon case need not be optimized as much
138 if (pos < pit->beginningOfBody()) {
140 layoutfont = layout->labelfont;
143 layoutfont = layout->font;
146 LyXFont tmpfont = pit->getFontSettings(params, pos);
147 tmpfont.realize(layoutfont);
150 pit->inInset()->getDrawFont(tmpfont);
152 // Realize with the fonts of lesser depth.
153 tmpfont.realize(outerFont(pit, ownerParagraphs()));
154 tmpfont.realize(defaultfont_);
160 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
162 LyXLayout_ptr const & layout = pit->layout();
164 if (!pit->getDepth())
165 return layout->resfont;
167 LyXFont font = layout->font;
168 // Realize with the fonts of lesser depth.
169 font.realize(outerFont(pit, ownerParagraphs()));
170 font.realize(defaultfont_);
176 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
178 LyXLayout_ptr const & layout = pit->layout();
180 if (!pit->getDepth())
181 return layout->reslabelfont;
183 LyXFont font = layout->labelfont;
184 // Realize with the fonts of lesser depth.
185 font.realize(outerFont(pit, ownerParagraphs()));
186 font.realize(defaultfont_);
192 void LyXText::setCharFont(ParagraphList::iterator pit,
193 pos_type pos, LyXFont const & fnt,
196 BufferParams const & params = bv()->buffer()->params();
197 LyXFont font = getFont(pit, pos);
198 font.update(fnt, params.language, toggleall);
199 // Let the insets convert their font
200 if (pit->isInset(pos)) {
201 InsetOld * inset = pit->getInset(pos);
202 if (isEditableInset(inset)) {
203 static_cast<UpdatableInset *>(inset)
204 ->setFont(bv(), fnt, toggleall, true);
208 // Plug through to version below:
209 setCharFont(pit, pos, font);
213 void LyXText::setCharFont(
214 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
217 LyXLayout_ptr const & layout = pit->layout();
219 // Get concrete layout font to reduce against
222 if (pos < pit->beginningOfBody())
223 layoutfont = layout->labelfont;
225 layoutfont = layout->font;
227 // Realize against environment font information
228 if (pit->getDepth()) {
229 ParagraphList::iterator tp = pit;
230 while (!layoutfont.resolved() &&
231 tp != ownerParagraphs().end() &&
233 tp = outerHook(tp, ownerParagraphs());
234 if (tp != ownerParagraphs().end())
235 layoutfont.realize(tp->layout()->font);
239 layoutfont.realize(defaultfont_);
241 // Now, reduce font against full layout font
242 font.reduce(layoutfont);
244 pit->setFont(pos, font);
248 InsetOld * LyXText::getInset() const
250 ParagraphList::iterator pit = cursor.par();
251 pos_type const pos = cursor.pos();
253 if (pos < pit->size() && pit->isInset(pos)) {
254 return pit->getInset(pos);
260 void LyXText::toggleInset()
262 InsetOld * inset = getInset();
263 // is there an editable inset at cursor position?
264 if (!isEditableInset(inset)) {
265 // No, try to see if we are inside a collapsable inset
266 if (inset_owner && inset_owner->owner()
267 && inset_owner->owner()->isOpen()) {
268 bv()->unlockInset(inset_owner->owner());
269 inset_owner->owner()->close(bv());
270 bv()->getLyXText()->cursorRight(bv());
274 //bv()->owner()->message(inset->editMessage());
276 // do we want to keep this?? (JMarc)
277 if (!isHighlyEditableInset(inset))
278 recordUndo(bv(), Undo::ATOMIC);
285 bv()->updateInset(inset);
289 /* used in setlayout */
290 // Asger is not sure we want to do this...
291 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
294 LyXLayout_ptr const & layout = par.layout();
295 pos_type const psize = par.size();
298 for (pos_type pos = 0; pos < psize; ++pos) {
299 if (pos < par.beginningOfBody())
300 layoutfont = layout->labelfont;
302 layoutfont = layout->font;
304 LyXFont tmpfont = par.getFontSettings(params, pos);
305 tmpfont.reduce(layoutfont);
306 par.setFont(pos, tmpfont);
311 ParagraphList::iterator
312 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
313 LyXCursor & send_cur,
314 string const & layout)
316 ParagraphList::iterator endpit = boost::next(send_cur.par());
317 ParagraphList::iterator undoendpit = endpit;
318 ParagraphList::iterator pars_end = ownerParagraphs().end();
320 if (endpit != pars_end && endpit->getDepth()) {
321 while (endpit != pars_end && endpit->getDepth()) {
325 } else if (endpit != pars_end) {
326 // because of parindents etc.
330 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
332 // ok we have a selection. This is always between sstart_cur
333 // and sel_end cursor
335 ParagraphList::iterator pit = sstart_cur.par();
336 ParagraphList::iterator epit = boost::next(send_cur.par());
338 BufferParams const & bufparams = bv()->buffer()->params();
339 LyXLayout_ptr const & lyxlayout =
340 bufparams.getLyXTextClass()[layout];
343 pit->applyLayout(lyxlayout);
344 makeFontEntriesLayoutSpecific(bufparams, *pit);
345 pit->params().spaceTop(lyxlayout->fill_top ?
346 VSpace(VSpace::VFILL)
347 : VSpace(VSpace::NONE));
348 pit->params().spaceBottom(lyxlayout->fill_bottom ?
349 VSpace(VSpace::VFILL)
350 : VSpace(VSpace::NONE));
351 if (lyxlayout->margintype == MARGIN_MANUAL)
352 pit->setLabelWidthString(lyxlayout->labelstring());
355 } while (pit != epit);
361 // set layout over selection and make a total rebreak of those paragraphs
362 void LyXText::setLayout(string const & layout)
364 LyXCursor tmpcursor = cursor; // store the current cursor
366 // if there is no selection just set the layout
367 // of the current paragraph
368 if (!selection.set()) {
369 selection.start = cursor; // dummy selection
370 selection.end = cursor;
373 // special handling of new environment insets
374 BufferParams const & params = bv()->buffer()->params();
375 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
376 if (lyxlayout->is_environment) {
377 // move everything in a new environment inset
378 lyxerr << "setting layout " << layout << endl;
379 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
380 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
381 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
382 InsetOld * inset = new InsetEnvironment(params, layout);
383 if (bv()->insertInset(inset)) {
385 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
392 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
393 selection.end, layout);
394 redoParagraphs(selection.start.par(), endpit);
396 // we have to reset the selection, because the
397 // geometry could have changed
398 setCursor(selection.start.par(), selection.start.pos(), false);
399 selection.cursor = cursor;
400 setCursor(selection.end.par(), selection.end.pos(), false);
404 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
408 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
410 ParagraphList::iterator pit = cursor.par();
411 ParagraphList::iterator end = cursor.par();
412 ParagraphList::iterator start = pit;
414 if (selection.set()) {
415 pit = selection.start.par();
416 end = selection.end.par();
420 ParagraphList::iterator pastend = boost::next(end);
423 recordUndo(bv(), Undo::ATOMIC, start, end);
425 bool changed = false;
427 int prev_after_depth = 0;
428 #warning parlist ... could be nicer ?
429 if (start != ownerParagraphs().begin()) {
430 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
434 int const depth = pit->params().depth();
435 if (type == bv_funcs::INC_DEPTH) {
436 if (depth < prev_after_depth
437 && pit->layout()->labeltype != LABEL_BIBLIO) {
440 pit->params().depth(depth + 1);
445 pit->params().depth(depth - 1);
448 prev_after_depth = pit->getMaxDepthAfter();
460 redoParagraphs(start, pastend);
462 // We need to actually move the text->cursor. I don't
463 // understand why ...
464 LyXCursor tmpcursor = cursor;
466 // we have to reset the visual selection because the
467 // geometry could have changed
468 if (selection.set()) {
469 setCursor(selection.start.par(), selection.start.pos());
470 selection.cursor = cursor;
471 setCursor(selection.end.par(), selection.end.pos());
474 // this handles the counter labels, and also fixes up
475 // depth values for follow-on (child) paragraphs
479 setCursor(tmpcursor.par(), tmpcursor.pos());
485 // set font over selection and make a total rebreak of those paragraphs
486 void LyXText::setFont(LyXFont const & font, bool toggleall)
488 // if there is no selection just set the current_font
489 if (!selection.set()) {
490 // Determine basis font
492 if (cursor.pos() < cursor.par()->beginningOfBody()) {
493 layoutfont = getLabelFont(cursor.par());
495 layoutfont = getLayoutFont(cursor.par());
497 // Update current font
498 real_current_font.update(font,
499 bv()->buffer()->params().language,
502 // Reduce to implicit settings
503 current_font = real_current_font;
504 current_font.reduce(layoutfont);
505 // And resolve it completely
506 real_current_font.realize(layoutfont);
511 LyXCursor tmpcursor = cursor; // store the current cursor
513 // ok we have a selection. This is always between sel_start_cursor
514 // and sel_end cursor
516 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
518 cursor = selection.start;
519 while (cursor.par() != selection.end.par() ||
520 cursor.pos() < selection.end.pos())
522 if (cursor.pos() < cursor.par()->size()) {
523 // an open footnote should behave like a closed one
524 setCharFont(cursor.par(), cursor.pos(),
526 cursor.pos(cursor.pos() + 1);
529 cursor.par(boost::next(cursor.par()));
534 redoParagraph(selection.start.par());
536 // we have to reset the selection, because the
537 // geometry could have changed, but we keep
538 // it for user convenience
539 setCursor(selection.start.par(), selection.start.pos());
540 selection.cursor = cursor;
541 setCursor(selection.end.par(), selection.end.pos());
543 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
544 tmpcursor.boundary());
548 int LyXText::redoParagraphInternal(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);
576 // set height and fill and width of rows
577 int const ww = workWidth();
578 for (rit = pit->rows.begin(); rit != end; ++rit) {
579 int const f = fill(pit, rit, ww);
580 int const w = ww - f;
581 par_width = std::max(par_width, w);
584 prepareToPrint(pit, rit);
585 setHeightOfRow(pit, rit);
586 height += rit->height();
589 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
594 int LyXText::redoParagraphs(ParagraphList::iterator start,
595 ParagraphList::iterator end)
598 for ( ; start != end; ++start) {
599 int par_width = redoParagraphInternal(start);
600 pars_width = std::max(par_width, pars_width);
602 updateRowPositions();
607 void LyXText::redoParagraph(ParagraphList::iterator pit)
609 redoParagraphInternal(pit);
610 updateRowPositions();
614 void LyXText::fullRebreak()
616 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
618 selection.cursor = cursor;
622 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
624 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
625 // << " workWidth: " << workWidth() << endl;
626 //BOOST_ASSERT(mi.base.textwidth);
633 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
636 dim.asc = firstRow()->ascent_of_text();
637 dim.des = height - dim.asc;
638 dim.wid = std::max(mi.base.textwidth, int(width));
642 // important for the screen
645 // the cursor set functions have a special mechanism. When they
646 // realize, that you left an empty paragraph, they will delete it.
647 // They also delete the corresponding row
649 // need the selection cursor:
650 void LyXText::setSelection()
652 TextCursor::setSelection();
657 void LyXText::clearSelection()
659 TextCursor::clearSelection();
661 // reset this in the bv_owner!
662 if (bv_owner && bv_owner->text)
663 bv_owner->text->xsel_cache.set(false);
667 void LyXText::cursorHome()
669 setCursor(cursor.par(), cursorRow()->pos());
673 void LyXText::cursorEnd()
675 setCursor(cursor.par(), cursorRow()->end() - 1);
679 void LyXText::cursorTop()
681 setCursor(ownerParagraphs().begin(), 0);
685 void LyXText::cursorBottom()
687 ParagraphList::iterator lastpit =
688 boost::prior(ownerParagraphs().end());
689 setCursor(lastpit, lastpit->size());
693 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
695 // If the mask is completely neutral, tell user
696 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
697 // Could only happen with user style
698 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
702 // Try implicit word selection
703 // If there is a change in the language the implicit word selection
705 LyXCursor resetCursor = cursor;
706 bool implicitSelection = (font.language() == ignore_language
707 && font.number() == LyXFont::IGNORE)
708 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
711 setFont(font, toggleall);
713 // Implicit selections are cleared afterwards
714 //and cursor is set to the original position.
715 if (implicitSelection) {
717 cursor = resetCursor;
718 setCursor(cursor.par(), cursor.pos());
719 selection.cursor = cursor;
724 string LyXText::getStringToIndex()
726 // Try implicit word selection
727 // If there is a change in the language the implicit word selection
729 LyXCursor const reset_cursor = cursor;
730 bool const implicitSelection =
731 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
734 if (!selection.set())
735 bv()->owner()->message(_("Nothing to index!"));
736 else if (selection.start.par() != selection.end.par())
737 bv()->owner()->message(_("Cannot index more than one paragraph!"));
739 idxstring = selectionAsString(*bv()->buffer(), false);
741 // Reset cursors to their original position.
742 cursor = reset_cursor;
743 setCursor(cursor.par(), cursor.pos());
744 selection.cursor = cursor;
746 // Clear the implicit selection.
747 if (implicitSelection)
754 // the DTP switches for paragraphs. LyX will store them in the first
755 // physical paragraph. When a paragraph is broken, the top settings rest,
756 // the bottom settings are given to the new one. So I can make sure,
757 // they do not duplicate themself and you cannnot make dirty things with
760 void LyXText::setParagraph(bool line_top, bool line_bottom,
761 bool pagebreak_top, bool pagebreak_bottom,
762 VSpace const & space_top,
763 VSpace const & space_bottom,
764 Spacing const & spacing,
766 string const & labelwidthstring,
769 LyXCursor tmpcursor = cursor;
770 if (!selection.set()) {
771 selection.start = cursor;
772 selection.end = cursor;
775 // make sure that the depth behind the selection are restored, too
776 ParagraphList::iterator endpit = boost::next(selection.end.par());
777 ParagraphList::iterator undoendpit = endpit;
778 ParagraphList::iterator pars_end = ownerParagraphs().end();
780 if (endpit != pars_end && endpit->getDepth()) {
781 while (endpit != pars_end && endpit->getDepth()) {
785 } else if (endpit != pars_end) {
786 // because of parindents etc.
790 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
791 boost::prior(undoendpit));
794 ParagraphList::iterator tmppit = selection.end.par();
796 while (tmppit != boost::prior(selection.start.par())) {
797 setCursor(tmppit, 0);
799 ParagraphList::iterator pit = cursor.par();
800 ParagraphParameters & params = pit->params();
802 params.lineTop(line_top);
803 params.lineBottom(line_bottom);
804 params.pagebreakTop(pagebreak_top);
805 params.pagebreakBottom(pagebreak_bottom);
806 params.spaceTop(space_top);
807 params.spaceBottom(space_bottom);
808 params.spacing(spacing);
809 // does the layout allow the new alignment?
810 LyXLayout_ptr const & layout = pit->layout();
812 if (align == LYX_ALIGN_LAYOUT)
813 align = layout->align;
814 if (align & layout->alignpossible) {
815 if (align == layout->align)
816 params.align(LYX_ALIGN_LAYOUT);
820 pit->setLabelWidthString(labelwidthstring);
821 params.noindent(noindent);
822 tmppit = boost::prior(pit);
825 redoParagraphs(selection.start.par(), endpit);
828 setCursor(selection.start.par(), selection.start.pos());
829 selection.cursor = cursor;
830 setCursor(selection.end.par(), selection.end.pos());
832 setCursor(tmpcursor.par(), tmpcursor.pos());
834 bv()->updateInset(inset_owner);
840 string expandLabel(LyXTextClass const & textclass,
841 LyXLayout_ptr const & layout, bool appendix)
843 string fmt = appendix ?
844 layout->labelstring_appendix() : layout->labelstring();
846 // handle 'inherited level parts' in 'fmt',
847 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
848 size_t const i = fmt.find('@', 0);
849 if (i != string::npos) {
850 size_t const j = fmt.find('@', i + 1);
851 if (j != string::npos) {
852 string parent(fmt, i + 1, j - i - 1);
853 string label = expandLabel(textclass, textclass[parent], appendix);
854 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
858 return textclass.counters().counterLabel(fmt);
864 // set the counter of a paragraph. This includes the labels
865 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
867 BufferParams const & bufparams = buf.params();
868 LyXTextClass const & textclass = bufparams.getLyXTextClass();
869 LyXLayout_ptr const & layout = pit->layout();
871 if (pit != ownerParagraphs().begin()) {
872 pit->params().appendix(boost::prior(pit)->params().appendix());
873 if (!pit->params().appendix() &&
874 pit->params().startOfAppendix()) {
875 pit->params().appendix(true);
876 textclass.counters().reset();
878 pit->enumdepth = boost::prior(pit)->enumdepth;
879 pit->itemdepth = boost::prior(pit)->itemdepth;
881 pit->params().appendix(pit->params().startOfAppendix());
886 // Maybe we have to increment the enumeration depth.
887 // Bibliographies can't have their depth changed ie. they
888 // are always of depth 0
889 if (pit != ownerParagraphs().begin()
890 && boost::prior(pit)->getDepth() < pit->getDepth()
891 && boost::prior(pit)->layout()->labeltype == LABEL_ENUMERATE
892 && pit->enumdepth < 3
893 && layout->labeltype != LABEL_BIBLIO) {
897 // Maybe we have to increment the enumeration depth.
898 // Bibliographies can't have their depth changed ie. they
899 // are always of depth 0
900 if (pit != ownerParagraphs().begin()
901 && boost::prior(pit)->getDepth() < pit->getDepth()
902 && boost::prior(pit)->layout()->labeltype == LABEL_ITEMIZE
903 && pit->itemdepth < 3
904 && layout->labeltype != LABEL_BIBLIO) {
908 // Maybe we have to decrement the enumeration depth, see note above
909 if (pit != ownerParagraphs().begin()
910 && boost::prior(pit)->getDepth() > pit->getDepth()
911 && layout->labeltype != LABEL_BIBLIO) {
912 pit->enumdepth = depthHook(pit, ownerParagraphs(),
913 pit->getDepth())->enumdepth;
916 // erase what was there before
917 pit->params().labelString(string());
919 if (layout->margintype == MARGIN_MANUAL) {
920 if (pit->params().labelWidthString().empty())
921 pit->setLabelWidthString(layout->labelstring());
923 pit->setLabelWidthString(string());
926 // is it a layout that has an automatic label?
927 if (layout->labeltype == LABEL_COUNTER) {
928 BufferParams const & bufparams = buf.params();
929 LyXTextClass const & textclass = bufparams.getLyXTextClass();
930 textclass.counters().step(layout->counter);
931 string label = expandLabel(textclass, layout, pit->params().appendix());
932 pit->params().labelString(label);
933 textclass.counters().reset("enum");
934 } else if (layout->labeltype == LABEL_ITEMIZE) {
935 // At some point of time we should do something more clever here,
937 // pit->params().labelString(
938 // bufparams.user_defined_bullet(pit->itemdepth).getText());
939 // for now, use a static label
940 pit->params().labelString("*");
941 textclass.counters().reset("enum");
942 } else if (layout->labeltype == LABEL_ENUMERATE) {
944 // Yes I know this is a really, really! bad solution
946 string enumcounter = "enum";
948 switch (pit->enumdepth) {
960 // not a valid enumdepth...
964 textclass.counters().step(enumcounter);
966 pit->params().labelString(textclass.counters().enumLabel(enumcounter));
967 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
968 textclass.counters().step("bibitem");
969 int number = textclass.counters().value("bibitem");
970 if (pit->bibitem()) {
971 pit->bibitem()->setCounter(number);
972 pit->params().labelString(layout->labelstring());
974 // In biblio should't be following counters but...
976 string s = buf.B_(layout->labelstring());
979 if (layout->labeltype == LABEL_SENSITIVE) {
980 ParagraphList::iterator end = ownerParagraphs().end();
981 ParagraphList::iterator tmppit = pit;
984 while (tmppit != end && tmppit->inInset()
985 // the single '=' is intended below
986 && (in = tmppit->inInset()->owner()))
988 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
989 in->lyxCode() == InsetOld::WRAP_CODE) {
993 Paragraph const * owner = &ownerPar(buf, in);
994 tmppit = ownerParagraphs().begin();
995 for ( ; tmppit != end; ++tmppit)
996 if (&*tmppit == owner)
1004 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1005 type = static_cast<InsetFloat*>(in)->params().type;
1006 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1007 type = static_cast<InsetWrap*>(in)->params().type;
1009 BOOST_ASSERT(false);
1011 Floating const & fl = textclass.floats().getType(type);
1013 textclass.counters().step(fl.type());
1015 // Doesn't work... yet.
1016 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1018 // par->SetLayout(0);
1019 // s = layout->labelstring;
1020 s = _("Senseless: ");
1023 pit->params().labelString(s);
1025 // reset the enumeration counter. They are always reset
1026 // when there is any other layout between
1027 // Just fall-through between the cases so that all
1028 // enum counters deeper than enumdepth is also reset.
1029 switch (pit->enumdepth) {
1031 textclass.counters().reset("enumi");
1033 textclass.counters().reset("enumii");
1035 textclass.counters().reset("enumiii");
1037 textclass.counters().reset("enumiv");
1043 // Updates all counters. Paragraphs with changed label string will be rebroken
1044 void LyXText::updateCounters()
1047 bv()->buffer()->params().getLyXTextClass().counters().reset();
1049 ParagraphList::iterator beg = ownerParagraphs().begin();
1050 ParagraphList::iterator end = ownerParagraphs().end();
1051 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1052 string const oldLabel = pit->params().labelString();
1054 size_t maxdepth = 0;
1056 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1058 if (pit->params().depth() > maxdepth)
1059 pit->params().depth(maxdepth);
1061 // setCounter can potentially change the labelString.
1062 setCounter(*bv()->buffer(), pit);
1064 string const & newLabel = pit->params().labelString();
1066 if (oldLabel != newLabel)
1072 void LyXText::insertInset(InsetOld * inset)
1074 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1076 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1078 cursor.par()->insertInset(cursor.pos(), inset);
1079 // Just to rebreak and refresh correctly.
1080 // The character will not be inserted a second time
1081 insertChar(Paragraph::META_INSET);
1082 // If we enter a highly editable inset the cursor should be before
1083 // the inset. After an Undo LyX tries to call inset->edit(...)
1084 // and fails if the cursor is behind the inset and getInset
1085 // does not return the inset!
1086 if (isHighlyEditableInset(inset))
1092 void LyXText::cutSelection(bool doclear, bool realcut)
1094 // Stuff what we got on the clipboard. Even if there is no selection.
1096 // There is a problem with having the stuffing here in that the
1097 // larger the selection the slower LyX will get. This can be
1098 // solved by running the line below only when the selection has
1099 // finished. The solution used currently just works, to make it
1100 // faster we need to be more clever and probably also have more
1101 // calls to stuffClipboard. (Lgb)
1102 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1104 // This doesn't make sense, if there is no selection
1105 if (!selection.set())
1108 // OK, we have a selection. This is always between selection.start
1109 // and selection.end
1111 // make sure that the depth behind the selection are restored, too
1112 ParagraphList::iterator endpit = boost::next(selection.end.par());
1113 ParagraphList::iterator undoendpit = endpit;
1114 ParagraphList::iterator pars_end = ownerParagraphs().end();
1116 if (endpit != pars_end && endpit->getDepth()) {
1117 while (endpit != pars_end && endpit->getDepth()) {
1119 undoendpit = endpit;
1121 } else if (endpit != pars_end) {
1122 // because of parindents etc.
1126 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1127 boost::prior(undoendpit));
1129 endpit = selection.end.par();
1130 int endpos = selection.end.pos();
1132 BufferParams const & bufparams = bv()->buffer()->params();
1133 boost::tie(endpit, endpos) = realcut ?
1134 CutAndPaste::cutSelection(bufparams,
1136 selection.start.par(), endpit,
1137 selection.start.pos(), endpos,
1138 bufparams.textclass,
1140 : CutAndPaste::eraseSelection(bufparams,
1142 selection.start.par(), endpit,
1143 selection.start.pos(), endpos,
1145 // sometimes necessary
1147 selection.start.par()->stripLeadingSpaces();
1149 redoParagraphs(selection.start.par(), boost::next(endpit));
1150 // cutSelection can invalidate the cursor so we need to set
1152 // we prefer the end for when tracking changes
1156 // need a valid cursor. (Lgb)
1159 setCursor(cursor.par(), cursor.pos());
1160 selection.cursor = cursor;
1165 void LyXText::copySelection()
1167 // stuff the selection onto the X clipboard, from an explicit copy request
1168 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1170 // this doesnt make sense, if there is no selection
1171 if (!selection.set())
1174 // ok we have a selection. This is always between selection.start
1175 // and sel_end cursor
1177 // copy behind a space if there is one
1178 while (selection.start.par()->size() > selection.start.pos()
1179 && selection.start.par()->isLineSeparator(selection.start.pos())
1180 && (selection.start.par() != selection.end.par()
1181 || selection.start.pos() < selection.end.pos()))
1182 selection.start.pos(selection.start.pos() + 1);
1184 CutAndPaste::copySelection(selection.start.par(),
1185 selection.end.par(),
1186 selection.start.pos(), selection.end.pos(),
1187 bv()->buffer()->params().textclass);
1191 void LyXText::pasteSelection(size_t sel_index)
1193 // this does not make sense, if there is nothing to paste
1194 if (!CutAndPaste::checkPastePossible())
1197 recordUndo(bv(), Undo::INSERT, cursor.par());
1199 ParagraphList::iterator endpit;
1204 boost::tie(ppp, endpit) =
1205 CutAndPaste::pasteSelection(*bv()->buffer(),
1207 cursor.par(), cursor.pos(),
1208 bv()->buffer()->params().textclass,
1210 bufferErrors(*bv()->buffer(), el);
1211 bv()->showErrorList(_("Paste"));
1213 redoParagraphs(cursor.par(), endpit);
1215 setCursor(cursor.par(), cursor.pos());
1218 selection.cursor = cursor;
1219 setCursor(ppp.first, ppp.second);
1225 void LyXText::setSelectionRange(lyx::pos_type length)
1230 selection.cursor = cursor;
1237 // simple replacing. The font of the first selected character is used
1238 void LyXText::replaceSelectionWithString(string const & str)
1240 recordUndo(bv(), Undo::ATOMIC);
1243 if (!selection.set()) { // create a dummy selection
1244 selection.end = cursor;
1245 selection.start = cursor;
1248 // Get font setting before we cut
1249 pos_type pos = selection.end.pos();
1250 LyXFont const font = selection.start.par()
1251 ->getFontSettings(bv()->buffer()->params(),
1252 selection.start.pos());
1254 // Insert the new string
1255 string::const_iterator cit = str.begin();
1256 string::const_iterator end = str.end();
1257 for (; cit != end; ++cit) {
1258 selection.end.par()->insertChar(pos, (*cit), font);
1262 // Cut the selection
1263 cutSelection(true, false);
1269 // needed to insert the selection
1270 void LyXText::insertStringAsLines(string const & str)
1272 ParagraphList::iterator pit = cursor.par();
1273 pos_type pos = cursor.pos();
1274 ParagraphList::iterator endpit = boost::next(cursor.par());
1276 recordUndo(bv(), Undo::ATOMIC);
1278 // only to be sure, should not be neccessary
1281 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1283 redoParagraphs(cursor.par(), endpit);
1284 setCursor(cursor.par(), cursor.pos());
1285 selection.cursor = cursor;
1286 setCursor(pit, pos);
1291 // turns double-CR to single CR, others where converted into one
1292 // blank. Then InsertStringAsLines is called
1293 void LyXText::insertStringAsParagraphs(string const & str)
1295 string linestr(str);
1296 bool newline_inserted = false;
1297 string::size_type const siz = linestr.length();
1299 for (string::size_type i = 0; i < siz; ++i) {
1300 if (linestr[i] == '\n') {
1301 if (newline_inserted) {
1302 // we know that \r will be ignored by
1303 // InsertStringA. Of course, it is a dirty
1304 // trick, but it works...
1305 linestr[i - 1] = '\r';
1309 newline_inserted = true;
1311 } else if (IsPrintable(linestr[i])) {
1312 newline_inserted = false;
1315 insertStringAsLines(linestr);
1319 bool LyXText::setCursor(ParagraphList::iterator pit,
1321 bool setfont, bool boundary)
1323 LyXCursor old_cursor = cursor;
1324 setCursorIntern(pit, pos, setfont, boundary);
1325 return deleteEmptyParagraphMechanism(old_cursor);
1329 void LyXText::redoCursor()
1331 #warning maybe the same for selections?
1332 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1336 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1337 pos_type pos, bool boundary)
1339 BOOST_ASSERT(pit != ownerParagraphs().end());
1343 cur.boundary(boundary);
1347 // get the cursor y position in text
1349 RowList::iterator row = getRow(pit, pos);
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 = lastPos(*pit, 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);
1382 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1383 pos_type pos, pos_type last, bool boundary) const
1385 pos_type cursor_vpos = 0;
1386 double x = rit->x();
1387 double fill_separator = rit->fill_separator();
1388 double fill_hfill = rit->fill_hfill();
1389 double fill_label_hfill = rit->fill_label_hfill();
1390 pos_type const rit_pos = rit->pos();
1393 cursor_vpos = rit_pos;
1394 else if (pos > last && !boundary)
1395 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1396 ? rit_pos : last + 1;
1397 else if (pos > rit_pos && (pos > last || boundary))
1398 // Place cursor after char at (logical) position pos - 1
1399 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1400 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1402 // Place cursor before char at (logical) position pos
1403 cursor_vpos = (bidi_level(pos) % 2 == 0)
1404 ? log2vis(pos) : log2vis(pos) + 1;
1406 pos_type body_pos = pit->beginningOfBody();
1408 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1411 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1412 pos_type pos = vis2log(vpos);
1413 if (body_pos > 0 && pos == body_pos - 1) {
1414 x += fill_label_hfill +
1415 font_metrics::width(
1416 pit->layout()->labelsep, getLabelFont(pit));
1417 if (pit->isLineSeparator(body_pos - 1))
1418 x -= singleWidth(pit, body_pos - 1);
1421 if (hfillExpansion(*pit, rit, pos)) {
1422 x += singleWidth(pit, pos);
1423 if (pos >= body_pos)
1426 x += fill_label_hfill;
1427 } else if (pit->isSeparator(pos)) {
1428 x += singleWidth(pit, pos);
1429 if (pos >= body_pos)
1430 x += fill_separator;
1432 x += singleWidth(pit, pos);
1438 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1439 pos_type pos, bool setfont, bool boundary)
1441 setCursor(cursor, pit, pos, boundary);
1447 void LyXText::setCurrentFont()
1449 pos_type pos = cursor.pos();
1450 ParagraphList::iterator pit = cursor.par();
1452 if (cursor.boundary() && pos > 0)
1456 if (pos == pit->size())
1458 else // potentional bug... BUG (Lgb)
1459 if (pit->isSeparator(pos)) {
1460 if (pos > cursorRow()->pos() &&
1461 bidi_level(pos) % 2 ==
1462 bidi_level(pos - 1) % 2)
1464 else if (pos + 1 < pit->size())
1469 BufferParams const & bufparams = bv()->buffer()->params();
1470 current_font = pit->getFontSettings(bufparams, pos);
1471 real_current_font = getFont(pit, pos);
1473 if (cursor.pos() == pit->size() &&
1474 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1475 !cursor.boundary()) {
1476 Language const * lang =
1477 pit->getParLanguage(bufparams);
1478 current_font.setLanguage(lang);
1479 current_font.setNumber(LyXFont::OFF);
1480 real_current_font.setLanguage(lang);
1481 real_current_font.setNumber(LyXFont::OFF);
1486 // returns the column near the specified x-coordinate of the row
1487 // x is set to the real beginning of this column
1488 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1489 RowList::iterator rit, int & x, bool & boundary) const
1491 double tmpx = rit->x();
1492 double fill_separator = rit->fill_separator();
1493 double fill_hfill = rit->fill_hfill();
1494 double fill_label_hfill = rit->fill_label_hfill();
1496 pos_type vc = rit->pos();
1497 pos_type last = lastPos(*pit, rit);
1499 LyXLayout_ptr const & layout = pit->layout();
1501 bool left_side = false;
1503 pos_type body_pos = pit->beginningOfBody();
1504 double last_tmpx = tmpx;
1507 (body_pos - 1 > last ||
1508 !pit->isLineSeparator(body_pos - 1)))
1511 // check for empty row
1517 while (vc <= last && tmpx <= x) {
1520 if (body_pos > 0 && c == body_pos - 1) {
1521 tmpx += fill_label_hfill +
1522 font_metrics::width(layout->labelsep, getLabelFont(pit));
1523 if (pit->isLineSeparator(body_pos - 1))
1524 tmpx -= singleWidth(pit, body_pos - 1);
1527 if (hfillExpansion(*pit, rit, c)) {
1528 tmpx += singleWidth(pit, c);
1532 tmpx += fill_label_hfill;
1533 } else if (pit->isSeparator(c)) {
1534 tmpx += singleWidth(pit, c);
1536 tmpx += fill_separator;
1538 tmpx += singleWidth(pit, c);
1543 if ((tmpx + last_tmpx) / 2 > x) {
1548 if (vc > last + 1) // This shouldn't happen.
1552 // This (rtl_support test) is not needed, but gives
1553 // some speedup if rtl_support == false
1554 bool const lastrow = lyxrc.rtl_support
1555 && boost::next(rit) == pit->rows.end();
1557 // If lastrow is false, we don't need to compute
1558 // the value of rtl.
1559 bool const rtl = (lastrow)
1560 ? pit->isRightToLeftPar(bv()->buffer()->params())
1563 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1564 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1566 else if (vc == rit->pos()) {
1568 if (bidi_level(c) % 2 == 1)
1571 c = vis2log(vc - 1);
1572 bool const rtl = (bidi_level(c) % 2 == 1);
1573 if (left_side == rtl) {
1575 boundary = isBoundary(*bv()->buffer(), *pit, c);
1579 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1580 if (bidi_level(last) % 2 == 0)
1581 tmpx -= singleWidth(pit, last);
1583 tmpx += singleWidth(pit, last);
1593 void LyXText::setCursorFromCoordinates(int x, int y)
1595 LyXCursor old_cursor = cursor;
1596 setCursorFromCoordinates(cursor, x, y);
1598 deleteEmptyParagraphMechanism(old_cursor);
1602 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1604 // Get the row first.
1605 ParagraphList::iterator pit;
1606 RowList::iterator rit = getRowNearY(y, pit);
1610 pos_type const column = getColumnNearX(pit, rit, x, bound);
1612 cur.pos(rit->pos() + column);
1614 cur.y(y + rit->baseline());
1616 cur.boundary(bound);
1620 void LyXText::cursorLeft(bool internal)
1622 if (cursor.pos() > 0) {
1623 bool boundary = cursor.boundary();
1624 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1625 if (!internal && !boundary &&
1626 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1627 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1628 } else if (cursor.par() != ownerParagraphs().begin()) {
1629 // steps into the paragraph above
1630 ParagraphList::iterator pit = boost::prior(cursor.par());
1631 setCursor(pit, pit->size());
1636 void LyXText::cursorRight(bool internal)
1638 bool const at_end = (cursor.pos() == cursor.par()->size());
1639 bool const at_newline = !at_end &&
1640 cursor.par()->isNewline(cursor.pos());
1642 if (!internal && cursor.boundary() && !at_newline)
1643 setCursor(cursor.par(), cursor.pos(), true, false);
1645 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1647 isBoundary(*bv()->buffer(), *cursor.par(), cursor.pos()))
1648 setCursor(cursor.par(), cursor.pos(), true, true);
1649 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1650 setCursor(boost::next(cursor.par()), 0);
1654 void LyXText::cursorUp(bool selecting)
1657 int x = cursor.x_fix();
1658 int y = cursor.y() - cursorRow()->baseline() - 1;
1659 setCursorFromCoordinates(x, y);
1661 int topy = bv_owner->top_y();
1662 int y1 = cursor.y() - topy;
1665 InsetOld * inset_hit = checkInsetHit(x, y1);
1666 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1667 inset_hit->localDispatch(
1668 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1672 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1673 cursorRow()->baseline() << endl;
1674 setCursorFromCoordinates(cursor.x_fix(),
1675 cursor.y() - cursorRow()->baseline() - 1);
1680 void LyXText::cursorDown(bool selecting)
1683 int x = cursor.x_fix();
1684 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1685 setCursorFromCoordinates(x, y);
1686 if (!selecting && cursorRow() == cursorIRow()) {
1687 int topy = bv_owner->top_y();
1688 int y1 = cursor.y() - topy;
1691 InsetOld * inset_hit = checkInsetHit(x, y1);
1692 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1693 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1694 inset_hit->localDispatch(cmd);
1698 setCursorFromCoordinates(cursor.x_fix(),
1699 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1704 void LyXText::cursorUpParagraph()
1706 if (cursor.pos() > 0)
1707 setCursor(cursor.par(), 0);
1708 else if (cursor.par() != ownerParagraphs().begin())
1709 setCursor(boost::prior(cursor.par()), 0);
1713 void LyXText::cursorDownParagraph()
1715 ParagraphList::iterator par = cursor.par();
1716 ParagraphList::iterator next_par = boost::next(par);
1718 if (next_par != ownerParagraphs().end())
1719 setCursor(next_par, 0);
1721 setCursor(par, par->size());
1725 // fix the cursor `cur' after a characters has been deleted at `where'
1726 // position. Called by deleteEmptyParagraphMechanism
1727 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1729 // if cursor is not in the paragraph where the delete occured,
1731 if (cur.par() != where.par())
1734 // if cursor position is after the place where the delete occured,
1736 if (cur.pos() > where.pos())
1737 cur.pos(cur.pos()-1);
1739 // check also if we don't want to set the cursor on a spot behind the
1740 // pagragraph because we erased the last character.
1741 if (cur.pos() > cur.par()->size())
1742 cur.pos(cur.par()->size());
1744 // recompute row et al. for this cursor
1745 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1749 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1751 // Would be wrong to delete anything if we have a selection.
1752 if (selection.set())
1755 // We allow all kinds of "mumbo-jumbo" when freespacing.
1756 if (old_cursor.par()->isFreeSpacing())
1759 /* Ok I'll put some comments here about what is missing.
1760 I have fixed BackSpace (and thus Delete) to not delete
1761 double-spaces automagically. I have also changed Cut,
1762 Copy and Paste to hopefully do some sensible things.
1763 There are still some small problems that can lead to
1764 double spaces stored in the document file or space at
1765 the beginning of paragraphs. This happens if you have
1766 the cursor between to spaces and then save. Or if you
1767 cut and paste and the selection have a space at the
1768 beginning and then save right after the paste. I am
1769 sure none of these are very hard to fix, but I will
1770 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1771 that I can get some feedback. (Lgb)
1774 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1775 // delete the LineSeparator.
1778 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1779 // delete the LineSeparator.
1782 // If the pos around the old_cursor were spaces, delete one of them.
1783 if (old_cursor.par() != cursor.par()
1784 || old_cursor.pos() != cursor.pos()) {
1786 // Only if the cursor has really moved
1787 if (old_cursor.pos() > 0
1788 && old_cursor.pos() < old_cursor.par()->size()
1789 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1790 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1791 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1792 redoParagraph(old_cursor.par());
1796 #ifdef WITH_WARNINGS
1797 #warning This will not work anymore when we have multiple views of the same buffer
1798 // In this case, we will have to correct also the cursors held by
1799 // other bufferviews. It will probably be easier to do that in a more
1800 // automated way in LyXCursor code. (JMarc 26/09/2001)
1802 // correct all cursors held by the LyXText
1803 fixCursorAfterDelete(cursor, old_cursor);
1804 fixCursorAfterDelete(selection.cursor, old_cursor);
1805 fixCursorAfterDelete(selection.start, old_cursor);
1806 fixCursorAfterDelete(selection.end, old_cursor);
1811 // don't delete anything if this is the ONLY paragraph!
1812 if (ownerParagraphs().size() == 1)
1815 // Do not delete empty paragraphs with keepempty set.
1816 if (old_cursor.par()->allowEmpty())
1819 // only do our magic if we changed paragraph
1820 if (old_cursor.par() == cursor.par())
1823 // record if we have deleted a paragraph
1824 // we can't possibly have deleted a paragraph before this point
1825 bool deleted = false;
1827 if (old_cursor.par()->empty() ||
1828 (old_cursor.par()->size() == 1 &&
1829 old_cursor.par()->isLineSeparator(0))) {
1830 // ok, we will delete something
1831 LyXCursor tmpcursor;
1835 bool selection_position_was_oldcursor_position = (
1836 selection.cursor.par() == old_cursor.par()
1837 && selection.cursor.pos() == old_cursor.pos());
1840 cursor = old_cursor; // that undo can restore the right cursor position
1842 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1843 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1846 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1850 ownerParagraphs().erase(old_cursor.par());
1854 setCursorIntern(cursor.par(), cursor.pos());
1856 if (selection_position_was_oldcursor_position) {
1857 // correct selection
1858 selection.cursor = cursor;
1862 if (old_cursor.par()->stripLeadingSpaces()) {
1863 redoParagraph(old_cursor.par());
1865 setCursorIntern(cursor.par(), cursor.pos());
1866 selection.cursor = cursor;
1873 ParagraphList & LyXText::ownerParagraphs() const
1879 bool LyXText::isInInset() const
1881 // Sub-level has non-null bv owner and non-null inset owner.
1882 return inset_owner != 0;
1886 int defaultRowHeight()
1888 LyXFont const font(LyXFont::ALL_SANE);
1889 return int(font_metrics::maxAscent(font)
1890 + font_metrics::maxDescent(font) * 1.5);