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_row_offset_(0),
64 inset_owner(0), the_locking_inset(0), bv_owner(bv)
66 anchor_row_ = rows().end();
70 LyXText::LyXText(BufferView * bv, InsetText * inset)
71 : height(0), width(0), anchor_row_offset_(0),
72 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
74 anchor_row_ = rows().end();
78 void LyXText::init(BufferView * bview)
86 anchor_row_ = rows().end();
87 anchor_row_offset_ = 0;
89 current_font = getFont(ownerParagraphs().begin(), 0);
91 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
92 setCursorIntern(ownerParagraphs().begin(), 0);
93 selection.cursor = cursor;
99 // Gets the fully instantiated font at a given position in a paragraph
100 // Basically the same routine as Paragraph::getFont() in paragraph.C.
101 // The difference is that this one is used for displaying, and thus we
102 // are allowed to make cosmetic improvements. For instance make footnotes
104 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
108 LyXLayout_ptr const & layout = pit->layout();
110 BufferParams const & params = bv()->buffer()->params;
112 // We specialize the 95% common case:
113 if (!pit->getDepth()) {
114 if (layout->labeltype == LABEL_MANUAL
115 && pos < pit->beginningOfBody()) {
117 LyXFont f = pit->getFontSettings(params, pos);
119 pit->inInset()->getDrawFont(f);
120 return f.realize(layout->reslabelfont);
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->resfont);
129 // The uncommon case need not be optimized as much
133 if (pos < pit->beginningOfBody()) {
135 layoutfont = layout->labelfont;
138 layoutfont = layout->font;
141 LyXFont tmpfont = pit->getFontSettings(params, pos);
142 tmpfont.realize(layoutfont);
145 pit->inInset()->getDrawFont(tmpfont);
147 // Realize with the fonts of lesser depth.
148 tmpfont.realize(outerFont(pit, ownerParagraphs()));
149 tmpfont.realize(defaultfont_);
155 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
157 LyXLayout_ptr const & layout = pit->layout();
159 if (!pit->getDepth())
160 return layout->resfont;
162 LyXFont font = layout->font;
163 // Realize with the fonts of lesser depth.
164 font.realize(outerFont(pit, ownerParagraphs()));
165 font.realize(defaultfont_);
171 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
173 LyXLayout_ptr const & layout = pit->layout();
175 if (!pit->getDepth())
176 return layout->reslabelfont;
178 LyXFont font = layout->labelfont;
179 // Realize with the fonts of lesser depth.
180 font.realize(outerFont(pit, ownerParagraphs()));
181 font.realize(defaultfont_);
187 void LyXText::setCharFont(ParagraphList::iterator pit,
188 pos_type pos, LyXFont const & fnt,
191 BufferParams const & params = bv()->buffer()->params;
192 LyXFont font = getFont(pit, pos);
193 font.update(fnt, params.language, toggleall);
194 // Let the insets convert their font
195 if (pit->isInset(pos)) {
196 InsetOld * inset = pit->getInset(pos);
197 if (isEditableInset(inset)) {
198 static_cast<UpdatableInset *>(inset)
199 ->setFont(bv(), fnt, toggleall, true);
203 // Plug through to version below:
204 setCharFont(pit, pos, font);
208 void LyXText::setCharFont(
209 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
212 LyXLayout_ptr const & layout = pit->layout();
214 // Get concrete layout font to reduce against
217 if (pos < pit->beginningOfBody())
218 layoutfont = layout->labelfont;
220 layoutfont = layout->font;
222 // Realize against environment font information
223 if (pit->getDepth()) {
224 ParagraphList::iterator tp = pit;
225 while (!layoutfont.resolved() &&
226 tp != ownerParagraphs().end() &&
228 tp = outerHook(tp, ownerParagraphs());
229 if (tp != ownerParagraphs().end())
230 layoutfont.realize(tp->layout()->font);
234 layoutfont.realize(defaultfont_);
236 // Now, reduce font against full layout font
237 font.reduce(layoutfont);
239 pit->setFont(pos, font);
243 // removes the row and reset the touched counters
244 void LyXText::removeRow(RowList::iterator rit)
246 if (anchor_row_ == rit) {
247 if (rit != rows().begin()) {
248 anchor_row_ = boost::prior(rit);
249 anchor_row_offset_ += anchor_row_->height();
251 anchor_row_ = boost::next(rit);
252 anchor_row_offset_ -= rit->height();
256 // the text becomes smaller
257 height -= rit->height();
263 // remove all following rows of the paragraph of the specified row.
264 void LyXText::removeParagraph(RowList::iterator rit)
266 ParagraphList::iterator tmppit = getPar(rit);
269 while (rit != rows().end() && getPar(rit) == tmppit) {
270 RowList::iterator tmprit = boost::next(rit);
277 void LyXText::insertParagraph(ParagraphList::iterator pit,
278 RowList::iterator rit)
280 // insert a new row, starting at position 0
281 rit = rowlist_.insert(rit, Row(0));
283 // and now append the whole paragraph before the new row
285 pos_type const last = pit->size();
289 pos_type z = rowBreakPoint(pit, *rit);
291 RowList::iterator tmprow = rit;
295 rit = rowlist_.insert(boost::next(rit), Row(z));
300 // Set the dimensions of the row
301 // fixed fill setting now by calling inset->update() in
302 // SingleWidth when needed!
303 tmprow->fill(fill(tmprow, workWidth()));
304 setHeightOfRow(tmprow);
310 InsetOld * LyXText::getInset() const
312 ParagraphList::iterator pit = cursor.par();
313 pos_type const pos = cursor.pos();
315 if (pos < pit->size() && pit->isInset(pos)) {
316 return pit->getInset(pos);
322 void LyXText::toggleInset()
324 InsetOld * inset = getInset();
325 // is there an editable inset at cursor position?
326 if (!isEditableInset(inset)) {
327 // No, try to see if we are inside a collapsable inset
328 if (inset_owner && inset_owner->owner()
329 && inset_owner->owner()->isOpen()) {
330 bv()->unlockInset(inset_owner->owner());
331 inset_owner->owner()->close(bv());
332 bv()->getLyXText()->cursorRight(bv());
336 //bv()->owner()->message(inset->editMessage());
338 // do we want to keep this?? (JMarc)
339 if (!isHighlyEditableInset(inset))
340 recordUndo(bv(), Undo::ATOMIC);
351 /* used in setlayout */
352 // Asger is not sure we want to do this...
353 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
356 LyXLayout_ptr const & layout = par.layout();
357 pos_type const psize = par.size();
360 for (pos_type pos = 0; pos < psize; ++pos) {
361 if (pos < par.beginningOfBody())
362 layoutfont = layout->labelfont;
364 layoutfont = layout->font;
366 LyXFont tmpfont = par.getFontSettings(params, pos);
367 tmpfont.reduce(layoutfont);
368 par.setFont(pos, tmpfont);
373 ParagraphList::iterator
374 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
375 LyXCursor & send_cur,
376 string const & layout)
378 ParagraphList::iterator endpit = boost::next(send_cur.par());
379 ParagraphList::iterator undoendpit = endpit;
380 ParagraphList::iterator pars_end = ownerParagraphs().end();
382 if (endpit != pars_end && endpit->getDepth()) {
383 while (endpit != pars_end && endpit->getDepth()) {
387 } else if (endpit != pars_end) {
388 // because of parindents etc.
392 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
394 // ok we have a selection. This is always between sstart_cur
395 // and sel_end cursor
397 ParagraphList::iterator pit = sstart_cur.par();
398 ParagraphList::iterator epit = boost::next(send_cur.par());
400 LyXLayout_ptr const & lyxlayout =
401 bv()->buffer()->params.getLyXTextClass()[layout];
404 pit->applyLayout(lyxlayout);
405 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
406 pit->params().spaceTop(lyxlayout->fill_top ?
407 VSpace(VSpace::VFILL)
408 : VSpace(VSpace::NONE));
409 pit->params().spaceBottom(lyxlayout->fill_bottom ?
410 VSpace(VSpace::VFILL)
411 : VSpace(VSpace::NONE));
412 if (lyxlayout->margintype == MARGIN_MANUAL)
413 pit->setLabelWidthString(lyxlayout->labelstring());
416 } while (pit != epit);
422 // set layout over selection and make a total rebreak of those paragraphs
423 void LyXText::setLayout(string const & layout)
425 LyXCursor tmpcursor = cursor; // store the current cursor
427 // if there is no selection just set the layout
428 // of the current paragraph
429 if (!selection.set()) {
430 selection.start = cursor; // dummy selection
431 selection.end = cursor;
434 // special handling of new environment insets
435 BufferParams const & params = bv()->buffer()->params;
436 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
437 if (lyxlayout->is_environment) {
438 // move everything in a new environment inset
439 lyxerr << "setting layout " << layout << endl;
440 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
441 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
442 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
443 InsetOld * inset = new InsetEnvironment(params, layout);
444 if (bv()->insertInset(inset)) {
446 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
453 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
454 selection.end, layout);
455 redoParagraphs(selection.start.par(), endpit);
457 // we have to reset the selection, because the
458 // geometry could have changed
459 setCursor(selection.start.par(), selection.start.pos(), false);
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos(), false);
465 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
469 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
471 ParagraphList::iterator pit = cursor.par();
472 ParagraphList::iterator end = cursor.par();
473 ParagraphList::iterator start = pit;
475 if (selection.set()) {
476 pit = selection.start.par();
477 end = selection.end.par();
481 ParagraphList::iterator pastend = boost::next(end);
484 recordUndo(bv(), Undo::ATOMIC, start, end);
486 bool changed = false;
488 int prev_after_depth = 0;
489 #warning parlist ... could be nicer ?
490 if (start != ownerParagraphs().begin()) {
491 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
495 int const depth = pit->params().depth();
496 if (type == bv_funcs::INC_DEPTH) {
497 if (depth < prev_after_depth
498 && pit->layout()->labeltype != LABEL_BIBLIO) {
501 pit->params().depth(depth + 1);
506 pit->params().depth(depth - 1);
509 prev_after_depth = pit->getMaxDepthAfter();
521 redoParagraphs(start, pastend);
523 // We need to actually move the text->cursor. I don't
524 // understand why ...
525 LyXCursor tmpcursor = cursor;
527 // we have to reset the visual selection because the
528 // geometry could have changed
529 if (selection.set()) {
530 setCursor(selection.start.par(), selection.start.pos());
531 selection.cursor = cursor;
532 setCursor(selection.end.par(), selection.end.pos());
535 // this handles the counter labels, and also fixes up
536 // depth values for follow-on (child) paragraphs
540 setCursor(tmpcursor.par(), tmpcursor.pos());
546 // set font over selection and make a total rebreak of those paragraphs
547 void LyXText::setFont(LyXFont const & font, bool toggleall)
549 // if there is no selection just set the current_font
550 if (!selection.set()) {
551 // Determine basis font
553 if (cursor.pos() < cursor.par()->beginningOfBody()) {
554 layoutfont = getLabelFont(cursor.par());
556 layoutfont = getLayoutFont(cursor.par());
558 // Update current font
559 real_current_font.update(font,
560 bv()->buffer()->params.language,
563 // Reduce to implicit settings
564 current_font = real_current_font;
565 current_font.reduce(layoutfont);
566 // And resolve it completely
567 real_current_font.realize(layoutfont);
572 LyXCursor tmpcursor = cursor; // store the current cursor
574 // ok we have a selection. This is always between sel_start_cursor
575 // and sel_end cursor
577 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
579 cursor = selection.start;
580 while (cursor.par() != selection.end.par() ||
581 cursor.pos() < selection.end.pos())
583 if (cursor.pos() < cursor.par()->size()) {
584 // an open footnote should behave like a closed one
585 setCharFont(cursor.par(), cursor.pos(),
587 cursor.pos(cursor.pos() + 1);
590 cursor.par(boost::next(cursor.par()));
595 redoParagraph(selection.start.par());
597 // we have to reset the selection, because the
598 // geometry could have changed, but we keep
599 // it for user convenience
600 setCursor(selection.start.par(), selection.start.pos());
601 selection.cursor = cursor;
602 setCursor(selection.end.par(), selection.end.pos());
604 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
605 tmpcursor.boundary());
609 // rebreaks all paragraphs between the specified pars
610 // This function is needed after SetLayout and SetFont etc.
611 void LyXText::redoParagraphs(ParagraphList::iterator start,
612 ParagraphList::iterator end)
614 for ( ; start != end; ++start)
615 redoParagraph(start);
619 void LyXText::redoParagraph(ParagraphList::iterator pit)
621 RowList::iterator first = beginRow(pit);
622 RowList::iterator last = endRow(first);
624 // remove paragraph from rowlist
625 while (first != last) {
626 RowList::iterator rit2 = first;
631 // reinsert the paragraph
632 insertParagraph(pit, last);
636 void LyXText::fullRebreak()
638 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
640 selection.cursor = cursor;
644 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
646 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
647 //Assert(mi.base.textwidth);
654 anchor_row_ = rows().end();
655 anchor_row_offset_ = 0;
657 ParagraphList::iterator pit = ownerParagraphs().begin();
658 ParagraphList::iterator end = ownerParagraphs().end();
660 for (; pit != end; ++pit) {
661 InsetList::iterator ii = pit->insetlist.begin();
662 InsetList::iterator iend = pit->insetlist.end();
663 for (; ii != iend; ++ii) {
666 #warning FIXME: pos != 0
667 m.base.font = getFont(pit, 0);
668 ii->inset->metrics(m, dim);
675 dim.asc = rows().begin()->ascent_of_text();
676 dim.des = height - dim.asc;
677 dim.wid = std::max(mi.base.textwidth, int(width));
681 // important for the screen
684 // the cursor set functions have a special mechanism. When they
685 // realize, that you left an empty paragraph, they will delete it.
686 // They also delete the corresponding row
688 // need the selection cursor:
689 void LyXText::setSelection()
691 TextCursor::setSelection();
696 void LyXText::clearSelection()
698 TextCursor::clearSelection();
700 // reset this in the bv_owner!
701 if (bv_owner && bv_owner->text)
702 bv_owner->text->xsel_cache.set(false);
706 void LyXText::cursorHome()
708 setCursor(cursor.par(), cursorRow()->pos());
712 void LyXText::cursorEnd()
714 if (cursor.par()->empty())
717 RowList::iterator rit = cursorRow();
718 RowList::iterator next_rit = boost::next(rit);
719 ParagraphList::iterator pit = cursor.par();
720 pos_type last_pos = lastPos(*this, pit, rit);
722 if (next_rit == rows().end() || getPar(next_rit) != pit) {
726 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
731 setCursor(pit, last_pos);
735 void LyXText::cursorTop()
737 setCursor(ownerParagraphs().begin(), 0);
741 void LyXText::cursorBottom()
743 ParagraphList::iterator lastpit =
744 boost::prior(ownerParagraphs().end());
745 setCursor(lastpit, lastpit->size());
749 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
751 // If the mask is completely neutral, tell user
752 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
753 // Could only happen with user style
754 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
758 // Try implicit word selection
759 // If there is a change in the language the implicit word selection
761 LyXCursor resetCursor = cursor;
762 bool implicitSelection = (font.language() == ignore_language
763 && font.number() == LyXFont::IGNORE)
764 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
767 setFont(font, toggleall);
769 // Implicit selections are cleared afterwards
770 //and cursor is set to the original position.
771 if (implicitSelection) {
773 cursor = resetCursor;
774 setCursor(cursor.par(), cursor.pos());
775 selection.cursor = cursor;
780 string LyXText::getStringToIndex()
782 // Try implicit word selection
783 // If there is a change in the language the implicit word selection
785 LyXCursor const reset_cursor = cursor;
786 bool const implicitSelection =
787 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
790 if (!selection.set())
791 bv()->owner()->message(_("Nothing to index!"));
792 else if (selection.start.par() != selection.end.par())
793 bv()->owner()->message(_("Cannot index more than one paragraph!"));
795 idxstring = selectionAsString(bv()->buffer(), false);
797 // Reset cursors to their original position.
798 cursor = reset_cursor;
799 setCursor(cursor.par(), cursor.pos());
800 selection.cursor = cursor;
802 // Clear the implicit selection.
803 if (implicitSelection)
810 // the DTP switches for paragraphs. LyX will store them in the first
811 // physical paragraph. When a paragraph is broken, the top settings rest,
812 // the bottom settings are given to the new one. So I can make sure,
813 // they do not duplicate themself and you cannnot make dirty things with
816 void LyXText::setParagraph(bool line_top, bool line_bottom,
817 bool pagebreak_top, bool pagebreak_bottom,
818 VSpace const & space_top,
819 VSpace const & space_bottom,
820 Spacing const & spacing,
822 string const & labelwidthstring,
825 LyXCursor tmpcursor = cursor;
826 if (!selection.set()) {
827 selection.start = cursor;
828 selection.end = cursor;
831 // make sure that the depth behind the selection are restored, too
832 ParagraphList::iterator endpit = boost::next(selection.end.par());
833 ParagraphList::iterator undoendpit = endpit;
834 ParagraphList::iterator pars_end = ownerParagraphs().end();
836 if (endpit != pars_end && endpit->getDepth()) {
837 while (endpit != pars_end && endpit->getDepth()) {
841 } else if (endpit != pars_end) {
842 // because of parindents etc.
846 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
847 boost::prior(undoendpit));
850 ParagraphList::iterator tmppit = selection.end.par();
852 while (tmppit != boost::prior(selection.start.par())) {
853 setCursor(tmppit, 0);
855 ParagraphList::iterator pit = cursor.par();
856 ParagraphParameters & params = pit->params();
858 params.lineTop(line_top);
859 params.lineBottom(line_bottom);
860 params.pagebreakTop(pagebreak_top);
861 params.pagebreakBottom(pagebreak_bottom);
862 params.spaceTop(space_top);
863 params.spaceBottom(space_bottom);
864 params.spacing(spacing);
865 // does the layout allow the new alignment?
866 LyXLayout_ptr const & layout = pit->layout();
868 if (align == LYX_ALIGN_LAYOUT)
869 align = layout->align;
870 if (align & layout->alignpossible) {
871 if (align == layout->align)
872 params.align(LYX_ALIGN_LAYOUT);
876 pit->setLabelWidthString(labelwidthstring);
877 params.noindent(noindent);
878 tmppit = boost::prior(pit);
881 redoParagraphs(selection.start.par(), endpit);
884 setCursor(selection.start.par(), selection.start.pos());
885 selection.cursor = cursor;
886 setCursor(selection.end.par(), selection.end.pos());
888 setCursor(tmpcursor.par(), tmpcursor.pos());
894 // set the counter of a paragraph. This includes the labels
895 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
897 LyXTextClass const & textclass = buf->params.getLyXTextClass();
898 LyXLayout_ptr const & layout = pit->layout();
900 if (pit != ownerParagraphs().begin()) {
902 pit->params().appendix(boost::prior(pit)->params().appendix());
903 if (!pit->params().appendix() &&
904 pit->params().startOfAppendix()) {
905 pit->params().appendix(true);
906 textclass.counters().reset();
908 pit->enumdepth = boost::prior(pit)->enumdepth;
909 pit->itemdepth = boost::prior(pit)->itemdepth;
911 pit->params().appendix(pit->params().startOfAppendix());
916 // Maybe we have to increment the enumeration depth.
917 // BUT, enumeration in a footnote is considered in isolation from its
918 // surrounding paragraph so don't increment if this is the
919 // first line of the footnote
920 // AND, bibliographies can't have their depth changed ie. they
921 // are always of depth 0
922 if (pit != ownerParagraphs().begin()
923 && boost::prior(pit)->getDepth() < pit->getDepth()
924 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
925 && pit->enumdepth < 3
926 && layout->labeltype != LABEL_BIBLIO) {
930 // Maybe we have to decrement the enumeration depth, see note above
931 if (pit != ownerParagraphs().begin()
932 && boost::prior(pit)->getDepth() > pit->getDepth()
933 && layout->labeltype != LABEL_BIBLIO) {
934 pit->enumdepth = depthHook(pit, ownerParagraphs(),
935 pit->getDepth())->enumdepth;
938 if (!pit->params().labelString().empty()) {
939 pit->params().labelString(string());
942 if (layout->margintype == MARGIN_MANUAL) {
943 if (pit->params().labelWidthString().empty())
944 pit->setLabelWidthString(layout->labelstring());
946 pit->setLabelWidthString(string());
949 // is it a layout that has an automatic label?
950 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
951 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
955 if (i >= 0 && i <= buf->params.secnumdepth) {
959 textclass.counters().step(layout->latexname());
961 // Is there a label? Useful for Chapter layout
962 if (!pit->params().appendix()) {
963 s << buf->B_(layout->labelstring());
965 s << buf->B_(layout->labelstring_appendix());
968 // Use of an integer is here less than elegant. For now.
969 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
970 if (!pit->params().appendix()) {
971 numbertype = "sectioning";
973 numbertype = "appendix";
974 if (pit->isRightToLeftPar(buf->params))
981 << textclass.counters()
982 .numberLabel(layout->latexname(),
983 numbertype, langtype, head);
985 pit->params().labelString(STRCONV(s.str()));
987 // reset enum counters
988 textclass.counters().reset("enum");
989 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
990 textclass.counters().reset("enum");
991 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
993 // Yes I know this is a really, really! bad solution
995 string enumcounter("enum");
997 switch (pit->enumdepth) {
1006 enumcounter += "iv";
1009 // not a valid enumdepth...
1013 textclass.counters().step(enumcounter);
1015 s << textclass.counters()
1016 .numberLabel(enumcounter, "enumeration");
1017 pit->params().labelString(STRCONV(s.str()));
1019 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1020 textclass.counters().step("bibitem");
1021 int number = textclass.counters().value("bibitem");
1022 if (pit->bibitem()) {
1023 pit->bibitem()->setCounter(number);
1024 pit->params().labelString(layout->labelstring());
1026 // In biblio should't be following counters but...
1028 string s = buf->B_(layout->labelstring());
1030 // the caption hack:
1031 if (layout->labeltype == LABEL_SENSITIVE) {
1032 ParagraphList::iterator end = ownerParagraphs().end();
1033 ParagraphList::iterator tmppit = pit;
1036 while (tmppit != end && tmppit->inInset()
1037 // the single '=' is intended below
1038 && (in = tmppit->inInset()->owner()))
1040 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1041 in->lyxCode() == InsetOld::WRAP_CODE) {
1045 tmppit = ownerParagraphs().begin();
1046 for ( ; tmppit != end; ++tmppit)
1047 if (&*tmppit == in->parOwner())
1055 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1056 type = static_cast<InsetFloat*>(in)->params().type;
1057 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1058 type = static_cast<InsetWrap*>(in)->params().type;
1062 Floating const & fl = textclass.floats().getType(type);
1064 textclass.counters().step(fl.type());
1066 // Doesn't work... yet.
1067 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1069 // par->SetLayout(0);
1070 // s = layout->labelstring;
1071 s = _("Senseless: ");
1074 pit->params().labelString(s);
1076 // reset the enumeration counter. They are always reset
1077 // when there is any other layout between
1078 // Just fall-through between the cases so that all
1079 // enum counters deeper than enumdepth is also reset.
1080 switch (pit->enumdepth) {
1082 textclass.counters().reset("enumi");
1084 textclass.counters().reset("enumii");
1086 textclass.counters().reset("enumiii");
1088 textclass.counters().reset("enumiv");
1094 // Updates all counters. Paragraphs with changed label string will be rebroken
1095 void LyXText::updateCounters()
1098 bv()->buffer()->params.getLyXTextClass().counters().reset();
1100 ParagraphList::iterator beg = ownerParagraphs().begin();
1101 ParagraphList::iterator end = ownerParagraphs().end();
1102 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1103 string const oldLabel = pit->params().labelString();
1105 size_t maxdepth = 0;
1107 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1109 if (pit->params().depth() > maxdepth)
1110 pit->params().depth(maxdepth);
1112 // setCounter can potentially change the labelString.
1113 setCounter(bv()->buffer(), pit);
1115 string const & newLabel = pit->params().labelString();
1117 if (oldLabel != newLabel)
1123 void LyXText::insertInset(InsetOld * inset)
1125 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1127 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1129 cursor.par()->insertInset(cursor.pos(), inset);
1130 // Just to rebreak and refresh correctly.
1131 // The character will not be inserted a second time
1132 insertChar(Paragraph::META_INSET);
1133 // If we enter a highly editable inset the cursor should be before
1134 // the inset. After an Undo LyX tries to call inset->edit(...)
1135 // and fails if the cursor is behind the inset and getInset
1136 // does not return the inset!
1137 if (isHighlyEditableInset(inset))
1143 void LyXText::cutSelection(bool doclear, bool realcut)
1145 // Stuff what we got on the clipboard. Even if there is no selection.
1147 // There is a problem with having the stuffing here in that the
1148 // larger the selection the slower LyX will get. This can be
1149 // solved by running the line below only when the selection has
1150 // finished. The solution used currently just works, to make it
1151 // faster we need to be more clever and probably also have more
1152 // calls to stuffClipboard. (Lgb)
1153 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1155 // This doesn't make sense, if there is no selection
1156 if (!selection.set())
1159 // OK, we have a selection. This is always between selection.start
1160 // and selection.end
1162 // make sure that the depth behind the selection are restored, too
1163 ParagraphList::iterator endpit = boost::next(selection.end.par());
1164 ParagraphList::iterator undoendpit = endpit;
1165 ParagraphList::iterator pars_end = ownerParagraphs().end();
1167 if (endpit != pars_end && endpit->getDepth()) {
1168 while (endpit != pars_end && endpit->getDepth()) {
1170 undoendpit = endpit;
1172 } else if (endpit != pars_end) {
1173 // because of parindents etc.
1177 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1178 boost::prior(undoendpit));
1180 endpit = selection.end.par();
1181 int endpos = selection.end.pos();
1183 boost::tie(endpit, endpos) = realcut ?
1184 CutAndPaste::cutSelection(bv()->buffer()->params,
1186 selection.start.par(), endpit,
1187 selection.start.pos(), endpos,
1188 bv()->buffer()->params.textclass,
1190 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1192 selection.start.par(), endpit,
1193 selection.start.pos(), endpos,
1195 // sometimes necessary
1197 selection.start.par()->stripLeadingSpaces();
1199 redoParagraphs(selection.start.par(), boost::next(endpit));
1200 // cutSelection can invalidate the cursor so we need to set
1202 // we prefer the end for when tracking changes
1206 // need a valid cursor. (Lgb)
1209 setCursor(cursor.par(), cursor.pos());
1210 selection.cursor = cursor;
1215 void LyXText::copySelection()
1217 // stuff the selection onto the X clipboard, from an explicit copy request
1218 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1220 // this doesnt make sense, if there is no selection
1221 if (!selection.set())
1224 // ok we have a selection. This is always between selection.start
1225 // and sel_end cursor
1227 // copy behind a space if there is one
1228 while (selection.start.par()->size() > selection.start.pos()
1229 && selection.start.par()->isLineSeparator(selection.start.pos())
1230 && (selection.start.par() != selection.end.par()
1231 || selection.start.pos() < selection.end.pos()))
1232 selection.start.pos(selection.start.pos() + 1);
1234 CutAndPaste::copySelection(selection.start.par(),
1235 selection.end.par(),
1236 selection.start.pos(), selection.end.pos(),
1237 bv()->buffer()->params.textclass);
1241 void LyXText::pasteSelection(size_t sel_index)
1243 // this does not make sense, if there is nothing to paste
1244 if (!CutAndPaste::checkPastePossible())
1247 recordUndo(bv(), Undo::INSERT, cursor.par());
1249 ParagraphList::iterator endpit;
1254 boost::tie(ppp, endpit) =
1255 CutAndPaste::pasteSelection(*bv()->buffer(),
1257 cursor.par(), cursor.pos(),
1258 bv()->buffer()->params.textclass,
1260 bufferErrors(*bv()->buffer(), el);
1261 bv()->showErrorList(_("Paste"));
1263 redoParagraphs(cursor.par(), endpit);
1265 setCursor(cursor.par(), cursor.pos());
1268 selection.cursor = cursor;
1269 setCursor(ppp.first, ppp.second);
1275 void LyXText::setSelectionRange(lyx::pos_type length)
1280 selection.cursor = cursor;
1287 // simple replacing. The font of the first selected character is used
1288 void LyXText::replaceSelectionWithString(string const & str)
1290 recordUndo(bv(), Undo::ATOMIC);
1293 if (!selection.set()) { // create a dummy selection
1294 selection.end = cursor;
1295 selection.start = cursor;
1298 // Get font setting before we cut
1299 pos_type pos = selection.end.pos();
1300 LyXFont const font = selection.start.par()
1301 ->getFontSettings(bv()->buffer()->params,
1302 selection.start.pos());
1304 // Insert the new string
1305 string::const_iterator cit = str.begin();
1306 string::const_iterator end = str.end();
1307 for (; cit != end; ++cit) {
1308 selection.end.par()->insertChar(pos, (*cit), font);
1312 // Cut the selection
1313 cutSelection(true, false);
1319 // needed to insert the selection
1320 void LyXText::insertStringAsLines(string const & str)
1322 ParagraphList::iterator pit = cursor.par();
1323 pos_type pos = cursor.pos();
1324 ParagraphList::iterator endpit = boost::next(cursor.par());
1326 recordUndo(bv(), Undo::ATOMIC);
1328 // only to be sure, should not be neccessary
1331 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1333 redoParagraphs(cursor.par(), endpit);
1334 setCursor(cursor.par(), cursor.pos());
1335 selection.cursor = cursor;
1336 setCursor(pit, pos);
1341 // turns double-CR to single CR, others where converted into one
1342 // blank. Then InsertStringAsLines is called
1343 void LyXText::insertStringAsParagraphs(string const & str)
1345 string linestr(str);
1346 bool newline_inserted = false;
1347 string::size_type const siz = linestr.length();
1349 for (string::size_type i = 0; i < siz; ++i) {
1350 if (linestr[i] == '\n') {
1351 if (newline_inserted) {
1352 // we know that \r will be ignored by
1353 // InsertStringA. Of course, it is a dirty
1354 // trick, but it works...
1355 linestr[i - 1] = '\r';
1359 newline_inserted = true;
1361 } else if (IsPrintable(linestr[i])) {
1362 newline_inserted = false;
1365 insertStringAsLines(linestr);
1369 bool LyXText::setCursor(ParagraphList::iterator pit,
1371 bool setfont, bool boundary)
1373 LyXCursor old_cursor = cursor;
1374 setCursorIntern(pit, pos, setfont, boundary);
1375 return deleteEmptyParagraphMechanism(old_cursor);
1379 void LyXText::redoCursor()
1381 #warning maybe the same for selections?
1382 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1386 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1387 pos_type pos, bool boundary)
1389 Assert(pit != ownerParagraphs().end());
1393 cur.boundary(boundary);
1397 // get the cursor y position in text
1399 RowList::iterator row = getRow(pit, pos, y);
1400 RowList::iterator beg = rows().begin();
1402 RowList::iterator old_row = row;
1403 // if we are before the first char of this row and are still in the
1404 // same paragraph and there is a previous row then put the cursor on
1405 // the end of the previous row
1406 cur.iy(y + row->baseline());
1409 getPar(boost::prior(row)) == getPar(row) &&
1410 pos < pit->size() &&
1411 pit->getChar(pos) == Paragraph::META_INSET) {
1412 InsetOld * ins = pit->getInset(pos);
1413 if (ins && (ins->needFullRow() || ins->display())) {
1419 // y is now the beginning of the cursor row
1420 y += row->baseline();
1421 // y is now the cursor baseline
1424 pos_type last = lastPrintablePos(*this, pit, old_row);
1426 // None of these should happen, but we're scaredy-cats
1427 if (pos > pit->size()) {
1428 lyxerr << "dont like 1 please report" << endl;
1431 } else if (pos > last + 1) {
1432 lyxerr << "dont like 2 please report" << endl;
1433 // This shouldn't happen.
1436 } else if (pos < row->pos()) {
1437 lyxerr << "dont like 3 please report" << endl;
1442 // now get the cursors x position
1443 float x = getCursorX(pit, row, pos, last, boundary);
1446 if (old_row != row) {
1447 x = getCursorX(pit, old_row, pos, last, boundary);
1451 /* We take out this for the time being because 1) the redraw code is not
1452 prepared to this yet and 2) because some good policy has yet to be decided
1453 while editting: for instance how to act on rows being created/deleted
1457 //if the cursor is in a visible row, anchor to it
1459 if (topy < y && y < topy + bv()->workHeight())
1465 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1466 pos_type pos, pos_type last, bool boundary) const
1468 pos_type cursor_vpos = 0;
1470 double fill_separator;
1472 double fill_label_hfill;
1473 // This call HAS to be here because of the BidiTables!!!
1474 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1477 pos_type const rit_pos = rit->pos();
1480 cursor_vpos = rit_pos;
1481 else if (pos > last && !boundary)
1482 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1483 ? rit_pos : last + 1;
1484 else if (pos > rit_pos && (pos > last || boundary))
1485 // Place cursor after char at (logical) position pos - 1
1486 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1487 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1489 // Place cursor before char at (logical) position pos
1490 cursor_vpos = (bidi_level(pos) % 2 == 0)
1491 ? log2vis(pos) : log2vis(pos) + 1;
1493 pos_type body_pos = pit->beginningOfBody();
1495 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1498 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1499 pos_type pos = vis2log(vpos);
1500 if (body_pos > 0 && pos == body_pos - 1) {
1501 x += fill_label_hfill +
1502 font_metrics::width(
1503 pit->layout()->labelsep, getLabelFont(pit));
1504 if (pit->isLineSeparator(body_pos - 1))
1505 x -= singleWidth(pit, body_pos - 1);
1508 if (hfillExpansion(*this, pit, rit, pos)) {
1509 x += singleWidth(pit, pos);
1510 if (pos >= body_pos)
1513 x += fill_label_hfill;
1514 } else if (pit->isSeparator(pos)) {
1515 x += singleWidth(pit, pos);
1516 if (pos >= body_pos)
1517 x += fill_separator;
1519 x += singleWidth(pit, pos);
1525 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1526 pos_type pos, bool setfont, bool boundary)
1528 setCursor(cursor, pit, pos, boundary);
1534 void LyXText::setCurrentFont()
1536 pos_type pos = cursor.pos();
1537 ParagraphList::iterator pit = cursor.par();
1539 if (cursor.boundary() && pos > 0)
1543 if (pos == pit->size())
1545 else // potentional bug... BUG (Lgb)
1546 if (pit->isSeparator(pos)) {
1547 if (pos > cursorRow()->pos() &&
1548 bidi_level(pos) % 2 ==
1549 bidi_level(pos - 1) % 2)
1551 else if (pos + 1 < pit->size())
1556 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1557 real_current_font = getFont(pit, pos);
1559 if (cursor.pos() == pit->size() &&
1560 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1561 !cursor.boundary()) {
1562 Language const * lang =
1563 pit->getParLanguage(bv()->buffer()->params);
1564 current_font.setLanguage(lang);
1565 current_font.setNumber(LyXFont::OFF);
1566 real_current_font.setLanguage(lang);
1567 real_current_font.setNumber(LyXFont::OFF);
1572 // returns the column near the specified x-coordinate of the row
1573 // x is set to the real beginning of this column
1574 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1575 RowList::iterator rit, int & x, bool & boundary) const
1578 double fill_separator;
1580 double fill_label_hfill;
1582 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1584 pos_type vc = rit->pos();
1585 pos_type last = lastPrintablePos(*this, pit, rit);
1587 LyXLayout_ptr const & layout = pit->layout();
1589 bool left_side = false;
1591 pos_type body_pos = pit->beginningOfBody();
1592 double last_tmpx = tmpx;
1595 (body_pos - 1 > last ||
1596 !pit->isLineSeparator(body_pos - 1)))
1599 // check for empty row
1605 while (vc <= last && tmpx <= x) {
1608 if (body_pos > 0 && c == body_pos - 1) {
1609 tmpx += fill_label_hfill +
1610 font_metrics::width(layout->labelsep, getLabelFont(pit));
1611 if (pit->isLineSeparator(body_pos - 1))
1612 tmpx -= singleWidth(pit, body_pos - 1);
1615 if (hfillExpansion(*this, pit, rit, c)) {
1616 tmpx += singleWidth(pit, c);
1620 tmpx += fill_label_hfill;
1621 } else if (pit->isSeparator(c)) {
1622 tmpx += singleWidth(pit, c);
1624 tmpx += fill_separator;
1626 tmpx += singleWidth(pit, c);
1631 if ((tmpx + last_tmpx) / 2 > x) {
1636 if (vc > last + 1) // This shouldn't happen.
1640 // This (rtl_support test) is not needed, but gives
1641 // some speedup if rtl_support=false
1642 RowList::iterator next_rit = boost::next(rit);
1644 bool const lastrow = lyxrc.rtl_support &&
1645 (next_rit == rowlist_.end() || getPar(next_rit) != pit);
1647 // If lastrow is false, we don't need to compute
1648 // the value of rtl.
1649 bool const rtl = (lastrow)
1650 ? pit->isRightToLeftPar(bv()->buffer()->params)
1653 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1654 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1656 else if (vc == rit->pos()) {
1658 if (bidi_level(c) % 2 == 1)
1661 c = vis2log(vc - 1);
1662 bool const rtl = (bidi_level(c) % 2 == 1);
1663 if (left_side == rtl) {
1665 boundary = isBoundary(bv()->buffer(), *pit, c);
1669 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1670 if (bidi_level(last) % 2 == 0)
1671 tmpx -= singleWidth(pit, last);
1673 tmpx += singleWidth(pit, last);
1683 void LyXText::setCursorFromCoordinates(int x, int y)
1685 //LyXCursor old_cursor = cursor;
1686 setCursorFromCoordinates(cursor, x, y);
1688 #warning DEPM disabled, otherwise crash when entering new table
1689 //deleteEmptyParagraphMechanism(old_cursor);
1696 * return true if the cursor given is at the end of a row,
1697 * and the next row is filled by an inset that spans an entire
1700 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1702 RowList::iterator row = lt.getRow(cur);
1703 if (boost::next(row) == lt.rows().end())
1706 RowList::iterator next = boost::next(row);
1708 if (next->pos() != cur.pos() || lt.getPar(next) != cur.par())
1711 if (cur.pos() == cur.par()->size()
1712 || !cur.par()->isInset(cur.pos()))
1715 InsetOld const * inset = cur.par()->getInset(cur.pos());
1716 if (inset->needFullRow() || inset->display())
1724 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1726 // Get the row first.
1728 RowList::iterator row = getRowNearY(y);
1729 ParagraphList::iterator pit = getPar(row);
1731 pos_type const column = getColumnNearX(pit, row, x, bound);
1733 cur.pos(row->pos() + column);
1735 cur.y(y + row->baseline());
1737 if (beforeFullRowInset(*this, cur)) {
1738 pos_type const last = lastPrintablePos(*this, pit, row);
1739 RowList::iterator next_row = boost::next(row);
1740 cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1741 cur.iy(y + row->height() + next_row->baseline());
1746 cur.boundary(bound);
1750 void LyXText::cursorLeft(bool internal)
1752 if (cursor.pos() > 0) {
1753 bool boundary = cursor.boundary();
1754 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1755 if (!internal && !boundary &&
1756 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1757 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1758 } else if (cursor.par() != ownerParagraphs().begin()) {
1759 // steps into the paragraph above
1760 ParagraphList::iterator pit = boost::prior(cursor.par());
1761 setCursor(pit, pit->size());
1766 void LyXText::cursorRight(bool internal)
1768 bool const at_end = (cursor.pos() == cursor.par()->size());
1769 bool const at_newline = !at_end &&
1770 cursor.par()->isNewline(cursor.pos());
1772 if (!internal && cursor.boundary() && !at_newline)
1773 setCursor(cursor.par(), cursor.pos(), true, false);
1775 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1777 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1778 setCursor(cursor.par(), cursor.pos(), true, true);
1779 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1780 setCursor(boost::next(cursor.par()), 0);
1784 void LyXText::cursorUp(bool selecting)
1787 int x = cursor.x_fix();
1788 int y = cursor.y() - cursorRow()->baseline() - 1;
1789 setCursorFromCoordinates(x, y);
1792 int y1 = cursor.iy() - topy;
1795 InsetOld * inset_hit = checkInsetHit(x, y1);
1796 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1797 inset_hit->localDispatch(
1798 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1802 setCursorFromCoordinates(bv(), cursor.x_fix(),
1803 cursor.y() - cursorRow()->baseline() - 1);
1808 void LyXText::cursorDown(bool selecting)
1811 int x = cursor.x_fix();
1812 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1813 setCursorFromCoordinates(x, y);
1814 if (!selecting && cursorRow() == cursorIRow()) {
1816 int y1 = cursor.iy() - topy;
1819 InsetOld * inset_hit = checkInsetHit(x, y1);
1820 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1821 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1822 inset_hit->localDispatch(cmd);
1826 setCursorFromCoordinates(bv(), cursor.x_fix(),
1827 cursor.y() - cursorRow()->baseline()
1828 + cursorRow()->height() + 1);
1833 void LyXText::cursorUpParagraph()
1835 if (cursor.pos() > 0)
1836 setCursor(cursor.par(), 0);
1837 else if (cursor.par() != ownerParagraphs().begin())
1838 setCursor(boost::prior(cursor.par()), 0);
1842 void LyXText::cursorDownParagraph()
1844 ParagraphList::iterator par = cursor.par();
1845 ParagraphList::iterator next_par = boost::next(par);
1847 if (next_par != ownerParagraphs().end())
1848 setCursor(next_par, 0);
1850 setCursor(par, par->size());
1854 // fix the cursor `cur' after a characters has been deleted at `where'
1855 // position. Called by deleteEmptyParagraphMechanism
1856 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1858 // if cursor is not in the paragraph where the delete occured,
1860 if (cur.par() != where.par())
1863 // if cursor position is after the place where the delete occured,
1865 if (cur.pos() > where.pos())
1866 cur.pos(cur.pos()-1);
1868 // check also if we don't want to set the cursor on a spot behind the
1869 // pagragraph because we erased the last character.
1870 if (cur.pos() > cur.par()->size())
1871 cur.pos(cur.par()->size());
1873 // recompute row et al. for this cursor
1874 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1878 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1880 // Would be wrong to delete anything if we have a selection.
1881 if (selection.set())
1884 // We allow all kinds of "mumbo-jumbo" when freespacing.
1885 if (old_cursor.par()->isFreeSpacing())
1888 /* Ok I'll put some comments here about what is missing.
1889 I have fixed BackSpace (and thus Delete) to not delete
1890 double-spaces automagically. I have also changed Cut,
1891 Copy and Paste to hopefully do some sensible things.
1892 There are still some small problems that can lead to
1893 double spaces stored in the document file or space at
1894 the beginning of paragraphs. This happens if you have
1895 the cursor betwenn to spaces and then save. Or if you
1896 cut and paste and the selection have a space at the
1897 beginning and then save right after the paste. I am
1898 sure none of these are very hard to fix, but I will
1899 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1900 that I can get some feedback. (Lgb)
1903 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1904 // delete the LineSeparator.
1907 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1908 // delete the LineSeparator.
1911 // If the pos around the old_cursor were spaces, delete one of them.
1912 if (old_cursor.par() != cursor.par()
1913 || old_cursor.pos() != cursor.pos()) {
1915 // Only if the cursor has really moved
1916 if (old_cursor.pos() > 0
1917 && old_cursor.pos() < old_cursor.par()->size()
1918 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1919 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1920 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1921 redoParagraph(old_cursor.par());
1925 #ifdef WITH_WARNINGS
1926 #warning This will not work anymore when we have multiple views of the same buffer
1927 // In this case, we will have to correct also the cursors held by
1928 // other bufferviews. It will probably be easier to do that in a more
1929 // automated way in LyXCursor code. (JMarc 26/09/2001)
1931 // correct all cursors held by the LyXText
1932 fixCursorAfterDelete(cursor, old_cursor);
1933 fixCursorAfterDelete(selection.cursor, old_cursor);
1934 fixCursorAfterDelete(selection.start, old_cursor);
1935 fixCursorAfterDelete(selection.end, old_cursor);
1940 // don't delete anything if this is the ONLY paragraph!
1941 if (ownerParagraphs().size() == 1)
1944 // Do not delete empty paragraphs with keepempty set.
1945 if (old_cursor.par()->allowEmpty())
1948 // only do our magic if we changed paragraph
1949 if (old_cursor.par() == cursor.par())
1952 // record if we have deleted a paragraph
1953 // we can't possibly have deleted a paragraph before this point
1954 bool deleted = false;
1956 if (old_cursor.par()->empty() ||
1957 (old_cursor.par()->size() == 1 &&
1958 old_cursor.par()->isLineSeparator(0))) {
1959 // ok, we will delete anything
1960 LyXCursor tmpcursor;
1964 bool selection_position_was_oldcursor_position = (
1965 selection.cursor.par() == old_cursor.par()
1966 && selection.cursor.pos() == old_cursor.pos());
1968 if (getRow(old_cursor) != rows().begin()) {
1969 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1971 cursor = old_cursor; // that undo can restore the right cursor position
1972 #warning FIXME. --end() iterator is usable here
1973 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1974 while (endpit != ownerParagraphs().end() &&
1975 endpit->getDepth()) {
1979 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
1980 boost::prior(endpit));
1984 removeRow(getRow(old_cursor));
1986 ownerParagraphs().erase(old_cursor.par());
1988 /* Breakagain the next par. Needed because of
1989 * the parindent that can occur or dissappear.
1990 * The next row can change its height, if
1991 * there is another layout before */
1992 RowList::iterator tmprit = boost::next(prevrow);
1993 if (tmprit != rows().end()) {
1994 redoParagraph(getPar(tmprit));
1997 setHeightOfRow(prevrow);
1999 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2002 cursor = old_cursor; // that undo can restore the right cursor position
2003 #warning FIXME. --end() iterator is usable here
2004 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2005 while (endpit != ownerParagraphs().end() &&
2006 endpit->getDepth()) {
2010 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2014 removeRow(getRow(old_cursor));
2016 ownerParagraphs().erase(old_cursor.par());
2018 /* Breakagain the next par. Needed because of
2019 the parindent that can occur or dissappear.
2020 The next row can change its height, if
2021 there is another layout before */
2022 if (nextrow != rows().end()) {
2023 redoParagraph(getPar(nextrow));
2029 setCursorIntern(cursor.par(), cursor.pos());
2031 if (selection_position_was_oldcursor_position) {
2032 // correct selection
2033 selection.cursor = cursor;
2037 if (old_cursor.par()->stripLeadingSpaces()) {
2038 redoParagraph(old_cursor.par());
2040 setCursorIntern(cursor.par(), cursor.pos());
2041 selection.cursor = cursor;
2048 ParagraphList & LyXText::ownerParagraphs() const
2051 return inset_owner->paragraphs;
2053 return bv_owner->buffer()->paragraphs;
2057 bool LyXText::isInInset() const
2059 // Sub-level has non-null bv owner and non-null inset owner.
2060 return inset_owner != 0 && bv_owner != 0;
2064 int defaultRowHeight()
2066 LyXFont const font(LyXFont::ALL_SANE);
2067 return int(font_metrics::maxAscent(font)
2068 + font_metrics::maxDescent(font) * 1.5);