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