1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
31 #include "FloatList.h"
33 #include "ParagraphParameters.h"
35 #include "lyxrow_funcs.h"
36 #include "metricsinfo.h"
37 #include "paragraph_funcs.h"
39 #include "insets/insetbibitem.h"
40 #include "insets/insetenv.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
48 #include <boost/tuple/tuple.hpp>
52 using namespace lyx::support;
62 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
63 ParagraphList & paragraphs)
64 : height(0), width(0), anchor_y_(0),
65 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
66 in_inset_(ininset), paragraphs_(paragraphs)
71 void LyXText::init(BufferView * bview)
75 ParagraphList::iterator const beg = ownerParagraphs().begin();
76 ParagraphList::iterator const end = ownerParagraphs().end();
77 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
85 current_font = getFont(beg, 0);
87 redoParagraphs(beg, end);
88 setCursorIntern(beg, 0);
89 selection.cursor = cursor;
95 // Gets the fully instantiated font at a given position in a paragraph
96 // Basically the same routine as Paragraph::getFont() in paragraph.C.
97 // The difference is that this one is used for displaying, and thus we
98 // are allowed to make cosmetic improvements. For instance make footnotes
100 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
104 LyXLayout_ptr const & layout = pit->layout();
106 BufferParams const & params = bv()->buffer()->params;
108 // We specialize the 95% common case:
109 if (!pit->getDepth()) {
110 if (layout->labeltype == LABEL_MANUAL
111 && pos < pit->beginningOfBody()) {
113 LyXFont f = pit->getFontSettings(params, pos);
115 pit->inInset()->getDrawFont(f);
116 return f.realize(layout->reslabelfont);
118 LyXFont f = pit->getFontSettings(params, pos);
120 pit->inInset()->getDrawFont(f);
121 return f.realize(layout->resfont);
125 // The uncommon case need not be optimized as much
129 if (pos < pit->beginningOfBody()) {
131 layoutfont = layout->labelfont;
134 layoutfont = layout->font;
137 LyXFont tmpfont = pit->getFontSettings(params, pos);
138 tmpfont.realize(layoutfont);
141 pit->inInset()->getDrawFont(tmpfont);
143 // Realize with the fonts of lesser depth.
144 tmpfont.realize(outerFont(pit, ownerParagraphs()));
145 tmpfont.realize(defaultfont_);
151 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
153 LyXLayout_ptr const & layout = pit->layout();
155 if (!pit->getDepth())
156 return layout->resfont;
158 LyXFont font = layout->font;
159 // Realize with the fonts of lesser depth.
160 font.realize(outerFont(pit, ownerParagraphs()));
161 font.realize(defaultfont_);
167 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
169 LyXLayout_ptr const & layout = pit->layout();
171 if (!pit->getDepth())
172 return layout->reslabelfont;
174 LyXFont font = layout->labelfont;
175 // Realize with the fonts of lesser depth.
176 font.realize(outerFont(pit, ownerParagraphs()));
177 font.realize(defaultfont_);
183 void LyXText::setCharFont(ParagraphList::iterator pit,
184 pos_type pos, LyXFont const & fnt,
187 BufferParams const & params = bv()->buffer()->params;
188 LyXFont font = getFont(pit, pos);
189 font.update(fnt, params.language, toggleall);
190 // Let the insets convert their font
191 if (pit->isInset(pos)) {
192 InsetOld * inset = pit->getInset(pos);
193 if (isEditableInset(inset)) {
194 static_cast<UpdatableInset *>(inset)
195 ->setFont(bv(), fnt, toggleall, true);
199 // Plug through to version below:
200 setCharFont(pit, pos, font);
204 void LyXText::setCharFont(
205 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
208 LyXLayout_ptr const & layout = pit->layout();
210 // Get concrete layout font to reduce against
213 if (pos < pit->beginningOfBody())
214 layoutfont = layout->labelfont;
216 layoutfont = layout->font;
218 // Realize against environment font information
219 if (pit->getDepth()) {
220 ParagraphList::iterator tp = pit;
221 while (!layoutfont.resolved() &&
222 tp != ownerParagraphs().end() &&
224 tp = outerHook(tp, ownerParagraphs());
225 if (tp != ownerParagraphs().end())
226 layoutfont.realize(tp->layout()->font);
230 layoutfont.realize(defaultfont_);
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 pit->setFont(pos, font);
239 InsetOld * LyXText::getInset() const
241 ParagraphList::iterator pit = cursor.par();
242 pos_type const pos = cursor.pos();
244 if (pos < pit->size() && pit->isInset(pos)) {
245 return pit->getInset(pos);
251 void LyXText::toggleInset()
253 InsetOld * inset = getInset();
254 // is there an editable inset at cursor position?
255 if (!isEditableInset(inset)) {
256 // No, try to see if we are inside a collapsable inset
257 if (inset_owner && inset_owner->owner()
258 && inset_owner->owner()->isOpen()) {
259 bv()->unlockInset(inset_owner->owner());
260 inset_owner->owner()->close(bv());
261 bv()->getLyXText()->cursorRight(bv());
265 //bv()->owner()->message(inset->editMessage());
267 // do we want to keep this?? (JMarc)
268 if (!isHighlyEditableInset(inset))
269 recordUndo(bv(), Undo::ATOMIC);
280 /* used in setlayout */
281 // Asger is not sure we want to do this...
282 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
285 LyXLayout_ptr const & layout = par.layout();
286 pos_type const psize = par.size();
289 for (pos_type pos = 0; pos < psize; ++pos) {
290 if (pos < par.beginningOfBody())
291 layoutfont = layout->labelfont;
293 layoutfont = layout->font;
295 LyXFont tmpfont = par.getFontSettings(params, pos);
296 tmpfont.reduce(layoutfont);
297 par.setFont(pos, tmpfont);
302 ParagraphList::iterator
303 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
304 LyXCursor & send_cur,
305 string const & layout)
307 ParagraphList::iterator endpit = boost::next(send_cur.par());
308 ParagraphList::iterator undoendpit = endpit;
309 ParagraphList::iterator pars_end = ownerParagraphs().end();
311 if (endpit != pars_end && endpit->getDepth()) {
312 while (endpit != pars_end && endpit->getDepth()) {
316 } else if (endpit != pars_end) {
317 // because of parindents etc.
321 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
323 // ok we have a selection. This is always between sstart_cur
324 // and sel_end cursor
326 ParagraphList::iterator pit = sstart_cur.par();
327 ParagraphList::iterator epit = boost::next(send_cur.par());
329 LyXLayout_ptr const & lyxlayout =
330 bv()->buffer()->params.getLyXTextClass()[layout];
333 pit->applyLayout(lyxlayout);
334 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
335 pit->params().spaceTop(lyxlayout->fill_top ?
336 VSpace(VSpace::VFILL)
337 : VSpace(VSpace::NONE));
338 pit->params().spaceBottom(lyxlayout->fill_bottom ?
339 VSpace(VSpace::VFILL)
340 : VSpace(VSpace::NONE));
341 if (lyxlayout->margintype == MARGIN_MANUAL)
342 pit->setLabelWidthString(lyxlayout->labelstring());
345 } while (pit != epit);
351 // set layout over selection and make a total rebreak of those paragraphs
352 void LyXText::setLayout(string const & layout)
354 LyXCursor tmpcursor = cursor; // store the current cursor
356 // if there is no selection just set the layout
357 // of the current paragraph
358 if (!selection.set()) {
359 selection.start = cursor; // dummy selection
360 selection.end = cursor;
363 // special handling of new environment insets
364 BufferParams const & params = bv()->buffer()->params;
365 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
366 if (lyxlayout->is_environment) {
367 // move everything in a new environment inset
368 lyxerr << "setting layout " << layout << endl;
369 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
370 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
371 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
372 InsetOld * inset = new InsetEnvironment(params, layout);
373 if (bv()->insertInset(inset)) {
375 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
382 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
383 selection.end, layout);
384 redoParagraphs(selection.start.par(), endpit);
386 // we have to reset the selection, because the
387 // geometry could have changed
388 setCursor(selection.start.par(), selection.start.pos(), false);
389 selection.cursor = cursor;
390 setCursor(selection.end.par(), selection.end.pos(), false);
394 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
398 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
400 ParagraphList::iterator pit = cursor.par();
401 ParagraphList::iterator end = cursor.par();
402 ParagraphList::iterator start = pit;
404 if (selection.set()) {
405 pit = selection.start.par();
406 end = selection.end.par();
410 ParagraphList::iterator pastend = boost::next(end);
413 recordUndo(bv(), Undo::ATOMIC, start, end);
415 bool changed = false;
417 int prev_after_depth = 0;
418 #warning parlist ... could be nicer ?
419 if (start != ownerParagraphs().begin()) {
420 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
424 int const depth = pit->params().depth();
425 if (type == bv_funcs::INC_DEPTH) {
426 if (depth < prev_after_depth
427 && pit->layout()->labeltype != LABEL_BIBLIO) {
430 pit->params().depth(depth + 1);
435 pit->params().depth(depth - 1);
438 prev_after_depth = pit->getMaxDepthAfter();
450 redoParagraphs(start, pastend);
452 // We need to actually move the text->cursor. I don't
453 // understand why ...
454 LyXCursor tmpcursor = cursor;
456 // we have to reset the visual selection because the
457 // geometry could have changed
458 if (selection.set()) {
459 setCursor(selection.start.par(), selection.start.pos());
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos());
464 // this handles the counter labels, and also fixes up
465 // depth values for follow-on (child) paragraphs
469 setCursor(tmpcursor.par(), tmpcursor.pos());
475 // set font over selection and make a total rebreak of those paragraphs
476 void LyXText::setFont(LyXFont const & font, bool toggleall)
478 // if there is no selection just set the current_font
479 if (!selection.set()) {
480 // Determine basis font
482 if (cursor.pos() < cursor.par()->beginningOfBody()) {
483 layoutfont = getLabelFont(cursor.par());
485 layoutfont = getLayoutFont(cursor.par());
487 // Update current font
488 real_current_font.update(font,
489 bv()->buffer()->params.language,
492 // Reduce to implicit settings
493 current_font = real_current_font;
494 current_font.reduce(layoutfont);
495 // And resolve it completely
496 real_current_font.realize(layoutfont);
501 LyXCursor tmpcursor = cursor; // store the current cursor
503 // ok we have a selection. This is always between sel_start_cursor
504 // and sel_end cursor
506 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
508 cursor = selection.start;
509 while (cursor.par() != selection.end.par() ||
510 cursor.pos() < selection.end.pos())
512 if (cursor.pos() < cursor.par()->size()) {
513 // an open footnote should behave like a closed one
514 setCharFont(cursor.par(), cursor.pos(),
516 cursor.pos(cursor.pos() + 1);
519 cursor.par(boost::next(cursor.par()));
524 redoParagraph(selection.start.par());
526 // we have to reset the selection, because the
527 // geometry could have changed, but we keep
528 // it for user convenience
529 setCursor(selection.start.par(), selection.start.pos());
530 selection.cursor = cursor;
531 setCursor(selection.end.par(), selection.end.pos());
533 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
534 tmpcursor.boundary());
538 // rebreaks all paragraphs between the specified pars
539 // This function is needed after SetLayout and SetFont etc.
540 void LyXText::redoParagraphs(ParagraphList::iterator start,
541 ParagraphList::iterator end)
543 for ( ; start != end; ++start)
544 redoParagraph(start);
548 void LyXText::redoParagraph(ParagraphList::iterator pit)
550 RowList::iterator rit = pit->rows.begin();
551 RowList::iterator end = pit->rows.end();
553 // remove rows of paragraph, keep track of height changes
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
558 // rebreak the paragraph
559 for (pos_type z = 0; z < pit->size() + 1; ) {
561 z = rowBreakPoint(pit, row) + 1;
563 pit->rows.push_back(row);
566 // set height and fill of rows
567 for (rit = pit->rows.begin(); rit != end; ++rit) {
568 rit->fill(fill(pit, rit, workWidth()));
569 prepareToPrint(pit, rit);
570 setHeightOfRow(pit, rit);
573 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
577 void LyXText::fullRebreak()
579 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
581 selection.cursor = cursor;
585 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
587 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
588 // << " workWidth: " << workWidth() << endl;
589 //Assert(mi.base.textwidth);
597 ParagraphList::iterator pit = ownerParagraphs().begin();
598 ParagraphList::iterator end = ownerParagraphs().end();
600 for (; pit != end; ++pit) {
603 InsetList::iterator ii = pit->insetlist.begin();
604 InsetList::iterator iend = pit->insetlist.end();
605 for (; ii != iend; ++ii) {
608 #warning FIXME: pos != 0
609 m.base.font = getFont(pit, 0);
610 ii->inset->metrics(m, dim);
617 dim.asc = firstRow()->ascent_of_text();
618 dim.des = height - dim.asc;
619 dim.wid = std::max(mi.base.textwidth, int(width));
623 // important for the screen
626 // the cursor set functions have a special mechanism. When they
627 // realize, that you left an empty paragraph, they will delete it.
628 // They also delete the corresponding row
630 // need the selection cursor:
631 void LyXText::setSelection()
633 TextCursor::setSelection();
638 void LyXText::clearSelection()
640 TextCursor::clearSelection();
642 // reset this in the bv_owner!
643 if (bv_owner && bv_owner->text)
644 bv_owner->text->xsel_cache.set(false);
648 void LyXText::cursorHome()
650 setCursor(cursor.par(), cursorRow()->pos());
654 void LyXText::cursorEnd()
656 if (cursor.par()->empty())
659 RowList::iterator rit = cursorRow();
660 RowList::iterator next_rit = boost::next(rit);
661 RowList::iterator end = boost::next(rit);
662 ParagraphList::iterator pit = cursor.par();
663 pos_type last_pos = lastPos(*pit, rit);
665 if (next_rit == end) {
669 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
674 setCursor(pit, last_pos);
678 void LyXText::cursorTop()
680 setCursor(ownerParagraphs().begin(), 0);
684 void LyXText::cursorBottom()
686 ParagraphList::iterator lastpit =
687 boost::prior(ownerParagraphs().end());
688 setCursor(lastpit, lastpit->size());
692 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
694 // If the mask is completely neutral, tell user
695 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
696 // Could only happen with user style
697 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
701 // Try implicit word selection
702 // If there is a change in the language the implicit word selection
704 LyXCursor resetCursor = cursor;
705 bool implicitSelection = (font.language() == ignore_language
706 && font.number() == LyXFont::IGNORE)
707 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
710 setFont(font, toggleall);
712 // Implicit selections are cleared afterwards
713 //and cursor is set to the original position.
714 if (implicitSelection) {
716 cursor = resetCursor;
717 setCursor(cursor.par(), cursor.pos());
718 selection.cursor = cursor;
723 string LyXText::getStringToIndex()
725 // Try implicit word selection
726 // If there is a change in the language the implicit word selection
728 LyXCursor const reset_cursor = cursor;
729 bool const implicitSelection =
730 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
733 if (!selection.set())
734 bv()->owner()->message(_("Nothing to index!"));
735 else if (selection.start.par() != selection.end.par())
736 bv()->owner()->message(_("Cannot index more than one paragraph!"));
738 idxstring = selectionAsString(bv()->buffer(), false);
740 // Reset cursors to their original position.
741 cursor = reset_cursor;
742 setCursor(cursor.par(), cursor.pos());
743 selection.cursor = cursor;
745 // Clear the implicit selection.
746 if (implicitSelection)
753 // the DTP switches for paragraphs. LyX will store them in the first
754 // physical paragraph. When a paragraph is broken, the top settings rest,
755 // the bottom settings are given to the new one. So I can make sure,
756 // they do not duplicate themself and you cannnot make dirty things with
759 void LyXText::setParagraph(bool line_top, bool line_bottom,
760 bool pagebreak_top, bool pagebreak_bottom,
761 VSpace const & space_top,
762 VSpace const & space_bottom,
763 Spacing const & spacing,
765 string const & labelwidthstring,
768 LyXCursor tmpcursor = cursor;
769 if (!selection.set()) {
770 selection.start = cursor;
771 selection.end = cursor;
774 // make sure that the depth behind the selection are restored, too
775 ParagraphList::iterator endpit = boost::next(selection.end.par());
776 ParagraphList::iterator undoendpit = endpit;
777 ParagraphList::iterator pars_end = ownerParagraphs().end();
779 if (endpit != pars_end && endpit->getDepth()) {
780 while (endpit != pars_end && endpit->getDepth()) {
784 } else if (endpit != pars_end) {
785 // because of parindents etc.
789 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
790 boost::prior(undoendpit));
793 ParagraphList::iterator tmppit = selection.end.par();
795 while (tmppit != boost::prior(selection.start.par())) {
796 setCursor(tmppit, 0);
798 ParagraphList::iterator pit = cursor.par();
799 ParagraphParameters & params = pit->params();
801 params.lineTop(line_top);
802 params.lineBottom(line_bottom);
803 params.pagebreakTop(pagebreak_top);
804 params.pagebreakBottom(pagebreak_bottom);
805 params.spaceTop(space_top);
806 params.spaceBottom(space_bottom);
807 params.spacing(spacing);
808 // does the layout allow the new alignment?
809 LyXLayout_ptr const & layout = pit->layout();
811 if (align == LYX_ALIGN_LAYOUT)
812 align = layout->align;
813 if (align & layout->alignpossible) {
814 if (align == layout->align)
815 params.align(LYX_ALIGN_LAYOUT);
819 pit->setLabelWidthString(labelwidthstring);
820 params.noindent(noindent);
821 tmppit = boost::prior(pit);
824 redoParagraphs(selection.start.par(), endpit);
827 setCursor(selection.start.par(), selection.start.pos());
828 selection.cursor = cursor;
829 setCursor(selection.end.par(), selection.end.pos());
831 setCursor(tmpcursor.par(), tmpcursor.pos());
837 // set the counter of a paragraph. This includes the labels
838 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
840 LyXTextClass const & textclass = buf->params.getLyXTextClass();
841 LyXLayout_ptr const & layout = pit->layout();
843 if (pit != ownerParagraphs().begin()) {
845 pit->params().appendix(boost::prior(pit)->params().appendix());
846 if (!pit->params().appendix() &&
847 pit->params().startOfAppendix()) {
848 pit->params().appendix(true);
849 textclass.counters().reset();
851 pit->enumdepth = boost::prior(pit)->enumdepth;
852 pit->itemdepth = boost::prior(pit)->itemdepth;
854 pit->params().appendix(pit->params().startOfAppendix());
859 // Maybe we have to increment the enumeration depth.
860 // BUT, enumeration in a footnote is considered in isolation from its
861 // surrounding paragraph so don't increment if this is the
862 // first line of the footnote
863 // AND, bibliographies can't have their depth changed ie. they
864 // are always of depth 0
865 if (pit != ownerParagraphs().begin()
866 && boost::prior(pit)->getDepth() < pit->getDepth()
867 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
868 && pit->enumdepth < 3
869 && layout->labeltype != LABEL_BIBLIO) {
873 // Maybe we have to decrement the enumeration depth, see note above
874 if (pit != ownerParagraphs().begin()
875 && boost::prior(pit)->getDepth() > pit->getDepth()
876 && layout->labeltype != LABEL_BIBLIO) {
877 pit->enumdepth = depthHook(pit, ownerParagraphs(),
878 pit->getDepth())->enumdepth;
881 if (!pit->params().labelString().empty()) {
882 pit->params().labelString(string());
885 if (layout->margintype == MARGIN_MANUAL) {
886 if (pit->params().labelWidthString().empty())
887 pit->setLabelWidthString(layout->labelstring());
889 pit->setLabelWidthString(string());
892 // is it a layout that has an automatic label?
893 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
894 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
898 if (i >= 0 && i <= buf->params.secnumdepth) {
902 textclass.counters().step(layout->latexname());
904 // Is there a label? Useful for Chapter layout
905 if (!pit->params().appendix()) {
906 s << buf->B_(layout->labelstring());
908 s << buf->B_(layout->labelstring_appendix());
911 // Use of an integer is here less than elegant. For now.
912 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
913 if (!pit->params().appendix()) {
914 numbertype = "sectioning";
916 numbertype = "appendix";
917 if (pit->isRightToLeftPar(buf->params))
924 << textclass.counters()
925 .numberLabel(layout->latexname(),
926 numbertype, langtype, head);
928 pit->params().labelString(STRCONV(s.str()));
930 // reset enum counters
931 textclass.counters().reset("enum");
932 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
933 textclass.counters().reset("enum");
934 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
936 // Yes I know this is a really, really! bad solution
938 string enumcounter("enum");
940 switch (pit->enumdepth) {
952 // not a valid enumdepth...
956 textclass.counters().step(enumcounter);
958 s << textclass.counters()
959 .numberLabel(enumcounter, "enumeration");
960 pit->params().labelString(STRCONV(s.str()));
962 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
963 textclass.counters().step("bibitem");
964 int number = textclass.counters().value("bibitem");
965 if (pit->bibitem()) {
966 pit->bibitem()->setCounter(number);
967 pit->params().labelString(layout->labelstring());
969 // In biblio should't be following counters but...
971 string s = buf->B_(layout->labelstring());
974 if (layout->labeltype == LABEL_SENSITIVE) {
975 ParagraphList::iterator end = ownerParagraphs().end();
976 ParagraphList::iterator tmppit = pit;
979 while (tmppit != end && tmppit->inInset()
980 // the single '=' is intended below
981 && (in = tmppit->inInset()->owner()))
983 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
984 in->lyxCode() == InsetOld::WRAP_CODE) {
988 tmppit = ownerParagraphs().begin();
989 for ( ; tmppit != end; ++tmppit)
990 if (&*tmppit == in->parOwner())
998 if (in->lyxCode() == InsetOld::FLOAT_CODE)
999 type = static_cast<InsetFloat*>(in)->params().type;
1000 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1001 type = static_cast<InsetWrap*>(in)->params().type;
1005 Floating const & fl = textclass.floats().getType(type);
1007 textclass.counters().step(fl.type());
1009 // Doesn't work... yet.
1010 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1012 // par->SetLayout(0);
1013 // s = layout->labelstring;
1014 s = _("Senseless: ");
1017 pit->params().labelString(s);
1019 // reset the enumeration counter. They are always reset
1020 // when there is any other layout between
1021 // Just fall-through between the cases so that all
1022 // enum counters deeper than enumdepth is also reset.
1023 switch (pit->enumdepth) {
1025 textclass.counters().reset("enumi");
1027 textclass.counters().reset("enumii");
1029 textclass.counters().reset("enumiii");
1031 textclass.counters().reset("enumiv");
1037 // Updates all counters. Paragraphs with changed label string will be rebroken
1038 void LyXText::updateCounters()
1041 bv()->buffer()->params.getLyXTextClass().counters().reset();
1043 ParagraphList::iterator beg = ownerParagraphs().begin();
1044 ParagraphList::iterator end = ownerParagraphs().end();
1045 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1046 string const oldLabel = pit->params().labelString();
1048 size_t maxdepth = 0;
1050 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1052 if (pit->params().depth() > maxdepth)
1053 pit->params().depth(maxdepth);
1055 // setCounter can potentially change the labelString.
1056 setCounter(bv()->buffer(), pit);
1058 string const & newLabel = pit->params().labelString();
1060 if (oldLabel != newLabel)
1066 void LyXText::insertInset(InsetOld * inset)
1068 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1070 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1072 cursor.par()->insertInset(cursor.pos(), inset);
1073 // Just to rebreak and refresh correctly.
1074 // The character will not be inserted a second time
1075 insertChar(Paragraph::META_INSET);
1076 // If we enter a highly editable inset the cursor should be before
1077 // the inset. After an Undo LyX tries to call inset->edit(...)
1078 // and fails if the cursor is behind the inset and getInset
1079 // does not return the inset!
1080 if (isHighlyEditableInset(inset))
1086 void LyXText::cutSelection(bool doclear, bool realcut)
1088 // Stuff what we got on the clipboard. Even if there is no selection.
1090 // There is a problem with having the stuffing here in that the
1091 // larger the selection the slower LyX will get. This can be
1092 // solved by running the line below only when the selection has
1093 // finished. The solution used currently just works, to make it
1094 // faster we need to be more clever and probably also have more
1095 // calls to stuffClipboard. (Lgb)
1096 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1098 // This doesn't make sense, if there is no selection
1099 if (!selection.set())
1102 // OK, we have a selection. This is always between selection.start
1103 // and selection.end
1105 // make sure that the depth behind the selection are restored, too
1106 ParagraphList::iterator endpit = boost::next(selection.end.par());
1107 ParagraphList::iterator undoendpit = endpit;
1108 ParagraphList::iterator pars_end = ownerParagraphs().end();
1110 if (endpit != pars_end && endpit->getDepth()) {
1111 while (endpit != pars_end && endpit->getDepth()) {
1113 undoendpit = endpit;
1115 } else if (endpit != pars_end) {
1116 // because of parindents etc.
1120 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1121 boost::prior(undoendpit));
1123 endpit = selection.end.par();
1124 int endpos = selection.end.pos();
1126 boost::tie(endpit, endpos) = realcut ?
1127 CutAndPaste::cutSelection(bv()->buffer()->params,
1129 selection.start.par(), endpit,
1130 selection.start.pos(), endpos,
1131 bv()->buffer()->params.textclass,
1133 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1135 selection.start.par(), endpit,
1136 selection.start.pos(), endpos,
1138 // sometimes necessary
1140 selection.start.par()->stripLeadingSpaces();
1142 redoParagraphs(selection.start.par(), boost::next(endpit));
1143 // cutSelection can invalidate the cursor so we need to set
1145 // we prefer the end for when tracking changes
1149 // need a valid cursor. (Lgb)
1152 setCursor(cursor.par(), cursor.pos());
1153 selection.cursor = cursor;
1158 void LyXText::copySelection()
1160 // stuff the selection onto the X clipboard, from an explicit copy request
1161 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1163 // this doesnt make sense, if there is no selection
1164 if (!selection.set())
1167 // ok we have a selection. This is always between selection.start
1168 // and sel_end cursor
1170 // copy behind a space if there is one
1171 while (selection.start.par()->size() > selection.start.pos()
1172 && selection.start.par()->isLineSeparator(selection.start.pos())
1173 && (selection.start.par() != selection.end.par()
1174 || selection.start.pos() < selection.end.pos()))
1175 selection.start.pos(selection.start.pos() + 1);
1177 CutAndPaste::copySelection(selection.start.par(),
1178 selection.end.par(),
1179 selection.start.pos(), selection.end.pos(),
1180 bv()->buffer()->params.textclass);
1184 void LyXText::pasteSelection(size_t sel_index)
1186 // this does not make sense, if there is nothing to paste
1187 if (!CutAndPaste::checkPastePossible())
1190 recordUndo(bv(), Undo::INSERT, cursor.par());
1192 ParagraphList::iterator endpit;
1197 boost::tie(ppp, endpit) =
1198 CutAndPaste::pasteSelection(*bv()->buffer(),
1200 cursor.par(), cursor.pos(),
1201 bv()->buffer()->params.textclass,
1203 bufferErrors(*bv()->buffer(), el);
1204 bv()->showErrorList(_("Paste"));
1206 redoParagraphs(cursor.par(), endpit);
1208 setCursor(cursor.par(), cursor.pos());
1211 selection.cursor = cursor;
1212 setCursor(ppp.first, ppp.second);
1218 void LyXText::setSelectionRange(lyx::pos_type length)
1223 selection.cursor = cursor;
1230 // simple replacing. The font of the first selected character is used
1231 void LyXText::replaceSelectionWithString(string const & str)
1233 recordUndo(bv(), Undo::ATOMIC);
1236 if (!selection.set()) { // create a dummy selection
1237 selection.end = cursor;
1238 selection.start = cursor;
1241 // Get font setting before we cut
1242 pos_type pos = selection.end.pos();
1243 LyXFont const font = selection.start.par()
1244 ->getFontSettings(bv()->buffer()->params,
1245 selection.start.pos());
1247 // Insert the new string
1248 string::const_iterator cit = str.begin();
1249 string::const_iterator end = str.end();
1250 for (; cit != end; ++cit) {
1251 selection.end.par()->insertChar(pos, (*cit), font);
1255 // Cut the selection
1256 cutSelection(true, false);
1262 // needed to insert the selection
1263 void LyXText::insertStringAsLines(string const & str)
1265 ParagraphList::iterator pit = cursor.par();
1266 pos_type pos = cursor.pos();
1267 ParagraphList::iterator endpit = boost::next(cursor.par());
1269 recordUndo(bv(), Undo::ATOMIC);
1271 // only to be sure, should not be neccessary
1274 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1276 redoParagraphs(cursor.par(), endpit);
1277 setCursor(cursor.par(), cursor.pos());
1278 selection.cursor = cursor;
1279 setCursor(pit, pos);
1284 // turns double-CR to single CR, others where converted into one
1285 // blank. Then InsertStringAsLines is called
1286 void LyXText::insertStringAsParagraphs(string const & str)
1288 string linestr(str);
1289 bool newline_inserted = false;
1290 string::size_type const siz = linestr.length();
1292 for (string::size_type i = 0; i < siz; ++i) {
1293 if (linestr[i] == '\n') {
1294 if (newline_inserted) {
1295 // we know that \r will be ignored by
1296 // InsertStringA. Of course, it is a dirty
1297 // trick, but it works...
1298 linestr[i - 1] = '\r';
1302 newline_inserted = true;
1304 } else if (IsPrintable(linestr[i])) {
1305 newline_inserted = false;
1308 insertStringAsLines(linestr);
1312 bool LyXText::setCursor(ParagraphList::iterator pit,
1314 bool setfont, bool boundary)
1316 LyXCursor old_cursor = cursor;
1317 setCursorIntern(pit, pos, setfont, boundary);
1318 return deleteEmptyParagraphMechanism(old_cursor);
1322 void LyXText::redoCursor()
1324 #warning maybe the same for selections?
1325 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1329 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1330 pos_type pos, bool boundary)
1332 Assert(pit != ownerParagraphs().end());
1336 cur.boundary(boundary);
1340 // get the cursor y position in text
1342 RowList::iterator row = getRow(pit, pos, y);
1343 RowList::iterator old_row = row;
1344 // if we are before the first char of this row and are still in the
1345 // same paragraph and there is a previous row then put the cursor on
1346 // the end of the previous row
1347 cur.iy(y + row->baseline());
1348 if (row != pit->rows.begin()
1350 && pos < pit->size()
1351 && pit->getChar(pos) == Paragraph::META_INSET) {
1352 InsetOld * ins = pit->getInset(pos);
1353 if (ins && (ins->needFullRow() || ins->display())) {
1359 // y is now the beginning of the cursor row
1360 y += row->baseline();
1361 // y is now the cursor baseline
1364 pos_type last = lastPrintablePos(*pit, old_row);
1366 // None of these should happen, but we're scaredy-cats
1367 if (pos > pit->size()) {
1368 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1371 } else if (pos > last + 1) {
1372 lyxerr << "dont like 2 please report" << endl;
1373 // This shouldn't happen.
1376 } else if (pos < row->pos()) {
1377 lyxerr << "dont like 3 please report" << endl;
1382 // now get the cursors x position
1383 float x = getCursorX(pit, row, pos, last, boundary);
1386 if (old_row != row) {
1387 x = getCursorX(pit, old_row, pos, last, boundary);
1391 /* We take out this for the time being because 1) the redraw code is not
1392 prepared to this yet and 2) because some good policy has yet to be decided
1393 while editting: for instance how to act on rows being created/deleted
1397 //if the cursor is in a visible row, anchor to it
1399 if (topy < y && y < topy + bv()->workHeight())
1405 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1406 pos_type pos, pos_type last, bool boundary) const
1408 pos_type cursor_vpos = 0;
1409 double x = rit->x();
1410 double fill_separator = rit->fill_separator();
1411 double fill_hfill = rit->fill_hfill();
1412 double fill_label_hfill = rit->fill_label_hfill();
1413 pos_type const rit_pos = rit->pos();
1416 cursor_vpos = rit_pos;
1417 else if (pos > last && !boundary)
1418 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1419 ? rit_pos : last + 1;
1420 else if (pos > rit_pos && (pos > last || boundary))
1421 // Place cursor after char at (logical) position pos - 1
1422 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1423 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1425 // Place cursor before char at (logical) position pos
1426 cursor_vpos = (bidi_level(pos) % 2 == 0)
1427 ? log2vis(pos) : log2vis(pos) + 1;
1429 pos_type body_pos = pit->beginningOfBody();
1431 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1434 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1435 pos_type pos = vis2log(vpos);
1436 if (body_pos > 0 && pos == body_pos - 1) {
1437 x += fill_label_hfill +
1438 font_metrics::width(
1439 pit->layout()->labelsep, getLabelFont(pit));
1440 if (pit->isLineSeparator(body_pos - 1))
1441 x -= singleWidth(pit, body_pos - 1);
1444 if (hfillExpansion(*pit, rit, pos)) {
1445 x += singleWidth(pit, pos);
1446 if (pos >= body_pos)
1449 x += fill_label_hfill;
1450 } else if (pit->isSeparator(pos)) {
1451 x += singleWidth(pit, pos);
1452 if (pos >= body_pos)
1453 x += fill_separator;
1455 x += singleWidth(pit, pos);
1461 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1462 pos_type pos, bool setfont, bool boundary)
1464 setCursor(cursor, pit, pos, boundary);
1470 void LyXText::setCurrentFont()
1472 pos_type pos = cursor.pos();
1473 ParagraphList::iterator pit = cursor.par();
1475 if (cursor.boundary() && pos > 0)
1479 if (pos == pit->size())
1481 else // potentional bug... BUG (Lgb)
1482 if (pit->isSeparator(pos)) {
1483 if (pos > cursorRow()->pos() &&
1484 bidi_level(pos) % 2 ==
1485 bidi_level(pos - 1) % 2)
1487 else if (pos + 1 < pit->size())
1492 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1493 real_current_font = getFont(pit, pos);
1495 if (cursor.pos() == pit->size() &&
1496 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1497 !cursor.boundary()) {
1498 Language const * lang =
1499 pit->getParLanguage(bv()->buffer()->params);
1500 current_font.setLanguage(lang);
1501 current_font.setNumber(LyXFont::OFF);
1502 real_current_font.setLanguage(lang);
1503 real_current_font.setNumber(LyXFont::OFF);
1508 // returns the column near the specified x-coordinate of the row
1509 // x is set to the real beginning of this column
1510 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1511 RowList::iterator rit, int & x, bool & boundary) const
1513 double tmpx = rit->x();
1514 double fill_separator = rit->fill_separator();
1515 double fill_hfill = rit->fill_hfill();
1516 double fill_label_hfill = rit->fill_label_hfill();
1518 pos_type vc = rit->pos();
1519 pos_type last = lastPrintablePos(*pit, rit);
1521 LyXLayout_ptr const & layout = pit->layout();
1523 bool left_side = false;
1525 pos_type body_pos = pit->beginningOfBody();
1526 double last_tmpx = tmpx;
1529 (body_pos - 1 > last ||
1530 !pit->isLineSeparator(body_pos - 1)))
1533 // check for empty row
1539 while (vc <= last && tmpx <= x) {
1542 if (body_pos > 0 && c == body_pos - 1) {
1543 tmpx += fill_label_hfill +
1544 font_metrics::width(layout->labelsep, getLabelFont(pit));
1545 if (pit->isLineSeparator(body_pos - 1))
1546 tmpx -= singleWidth(pit, body_pos - 1);
1549 if (hfillExpansion(*pit, rit, c)) {
1550 tmpx += singleWidth(pit, c);
1554 tmpx += fill_label_hfill;
1555 } else if (pit->isSeparator(c)) {
1556 tmpx += singleWidth(pit, c);
1558 tmpx += fill_separator;
1560 tmpx += singleWidth(pit, c);
1565 if ((tmpx + last_tmpx) / 2 > x) {
1570 if (vc > last + 1) // This shouldn't happen.
1574 // This (rtl_support test) is not needed, but gives
1575 // some speedup if rtl_support == false
1576 bool const lastrow = lyxrc.rtl_support
1577 && boost::next(rit) == pit->rows.end();
1579 // If lastrow is false, we don't need to compute
1580 // the value of rtl.
1581 bool const rtl = (lastrow)
1582 ? pit->isRightToLeftPar(bv()->buffer()->params)
1585 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1586 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1588 else if (vc == rit->pos()) {
1590 if (bidi_level(c) % 2 == 1)
1593 c = vis2log(vc - 1);
1594 bool const rtl = (bidi_level(c) % 2 == 1);
1595 if (left_side == rtl) {
1597 boundary = isBoundary(bv()->buffer(), *pit, c);
1601 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1602 if (bidi_level(last) % 2 == 0)
1603 tmpx -= singleWidth(pit, last);
1605 tmpx += singleWidth(pit, last);
1615 void LyXText::setCursorFromCoordinates(int x, int y)
1617 LyXCursor old_cursor = cursor;
1618 setCursorFromCoordinates(cursor, x, y);
1620 deleteEmptyParagraphMechanism(old_cursor);
1627 * return true if the cursor given is at the end of a row,
1628 * and the next row is filled by an inset that spans an entire
1631 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1633 RowList::iterator row = lt.getRow(cur);
1634 RowList::iterator next = boost::next(row);
1636 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1639 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1642 InsetOld const * inset = cur.par()->getInset(cur.pos());
1643 if (inset->needFullRow() || inset->display())
1651 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1653 // Get the row first.
1654 ParagraphList::iterator pit;
1655 RowList::iterator rit = getRowNearY(y, pit);
1657 pos_type const column = getColumnNearX(pit, rit, x, bound);
1659 cur.pos(rit->pos() + column);
1661 cur.y(y + rit->baseline());
1663 if (beforeFullRowInset(*this, cur)) {
1664 pos_type const last = lastPrintablePos(*pit, rit);
1665 RowList::iterator next_rit = rit;
1666 ParagraphList::iterator next_pit = pit;
1667 nextRow(next_pit, next_rit);
1668 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1669 cur.iy(y + rit->height() + next_rit->baseline());
1674 cur.boundary(bound);
1678 void LyXText::cursorLeft(bool internal)
1680 if (cursor.pos() > 0) {
1681 bool boundary = cursor.boundary();
1682 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1683 if (!internal && !boundary &&
1684 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1685 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1686 } else if (cursor.par() != ownerParagraphs().begin()) {
1687 // steps into the paragraph above
1688 ParagraphList::iterator pit = boost::prior(cursor.par());
1689 setCursor(pit, pit->size());
1694 void LyXText::cursorRight(bool internal)
1696 bool const at_end = (cursor.pos() == cursor.par()->size());
1697 bool const at_newline = !at_end &&
1698 cursor.par()->isNewline(cursor.pos());
1700 if (!internal && cursor.boundary() && !at_newline)
1701 setCursor(cursor.par(), cursor.pos(), true, false);
1703 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1705 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1706 setCursor(cursor.par(), cursor.pos(), true, true);
1707 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1708 setCursor(boost::next(cursor.par()), 0);
1712 void LyXText::cursorUp(bool selecting)
1715 int x = cursor.x_fix();
1716 int y = cursor.y() - cursorRow()->baseline() - 1;
1717 setCursorFromCoordinates(x, y);
1720 int y1 = cursor.iy() - topy;
1723 InsetOld * inset_hit = checkInsetHit(x, y1);
1724 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1725 inset_hit->localDispatch(
1726 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1730 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1731 cursorRow()->baseline() << endl;
1732 setCursorFromCoordinates(cursor.x_fix(),
1733 cursor.y() - cursorRow()->baseline() - 1);
1738 void LyXText::cursorDown(bool selecting)
1741 int x = cursor.x_fix();
1742 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1743 setCursorFromCoordinates(x, y);
1744 if (!selecting && cursorRow() == cursorIRow()) {
1746 int y1 = cursor.iy() - topy;
1749 InsetOld * inset_hit = checkInsetHit(x, y1);
1750 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1751 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1752 inset_hit->localDispatch(cmd);
1756 setCursorFromCoordinates(cursor.x_fix(),
1757 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1762 void LyXText::cursorUpParagraph()
1764 if (cursor.pos() > 0)
1765 setCursor(cursor.par(), 0);
1766 else if (cursor.par() != ownerParagraphs().begin())
1767 setCursor(boost::prior(cursor.par()), 0);
1771 void LyXText::cursorDownParagraph()
1773 ParagraphList::iterator par = cursor.par();
1774 ParagraphList::iterator next_par = boost::next(par);
1776 if (next_par != ownerParagraphs().end())
1777 setCursor(next_par, 0);
1779 setCursor(par, par->size());
1783 // fix the cursor `cur' after a characters has been deleted at `where'
1784 // position. Called by deleteEmptyParagraphMechanism
1785 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1787 // if cursor is not in the paragraph where the delete occured,
1789 if (cur.par() != where.par())
1792 // if cursor position is after the place where the delete occured,
1794 if (cur.pos() > where.pos())
1795 cur.pos(cur.pos()-1);
1797 // check also if we don't want to set the cursor on a spot behind the
1798 // pagragraph because we erased the last character.
1799 if (cur.pos() > cur.par()->size())
1800 cur.pos(cur.par()->size());
1802 // recompute row et al. for this cursor
1803 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1807 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1809 // Would be wrong to delete anything if we have a selection.
1810 if (selection.set())
1813 // We allow all kinds of "mumbo-jumbo" when freespacing.
1814 if (old_cursor.par()->isFreeSpacing())
1817 /* Ok I'll put some comments here about what is missing.
1818 I have fixed BackSpace (and thus Delete) to not delete
1819 double-spaces automagically. I have also changed Cut,
1820 Copy and Paste to hopefully do some sensible things.
1821 There are still some small problems that can lead to
1822 double spaces stored in the document file or space at
1823 the beginning of paragraphs. This happens if you have
1824 the cursor between to spaces and then save. Or if you
1825 cut and paste and the selection have a space at the
1826 beginning and then save right after the paste. I am
1827 sure none of these are very hard to fix, but I will
1828 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1829 that I can get some feedback. (Lgb)
1832 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1833 // delete the LineSeparator.
1836 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1837 // delete the LineSeparator.
1840 // If the pos around the old_cursor were spaces, delete one of them.
1841 if (old_cursor.par() != cursor.par()
1842 || old_cursor.pos() != cursor.pos()) {
1844 // Only if the cursor has really moved
1845 if (old_cursor.pos() > 0
1846 && old_cursor.pos() < old_cursor.par()->size()
1847 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1848 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1849 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1850 redoParagraph(old_cursor.par());
1854 #ifdef WITH_WARNINGS
1855 #warning This will not work anymore when we have multiple views of the same buffer
1856 // In this case, we will have to correct also the cursors held by
1857 // other bufferviews. It will probably be easier to do that in a more
1858 // automated way in LyXCursor code. (JMarc 26/09/2001)
1860 // correct all cursors held by the LyXText
1861 fixCursorAfterDelete(cursor, old_cursor);
1862 fixCursorAfterDelete(selection.cursor, old_cursor);
1863 fixCursorAfterDelete(selection.start, old_cursor);
1864 fixCursorAfterDelete(selection.end, old_cursor);
1869 // don't delete anything if this is the ONLY paragraph!
1870 if (ownerParagraphs().size() == 1)
1873 // Do not delete empty paragraphs with keepempty set.
1874 if (old_cursor.par()->allowEmpty())
1877 // only do our magic if we changed paragraph
1878 if (old_cursor.par() == cursor.par())
1881 // record if we have deleted a paragraph
1882 // we can't possibly have deleted a paragraph before this point
1883 bool deleted = false;
1885 if (old_cursor.par()->empty() ||
1886 (old_cursor.par()->size() == 1 &&
1887 old_cursor.par()->isLineSeparator(0))) {
1888 // ok, we will delete something
1889 LyXCursor tmpcursor;
1893 bool selection_position_was_oldcursor_position = (
1894 selection.cursor.par() == old_cursor.par()
1895 && selection.cursor.pos() == old_cursor.pos());
1898 cursor = old_cursor; // that undo can restore the right cursor position
1900 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1901 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1904 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1908 ownerParagraphs().erase(old_cursor.par());
1912 setCursorIntern(cursor.par(), cursor.pos());
1914 if (selection_position_was_oldcursor_position) {
1915 // correct selection
1916 selection.cursor = cursor;
1920 if (old_cursor.par()->stripLeadingSpaces()) {
1921 redoParagraph(old_cursor.par());
1923 setCursorIntern(cursor.par(), cursor.pos());
1924 selection.cursor = cursor;
1931 ParagraphList & LyXText::ownerParagraphs() const
1937 bool LyXText::isInInset() const
1939 // Sub-level has non-null bv owner and non-null inset owner.
1940 return inset_owner != 0;
1944 int defaultRowHeight()
1946 LyXFont const font(LyXFont::ALL_SANE);
1947 return int(font_metrics::maxAscent(font)
1948 + font_metrics::maxDescent(font) * 1.5);