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 pit = getPar(rit);
267 RowList::iterator end = endRow(pit);
269 for (++rit; rit != end; ) {
270 RowList::iterator rit2 = 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(pit, tmprow, workWidth()));
304 setHeightOfRow(pit, 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(pit);
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 RowList::iterator end = boost::next(rit);
720 ParagraphList::iterator pit = cursor.par();
721 pos_type last_pos = lastPos(*this, pit, rit);
723 if (next_rit == end) {
727 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
732 setCursor(pit, last_pos);
736 void LyXText::cursorTop()
738 setCursor(ownerParagraphs().begin(), 0);
742 void LyXText::cursorBottom()
744 ParagraphList::iterator lastpit =
745 boost::prior(ownerParagraphs().end());
746 setCursor(lastpit, lastpit->size());
750 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
752 // If the mask is completely neutral, tell user
753 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
754 // Could only happen with user style
755 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
759 // Try implicit word selection
760 // If there is a change in the language the implicit word selection
762 LyXCursor resetCursor = cursor;
763 bool implicitSelection = (font.language() == ignore_language
764 && font.number() == LyXFont::IGNORE)
765 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
768 setFont(font, toggleall);
770 // Implicit selections are cleared afterwards
771 //and cursor is set to the original position.
772 if (implicitSelection) {
774 cursor = resetCursor;
775 setCursor(cursor.par(), cursor.pos());
776 selection.cursor = cursor;
781 string LyXText::getStringToIndex()
783 // Try implicit word selection
784 // If there is a change in the language the implicit word selection
786 LyXCursor const reset_cursor = cursor;
787 bool const implicitSelection =
788 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
791 if (!selection.set())
792 bv()->owner()->message(_("Nothing to index!"));
793 else if (selection.start.par() != selection.end.par())
794 bv()->owner()->message(_("Cannot index more than one paragraph!"));
796 idxstring = selectionAsString(bv()->buffer(), false);
798 // Reset cursors to their original position.
799 cursor = reset_cursor;
800 setCursor(cursor.par(), cursor.pos());
801 selection.cursor = cursor;
803 // Clear the implicit selection.
804 if (implicitSelection)
811 // the DTP switches for paragraphs. LyX will store them in the first
812 // physical paragraph. When a paragraph is broken, the top settings rest,
813 // the bottom settings are given to the new one. So I can make sure,
814 // they do not duplicate themself and you cannnot make dirty things with
817 void LyXText::setParagraph(bool line_top, bool line_bottom,
818 bool pagebreak_top, bool pagebreak_bottom,
819 VSpace const & space_top,
820 VSpace const & space_bottom,
821 Spacing const & spacing,
823 string const & labelwidthstring,
826 LyXCursor tmpcursor = cursor;
827 if (!selection.set()) {
828 selection.start = cursor;
829 selection.end = cursor;
832 // make sure that the depth behind the selection are restored, too
833 ParagraphList::iterator endpit = boost::next(selection.end.par());
834 ParagraphList::iterator undoendpit = endpit;
835 ParagraphList::iterator pars_end = ownerParagraphs().end();
837 if (endpit != pars_end && endpit->getDepth()) {
838 while (endpit != pars_end && endpit->getDepth()) {
842 } else if (endpit != pars_end) {
843 // because of parindents etc.
847 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
848 boost::prior(undoendpit));
851 ParagraphList::iterator tmppit = selection.end.par();
853 while (tmppit != boost::prior(selection.start.par())) {
854 setCursor(tmppit, 0);
856 ParagraphList::iterator pit = cursor.par();
857 ParagraphParameters & params = pit->params();
859 params.lineTop(line_top);
860 params.lineBottom(line_bottom);
861 params.pagebreakTop(pagebreak_top);
862 params.pagebreakBottom(pagebreak_bottom);
863 params.spaceTop(space_top);
864 params.spaceBottom(space_bottom);
865 params.spacing(spacing);
866 // does the layout allow the new alignment?
867 LyXLayout_ptr const & layout = pit->layout();
869 if (align == LYX_ALIGN_LAYOUT)
870 align = layout->align;
871 if (align & layout->alignpossible) {
872 if (align == layout->align)
873 params.align(LYX_ALIGN_LAYOUT);
877 pit->setLabelWidthString(labelwidthstring);
878 params.noindent(noindent);
879 tmppit = boost::prior(pit);
882 redoParagraphs(selection.start.par(), endpit);
885 setCursor(selection.start.par(), selection.start.pos());
886 selection.cursor = cursor;
887 setCursor(selection.end.par(), selection.end.pos());
889 setCursor(tmpcursor.par(), tmpcursor.pos());
895 // set the counter of a paragraph. This includes the labels
896 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
898 LyXTextClass const & textclass = buf->params.getLyXTextClass();
899 LyXLayout_ptr const & layout = pit->layout();
901 if (pit != ownerParagraphs().begin()) {
903 pit->params().appendix(boost::prior(pit)->params().appendix());
904 if (!pit->params().appendix() &&
905 pit->params().startOfAppendix()) {
906 pit->params().appendix(true);
907 textclass.counters().reset();
909 pit->enumdepth = boost::prior(pit)->enumdepth;
910 pit->itemdepth = boost::prior(pit)->itemdepth;
912 pit->params().appendix(pit->params().startOfAppendix());
917 // Maybe we have to increment the enumeration depth.
918 // BUT, enumeration in a footnote is considered in isolation from its
919 // surrounding paragraph so don't increment if this is the
920 // first line of the footnote
921 // AND, bibliographies can't have their depth changed ie. they
922 // are always of depth 0
923 if (pit != ownerParagraphs().begin()
924 && boost::prior(pit)->getDepth() < pit->getDepth()
925 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
926 && pit->enumdepth < 3
927 && layout->labeltype != LABEL_BIBLIO) {
931 // Maybe we have to decrement the enumeration depth, see note above
932 if (pit != ownerParagraphs().begin()
933 && boost::prior(pit)->getDepth() > pit->getDepth()
934 && layout->labeltype != LABEL_BIBLIO) {
935 pit->enumdepth = depthHook(pit, ownerParagraphs(),
936 pit->getDepth())->enumdepth;
939 if (!pit->params().labelString().empty()) {
940 pit->params().labelString(string());
943 if (layout->margintype == MARGIN_MANUAL) {
944 if (pit->params().labelWidthString().empty())
945 pit->setLabelWidthString(layout->labelstring());
947 pit->setLabelWidthString(string());
950 // is it a layout that has an automatic label?
951 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
952 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
956 if (i >= 0 && i <= buf->params.secnumdepth) {
960 textclass.counters().step(layout->latexname());
962 // Is there a label? Useful for Chapter layout
963 if (!pit->params().appendix()) {
964 s << buf->B_(layout->labelstring());
966 s << buf->B_(layout->labelstring_appendix());
969 // Use of an integer is here less than elegant. For now.
970 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
971 if (!pit->params().appendix()) {
972 numbertype = "sectioning";
974 numbertype = "appendix";
975 if (pit->isRightToLeftPar(buf->params))
982 << textclass.counters()
983 .numberLabel(layout->latexname(),
984 numbertype, langtype, head);
986 pit->params().labelString(STRCONV(s.str()));
988 // reset enum counters
989 textclass.counters().reset("enum");
990 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
991 textclass.counters().reset("enum");
992 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
994 // Yes I know this is a really, really! bad solution
996 string enumcounter("enum");
998 switch (pit->enumdepth) {
1007 enumcounter += "iv";
1010 // not a valid enumdepth...
1014 textclass.counters().step(enumcounter);
1016 s << textclass.counters()
1017 .numberLabel(enumcounter, "enumeration");
1018 pit->params().labelString(STRCONV(s.str()));
1020 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1021 textclass.counters().step("bibitem");
1022 int number = textclass.counters().value("bibitem");
1023 if (pit->bibitem()) {
1024 pit->bibitem()->setCounter(number);
1025 pit->params().labelString(layout->labelstring());
1027 // In biblio should't be following counters but...
1029 string s = buf->B_(layout->labelstring());
1031 // the caption hack:
1032 if (layout->labeltype == LABEL_SENSITIVE) {
1033 ParagraphList::iterator end = ownerParagraphs().end();
1034 ParagraphList::iterator tmppit = pit;
1037 while (tmppit != end && tmppit->inInset()
1038 // the single '=' is intended below
1039 && (in = tmppit->inInset()->owner()))
1041 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1042 in->lyxCode() == InsetOld::WRAP_CODE) {
1046 tmppit = ownerParagraphs().begin();
1047 for ( ; tmppit != end; ++tmppit)
1048 if (&*tmppit == in->parOwner())
1056 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1057 type = static_cast<InsetFloat*>(in)->params().type;
1058 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1059 type = static_cast<InsetWrap*>(in)->params().type;
1063 Floating const & fl = textclass.floats().getType(type);
1065 textclass.counters().step(fl.type());
1067 // Doesn't work... yet.
1068 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1070 // par->SetLayout(0);
1071 // s = layout->labelstring;
1072 s = _("Senseless: ");
1075 pit->params().labelString(s);
1077 // reset the enumeration counter. They are always reset
1078 // when there is any other layout between
1079 // Just fall-through between the cases so that all
1080 // enum counters deeper than enumdepth is also reset.
1081 switch (pit->enumdepth) {
1083 textclass.counters().reset("enumi");
1085 textclass.counters().reset("enumii");
1087 textclass.counters().reset("enumiii");
1089 textclass.counters().reset("enumiv");
1095 // Updates all counters. Paragraphs with changed label string will be rebroken
1096 void LyXText::updateCounters()
1099 bv()->buffer()->params.getLyXTextClass().counters().reset();
1101 ParagraphList::iterator beg = ownerParagraphs().begin();
1102 ParagraphList::iterator end = ownerParagraphs().end();
1103 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1104 string const oldLabel = pit->params().labelString();
1106 size_t maxdepth = 0;
1108 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1110 if (pit->params().depth() > maxdepth)
1111 pit->params().depth(maxdepth);
1113 // setCounter can potentially change the labelString.
1114 setCounter(bv()->buffer(), pit);
1116 string const & newLabel = pit->params().labelString();
1118 if (oldLabel != newLabel)
1124 void LyXText::insertInset(InsetOld * inset)
1126 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1128 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1130 cursor.par()->insertInset(cursor.pos(), inset);
1131 // Just to rebreak and refresh correctly.
1132 // The character will not be inserted a second time
1133 insertChar(Paragraph::META_INSET);
1134 // If we enter a highly editable inset the cursor should be before
1135 // the inset. After an Undo LyX tries to call inset->edit(...)
1136 // and fails if the cursor is behind the inset and getInset
1137 // does not return the inset!
1138 if (isHighlyEditableInset(inset))
1144 void LyXText::cutSelection(bool doclear, bool realcut)
1146 // Stuff what we got on the clipboard. Even if there is no selection.
1148 // There is a problem with having the stuffing here in that the
1149 // larger the selection the slower LyX will get. This can be
1150 // solved by running the line below only when the selection has
1151 // finished. The solution used currently just works, to make it
1152 // faster we need to be more clever and probably also have more
1153 // calls to stuffClipboard. (Lgb)
1154 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1156 // This doesn't make sense, if there is no selection
1157 if (!selection.set())
1160 // OK, we have a selection. This is always between selection.start
1161 // and selection.end
1163 // make sure that the depth behind the selection are restored, too
1164 ParagraphList::iterator endpit = boost::next(selection.end.par());
1165 ParagraphList::iterator undoendpit = endpit;
1166 ParagraphList::iterator pars_end = ownerParagraphs().end();
1168 if (endpit != pars_end && endpit->getDepth()) {
1169 while (endpit != pars_end && endpit->getDepth()) {
1171 undoendpit = endpit;
1173 } else if (endpit != pars_end) {
1174 // because of parindents etc.
1178 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1179 boost::prior(undoendpit));
1181 endpit = selection.end.par();
1182 int endpos = selection.end.pos();
1184 boost::tie(endpit, endpos) = realcut ?
1185 CutAndPaste::cutSelection(bv()->buffer()->params,
1187 selection.start.par(), endpit,
1188 selection.start.pos(), endpos,
1189 bv()->buffer()->params.textclass,
1191 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1193 selection.start.par(), endpit,
1194 selection.start.pos(), endpos,
1196 // sometimes necessary
1198 selection.start.par()->stripLeadingSpaces();
1200 redoParagraphs(selection.start.par(), boost::next(endpit));
1201 // cutSelection can invalidate the cursor so we need to set
1203 // we prefer the end for when tracking changes
1207 // need a valid cursor. (Lgb)
1210 setCursor(cursor.par(), cursor.pos());
1211 selection.cursor = cursor;
1216 void LyXText::copySelection()
1218 // stuff the selection onto the X clipboard, from an explicit copy request
1219 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1221 // this doesnt make sense, if there is no selection
1222 if (!selection.set())
1225 // ok we have a selection. This is always between selection.start
1226 // and sel_end cursor
1228 // copy behind a space if there is one
1229 while (selection.start.par()->size() > selection.start.pos()
1230 && selection.start.par()->isLineSeparator(selection.start.pos())
1231 && (selection.start.par() != selection.end.par()
1232 || selection.start.pos() < selection.end.pos()))
1233 selection.start.pos(selection.start.pos() + 1);
1235 CutAndPaste::copySelection(selection.start.par(),
1236 selection.end.par(),
1237 selection.start.pos(), selection.end.pos(),
1238 bv()->buffer()->params.textclass);
1242 void LyXText::pasteSelection(size_t sel_index)
1244 // this does not make sense, if there is nothing to paste
1245 if (!CutAndPaste::checkPastePossible())
1248 recordUndo(bv(), Undo::INSERT, cursor.par());
1250 ParagraphList::iterator endpit;
1255 boost::tie(ppp, endpit) =
1256 CutAndPaste::pasteSelection(*bv()->buffer(),
1258 cursor.par(), cursor.pos(),
1259 bv()->buffer()->params.textclass,
1261 bufferErrors(*bv()->buffer(), el);
1262 bv()->showErrorList(_("Paste"));
1264 redoParagraphs(cursor.par(), endpit);
1266 setCursor(cursor.par(), cursor.pos());
1269 selection.cursor = cursor;
1270 setCursor(ppp.first, ppp.second);
1276 void LyXText::setSelectionRange(lyx::pos_type length)
1281 selection.cursor = cursor;
1288 // simple replacing. The font of the first selected character is used
1289 void LyXText::replaceSelectionWithString(string const & str)
1291 recordUndo(bv(), Undo::ATOMIC);
1294 if (!selection.set()) { // create a dummy selection
1295 selection.end = cursor;
1296 selection.start = cursor;
1299 // Get font setting before we cut
1300 pos_type pos = selection.end.pos();
1301 LyXFont const font = selection.start.par()
1302 ->getFontSettings(bv()->buffer()->params,
1303 selection.start.pos());
1305 // Insert the new string
1306 string::const_iterator cit = str.begin();
1307 string::const_iterator end = str.end();
1308 for (; cit != end; ++cit) {
1309 selection.end.par()->insertChar(pos, (*cit), font);
1313 // Cut the selection
1314 cutSelection(true, false);
1320 // needed to insert the selection
1321 void LyXText::insertStringAsLines(string const & str)
1323 ParagraphList::iterator pit = cursor.par();
1324 pos_type pos = cursor.pos();
1325 ParagraphList::iterator endpit = boost::next(cursor.par());
1327 recordUndo(bv(), Undo::ATOMIC);
1329 // only to be sure, should not be neccessary
1332 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1334 redoParagraphs(cursor.par(), endpit);
1335 setCursor(cursor.par(), cursor.pos());
1336 selection.cursor = cursor;
1337 setCursor(pit, pos);
1342 // turns double-CR to single CR, others where converted into one
1343 // blank. Then InsertStringAsLines is called
1344 void LyXText::insertStringAsParagraphs(string const & str)
1346 string linestr(str);
1347 bool newline_inserted = false;
1348 string::size_type const siz = linestr.length();
1350 for (string::size_type i = 0; i < siz; ++i) {
1351 if (linestr[i] == '\n') {
1352 if (newline_inserted) {
1353 // we know that \r will be ignored by
1354 // InsertStringA. Of course, it is a dirty
1355 // trick, but it works...
1356 linestr[i - 1] = '\r';
1360 newline_inserted = true;
1362 } else if (IsPrintable(linestr[i])) {
1363 newline_inserted = false;
1366 insertStringAsLines(linestr);
1370 bool LyXText::setCursor(ParagraphList::iterator pit,
1372 bool setfont, bool boundary)
1374 LyXCursor old_cursor = cursor;
1375 setCursorIntern(pit, pos, setfont, boundary);
1376 return deleteEmptyParagraphMechanism(old_cursor);
1380 void LyXText::redoCursor()
1382 #warning maybe the same for selections?
1383 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1387 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1388 pos_type pos, bool boundary)
1390 Assert(pit != ownerParagraphs().end());
1394 cur.boundary(boundary);
1398 // get the cursor y position in text
1400 RowList::iterator row = getRow(pit, pos, y);
1401 RowList::iterator beg = rows().begin();
1403 RowList::iterator old_row = row;
1404 // if we are before the first char of this row and are still in the
1405 // same paragraph and there is a previous row then put the cursor on
1406 // the end of the previous row
1407 cur.iy(y + row->baseline());
1410 getPar(boost::prior(row)) == getPar(row) &&
1411 pos < pit->size() &&
1412 pit->getChar(pos) == Paragraph::META_INSET) {
1413 InsetOld * ins = pit->getInset(pos);
1414 if (ins && (ins->needFullRow() || ins->display())) {
1420 // y is now the beginning of the cursor row
1421 y += row->baseline();
1422 // y is now the cursor baseline
1425 pos_type last = lastPrintablePos(*this, pit, old_row);
1427 // None of these should happen, but we're scaredy-cats
1428 if (pos > pit->size()) {
1429 lyxerr << "dont like 1 please report" << endl;
1432 } else if (pos > last + 1) {
1433 lyxerr << "dont like 2 please report" << endl;
1434 // This shouldn't happen.
1437 } else if (pos < row->pos()) {
1438 lyxerr << "dont like 3 please report" << endl;
1443 // now get the cursors x position
1444 float x = getCursorX(pit, row, pos, last, boundary);
1447 if (old_row != row) {
1448 x = getCursorX(pit, old_row, pos, last, boundary);
1452 /* We take out this for the time being because 1) the redraw code is not
1453 prepared to this yet and 2) because some good policy has yet to be decided
1454 while editting: for instance how to act on rows being created/deleted
1458 //if the cursor is in a visible row, anchor to it
1460 if (topy < y && y < topy + bv()->workHeight())
1466 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1467 pos_type pos, pos_type last, bool boundary) const
1469 pos_type cursor_vpos = 0;
1471 double fill_separator;
1473 double fill_label_hfill;
1474 // This call HAS to be here because of the BidiTables!!!
1475 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1478 pos_type const rit_pos = rit->pos();
1481 cursor_vpos = rit_pos;
1482 else if (pos > last && !boundary)
1483 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1484 ? rit_pos : last + 1;
1485 else if (pos > rit_pos && (pos > last || boundary))
1486 // Place cursor after char at (logical) position pos - 1
1487 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1488 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1490 // Place cursor before char at (logical) position pos
1491 cursor_vpos = (bidi_level(pos) % 2 == 0)
1492 ? log2vis(pos) : log2vis(pos) + 1;
1494 pos_type body_pos = pit->beginningOfBody();
1496 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1499 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1500 pos_type pos = vis2log(vpos);
1501 if (body_pos > 0 && pos == body_pos - 1) {
1502 x += fill_label_hfill +
1503 font_metrics::width(
1504 pit->layout()->labelsep, getLabelFont(pit));
1505 if (pit->isLineSeparator(body_pos - 1))
1506 x -= singleWidth(pit, body_pos - 1);
1509 if (hfillExpansion(*this, pit, rit, pos)) {
1510 x += singleWidth(pit, pos);
1511 if (pos >= body_pos)
1514 x += fill_label_hfill;
1515 } else if (pit->isSeparator(pos)) {
1516 x += singleWidth(pit, pos);
1517 if (pos >= body_pos)
1518 x += fill_separator;
1520 x += singleWidth(pit, pos);
1526 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1527 pos_type pos, bool setfont, bool boundary)
1529 setCursor(cursor, pit, pos, boundary);
1535 void LyXText::setCurrentFont()
1537 pos_type pos = cursor.pos();
1538 ParagraphList::iterator pit = cursor.par();
1540 if (cursor.boundary() && pos > 0)
1544 if (pos == pit->size())
1546 else // potentional bug... BUG (Lgb)
1547 if (pit->isSeparator(pos)) {
1548 if (pos > cursorRow()->pos() &&
1549 bidi_level(pos) % 2 ==
1550 bidi_level(pos - 1) % 2)
1552 else if (pos + 1 < pit->size())
1557 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1558 real_current_font = getFont(pit, pos);
1560 if (cursor.pos() == pit->size() &&
1561 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1562 !cursor.boundary()) {
1563 Language const * lang =
1564 pit->getParLanguage(bv()->buffer()->params);
1565 current_font.setLanguage(lang);
1566 current_font.setNumber(LyXFont::OFF);
1567 real_current_font.setLanguage(lang);
1568 real_current_font.setNumber(LyXFont::OFF);
1573 // returns the column near the specified x-coordinate of the row
1574 // x is set to the real beginning of this column
1575 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1576 RowList::iterator rit, int & x, bool & boundary) const
1579 double fill_separator;
1581 double fill_label_hfill;
1583 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1585 pos_type vc = rit->pos();
1586 pos_type last = lastPrintablePos(*this, pit, rit);
1588 LyXLayout_ptr const & layout = pit->layout();
1590 bool left_side = false;
1592 pos_type body_pos = pit->beginningOfBody();
1593 double last_tmpx = tmpx;
1596 (body_pos - 1 > last ||
1597 !pit->isLineSeparator(body_pos - 1)))
1600 // check for empty row
1606 while (vc <= last && tmpx <= x) {
1609 if (body_pos > 0 && c == body_pos - 1) {
1610 tmpx += fill_label_hfill +
1611 font_metrics::width(layout->labelsep, getLabelFont(pit));
1612 if (pit->isLineSeparator(body_pos - 1))
1613 tmpx -= singleWidth(pit, body_pos - 1);
1616 if (hfillExpansion(*this, pit, rit, c)) {
1617 tmpx += singleWidth(pit, c);
1621 tmpx += fill_label_hfill;
1622 } else if (pit->isSeparator(c)) {
1623 tmpx += singleWidth(pit, c);
1625 tmpx += fill_separator;
1627 tmpx += singleWidth(pit, c);
1632 if ((tmpx + last_tmpx) / 2 > x) {
1637 if (vc > last + 1) // This shouldn't happen.
1641 // This (rtl_support test) is not needed, but gives
1642 // some speedup if rtl_support == false
1643 bool const lastrow = lyxrc.rtl_support
1644 && boost::next(rit) == endRow(pit);
1646 // If lastrow is false, we don't need to compute
1647 // the value of rtl.
1648 bool const rtl = (lastrow)
1649 ? pit->isRightToLeftPar(bv()->buffer()->params)
1652 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1653 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1655 else if (vc == rit->pos()) {
1657 if (bidi_level(c) % 2 == 1)
1660 c = vis2log(vc - 1);
1661 bool const rtl = (bidi_level(c) % 2 == 1);
1662 if (left_side == rtl) {
1664 boundary = isBoundary(bv()->buffer(), *pit, c);
1668 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1669 if (bidi_level(last) % 2 == 0)
1670 tmpx -= singleWidth(pit, last);
1672 tmpx += singleWidth(pit, last);
1682 void LyXText::setCursorFromCoordinates(int x, int y)
1684 //LyXCursor old_cursor = cursor;
1685 setCursorFromCoordinates(cursor, x, y);
1687 #warning DEPM disabled, otherwise crash when entering new table
1688 //deleteEmptyParagraphMechanism(old_cursor);
1695 * return true if the cursor given is at the end of a row,
1696 * and the next row is filled by an inset that spans an entire
1699 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1701 RowList::iterator row = lt.getRow(cur);
1702 if (boost::next(row) == lt.rows().end())
1705 RowList::iterator next = boost::next(row);
1707 if (next->pos() != cur.pos() || lt.getPar(next) != cur.par())
1710 if (cur.pos() == cur.par()->size()
1711 || !cur.par()->isInset(cur.pos()))
1714 InsetOld const * inset = cur.par()->getInset(cur.pos());
1715 if (inset->needFullRow() || inset->display())
1723 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1725 // Get the row first.
1727 RowList::iterator row = getRowNearY(y);
1728 ParagraphList::iterator pit = getPar(row);
1730 pos_type const column = getColumnNearX(pit, row, x, bound);
1732 cur.pos(row->pos() + column);
1734 cur.y(y + row->baseline());
1736 if (beforeFullRowInset(*this, cur)) {
1737 pos_type const last = lastPrintablePos(*this, pit, row);
1738 RowList::iterator next_row = boost::next(row);
1739 cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1740 cur.iy(y + row->height() + next_row->baseline());
1745 cur.boundary(bound);
1749 void LyXText::cursorLeft(bool internal)
1751 if (cursor.pos() > 0) {
1752 bool boundary = cursor.boundary();
1753 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1754 if (!internal && !boundary &&
1755 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1756 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1757 } else if (cursor.par() != ownerParagraphs().begin()) {
1758 // steps into the paragraph above
1759 ParagraphList::iterator pit = boost::prior(cursor.par());
1760 setCursor(pit, pit->size());
1765 void LyXText::cursorRight(bool internal)
1767 bool const at_end = (cursor.pos() == cursor.par()->size());
1768 bool const at_newline = !at_end &&
1769 cursor.par()->isNewline(cursor.pos());
1771 if (!internal && cursor.boundary() && !at_newline)
1772 setCursor(cursor.par(), cursor.pos(), true, false);
1774 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1776 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1777 setCursor(cursor.par(), cursor.pos(), true, true);
1778 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1779 setCursor(boost::next(cursor.par()), 0);
1783 void LyXText::cursorUp(bool selecting)
1786 int x = cursor.x_fix();
1787 int y = cursor.y() - cursorRow()->baseline() - 1;
1788 setCursorFromCoordinates(x, y);
1791 int y1 = cursor.iy() - topy;
1794 InsetOld * inset_hit = checkInsetHit(x, y1);
1795 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1796 inset_hit->localDispatch(
1797 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1801 setCursorFromCoordinates(bv(), cursor.x_fix(),
1802 cursor.y() - cursorRow()->baseline() - 1);
1807 void LyXText::cursorDown(bool selecting)
1810 int x = cursor.x_fix();
1811 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1812 setCursorFromCoordinates(x, y);
1813 if (!selecting && cursorRow() == cursorIRow()) {
1815 int y1 = cursor.iy() - topy;
1818 InsetOld * inset_hit = checkInsetHit(x, y1);
1819 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1820 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1821 inset_hit->localDispatch(cmd);
1825 setCursorFromCoordinates(bv(), cursor.x_fix(),
1826 cursor.y() - cursorRow()->baseline()
1827 + cursorRow()->height() + 1);
1832 void LyXText::cursorUpParagraph()
1834 if (cursor.pos() > 0)
1835 setCursor(cursor.par(), 0);
1836 else if (cursor.par() != ownerParagraphs().begin())
1837 setCursor(boost::prior(cursor.par()), 0);
1841 void LyXText::cursorDownParagraph()
1843 ParagraphList::iterator par = cursor.par();
1844 ParagraphList::iterator next_par = boost::next(par);
1846 if (next_par != ownerParagraphs().end())
1847 setCursor(next_par, 0);
1849 setCursor(par, par->size());
1853 // fix the cursor `cur' after a characters has been deleted at `where'
1854 // position. Called by deleteEmptyParagraphMechanism
1855 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1857 // if cursor is not in the paragraph where the delete occured,
1859 if (cur.par() != where.par())
1862 // if cursor position is after the place where the delete occured,
1864 if (cur.pos() > where.pos())
1865 cur.pos(cur.pos()-1);
1867 // check also if we don't want to set the cursor on a spot behind the
1868 // pagragraph because we erased the last character.
1869 if (cur.pos() > cur.par()->size())
1870 cur.pos(cur.par()->size());
1872 // recompute row et al. for this cursor
1873 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1877 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1879 // Would be wrong to delete anything if we have a selection.
1880 if (selection.set())
1883 // We allow all kinds of "mumbo-jumbo" when freespacing.
1884 if (old_cursor.par()->isFreeSpacing())
1887 /* Ok I'll put some comments here about what is missing.
1888 I have fixed BackSpace (and thus Delete) to not delete
1889 double-spaces automagically. I have also changed Cut,
1890 Copy and Paste to hopefully do some sensible things.
1891 There are still some small problems that can lead to
1892 double spaces stored in the document file or space at
1893 the beginning of paragraphs. This happens if you have
1894 the cursor betwenn to spaces and then save. Or if you
1895 cut and paste and the selection have a space at the
1896 beginning and then save right after the paste. I am
1897 sure none of these are very hard to fix, but I will
1898 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1899 that I can get some feedback. (Lgb)
1902 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1903 // delete the LineSeparator.
1906 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1907 // delete the LineSeparator.
1910 // If the pos around the old_cursor were spaces, delete one of them.
1911 if (old_cursor.par() != cursor.par()
1912 || old_cursor.pos() != cursor.pos()) {
1914 // Only if the cursor has really moved
1915 if (old_cursor.pos() > 0
1916 && old_cursor.pos() < old_cursor.par()->size()
1917 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1918 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1919 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1920 redoParagraph(old_cursor.par());
1924 #ifdef WITH_WARNINGS
1925 #warning This will not work anymore when we have multiple views of the same buffer
1926 // In this case, we will have to correct also the cursors held by
1927 // other bufferviews. It will probably be easier to do that in a more
1928 // automated way in LyXCursor code. (JMarc 26/09/2001)
1930 // correct all cursors held by the LyXText
1931 fixCursorAfterDelete(cursor, old_cursor);
1932 fixCursorAfterDelete(selection.cursor, old_cursor);
1933 fixCursorAfterDelete(selection.start, old_cursor);
1934 fixCursorAfterDelete(selection.end, old_cursor);
1939 // don't delete anything if this is the ONLY paragraph!
1940 if (ownerParagraphs().size() == 1)
1943 // Do not delete empty paragraphs with keepempty set.
1944 if (old_cursor.par()->allowEmpty())
1947 // only do our magic if we changed paragraph
1948 if (old_cursor.par() == cursor.par())
1951 // record if we have deleted a paragraph
1952 // we can't possibly have deleted a paragraph before this point
1953 bool deleted = false;
1955 if (old_cursor.par()->empty() ||
1956 (old_cursor.par()->size() == 1 &&
1957 old_cursor.par()->isLineSeparator(0))) {
1958 // ok, we will delete anything
1959 LyXCursor tmpcursor;
1963 bool selection_position_was_oldcursor_position = (
1964 selection.cursor.par() == old_cursor.par()
1965 && selection.cursor.pos() == old_cursor.pos());
1967 if (getRow(old_cursor) != rows().begin()) {
1968 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1970 cursor = old_cursor; // that undo can restore the right cursor position
1971 #warning FIXME. --end() iterator is usable here
1972 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1973 while (endpit != ownerParagraphs().end() &&
1974 endpit->getDepth()) {
1978 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
1979 boost::prior(endpit));
1983 removeRow(getRow(old_cursor));
1985 ownerParagraphs().erase(old_cursor.par());
1987 /* Breakagain the next par. Needed because of
1988 * the parindent that can occur or dissappear.
1989 * The next row can change its height, if
1990 * there is another layout before */
1991 RowList::iterator tmprit = boost::next(prevrow);
1992 if (tmprit != rows().end()) {
1993 redoParagraph(getPar(tmprit));
1996 setHeightOfRow(getPar(prevrow), prevrow);
1998 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2001 cursor = old_cursor; // that undo can restore the right cursor position
2002 #warning FIXME. --end() iterator is usable here
2003 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2004 while (endpit != ownerParagraphs().end() &&
2005 endpit->getDepth()) {
2009 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2013 removeRow(getRow(old_cursor));
2015 ownerParagraphs().erase(old_cursor.par());
2017 /* Breakagain the next par. Needed because of
2018 the parindent that can occur or dissappear.
2019 The next row can change its height, if
2020 there is another layout before */
2021 if (nextrow != rows().end()) {
2022 redoParagraph(getPar(nextrow));
2028 setCursorIntern(cursor.par(), cursor.pos());
2030 if (selection_position_was_oldcursor_position) {
2031 // correct selection
2032 selection.cursor = cursor;
2036 if (old_cursor.par()->stripLeadingSpaces()) {
2037 redoParagraph(old_cursor.par());
2039 setCursorIntern(cursor.par(), cursor.pos());
2040 selection.cursor = cursor;
2047 ParagraphList & LyXText::ownerParagraphs() const
2050 return inset_owner->paragraphs;
2052 return bv_owner->buffer()->paragraphs;
2056 bool LyXText::isInInset() const
2058 // Sub-level has non-null bv owner and non-null inset owner.
2059 return inset_owner != 0 && bv_owner != 0;
2063 int defaultRowHeight()
2065 LyXFont const font(LyXFont::ALL_SANE);
2066 return int(font_metrics::maxAscent(font)
2067 + font_metrics::maxDescent(font) * 1.5);