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)
622 // find first row of given par
623 RowList::iterator first;
624 for (first = rows().begin(); first != rows().end(); ++first)
625 if (getPar(first) == pit)
628 // find last row of given par
629 RowList::iterator last = first;
630 for ( ; last != rows().end() && getPar(last) == pit; ++last)
633 Assert(first == beginRow(pit));
634 Assert(last == endRow(pit));
636 RowList::iterator first = beginRow(pit);
637 RowList::iterator last = endRow(pit);
640 // remove paragraph from rowlist
641 while (first != last) {
642 RowList::iterator rit2 = first;
647 // reinsert the paragraph
648 insertParagraph(pit, last);
652 void LyXText::fullRebreak()
654 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
656 selection.cursor = cursor;
660 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
662 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
663 //Assert(mi.base.textwidth);
670 anchor_row_ = rows().end();
671 anchor_row_offset_ = 0;
673 ParagraphList::iterator pit = ownerParagraphs().begin();
674 ParagraphList::iterator end = ownerParagraphs().end();
676 for (; pit != end; ++pit) {
677 InsetList::iterator ii = pit->insetlist.begin();
678 InsetList::iterator iend = pit->insetlist.end();
679 for (; ii != iend; ++ii) {
682 #warning FIXME: pos != 0
683 m.base.font = getFont(pit, 0);
684 ii->inset->metrics(m, dim);
691 dim.asc = rows().begin()->ascent_of_text();
692 dim.des = height - dim.asc;
693 dim.wid = std::max(mi.base.textwidth, int(width));
697 // important for the screen
700 // the cursor set functions have a special mechanism. When they
701 // realize, that you left an empty paragraph, they will delete it.
702 // They also delete the corresponding row
704 // need the selection cursor:
705 void LyXText::setSelection()
707 TextCursor::setSelection();
712 void LyXText::clearSelection()
714 TextCursor::clearSelection();
716 // reset this in the bv_owner!
717 if (bv_owner && bv_owner->text)
718 bv_owner->text->xsel_cache.set(false);
722 void LyXText::cursorHome()
724 setCursor(cursor.par(), cursorRow()->pos());
728 void LyXText::cursorEnd()
730 if (cursor.par()->empty())
733 RowList::iterator rit = cursorRow();
734 RowList::iterator next_rit = boost::next(rit);
735 ParagraphList::iterator pit = cursor.par();
736 pos_type last_pos = lastPos(*this, pit, rit);
738 if (next_rit == rows().end() || getPar(next_rit) != pit) {
742 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
747 setCursor(pit, last_pos);
751 void LyXText::cursorTop()
753 setCursor(ownerParagraphs().begin(), 0);
757 void LyXText::cursorBottom()
759 ParagraphList::iterator lastpit =
760 boost::prior(ownerParagraphs().end());
761 setCursor(lastpit, lastpit->size());
765 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
767 // If the mask is completely neutral, tell user
768 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
769 // Could only happen with user style
770 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
774 // Try implicit word selection
775 // If there is a change in the language the implicit word selection
777 LyXCursor resetCursor = cursor;
778 bool implicitSelection = (font.language() == ignore_language
779 && font.number() == LyXFont::IGNORE)
780 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
783 setFont(font, toggleall);
785 // Implicit selections are cleared afterwards
786 //and cursor is set to the original position.
787 if (implicitSelection) {
789 cursor = resetCursor;
790 setCursor(cursor.par(), cursor.pos());
791 selection.cursor = cursor;
796 string LyXText::getStringToIndex()
798 // Try implicit word selection
799 // If there is a change in the language the implicit word selection
801 LyXCursor const reset_cursor = cursor;
802 bool const implicitSelection =
803 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
806 if (!selection.set())
807 bv()->owner()->message(_("Nothing to index!"));
808 else if (selection.start.par() != selection.end.par())
809 bv()->owner()->message(_("Cannot index more than one paragraph!"));
811 idxstring = selectionAsString(bv()->buffer(), false);
813 // Reset cursors to their original position.
814 cursor = reset_cursor;
815 setCursor(cursor.par(), cursor.pos());
816 selection.cursor = cursor;
818 // Clear the implicit selection.
819 if (implicitSelection)
826 // the DTP switches for paragraphs. LyX will store them in the first
827 // physical paragraph. When a paragraph is broken, the top settings rest,
828 // the bottom settings are given to the new one. So I can make sure,
829 // they do not duplicate themself and you cannnot make dirty things with
832 void LyXText::setParagraph(bool line_top, bool line_bottom,
833 bool pagebreak_top, bool pagebreak_bottom,
834 VSpace const & space_top,
835 VSpace const & space_bottom,
836 Spacing const & spacing,
838 string const & labelwidthstring,
841 LyXCursor tmpcursor = cursor;
842 if (!selection.set()) {
843 selection.start = cursor;
844 selection.end = cursor;
847 // make sure that the depth behind the selection are restored, too
848 ParagraphList::iterator endpit = boost::next(selection.end.par());
849 ParagraphList::iterator undoendpit = endpit;
850 ParagraphList::iterator pars_end = ownerParagraphs().end();
852 if (endpit != pars_end && endpit->getDepth()) {
853 while (endpit != pars_end && endpit->getDepth()) {
857 } else if (endpit != pars_end) {
858 // because of parindents etc.
862 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
863 boost::prior(undoendpit));
866 ParagraphList::iterator tmppit = selection.end.par();
868 while (tmppit != boost::prior(selection.start.par())) {
869 setCursor(tmppit, 0);
871 ParagraphList::iterator pit = cursor.par();
872 ParagraphParameters & params = pit->params();
874 params.lineTop(line_top);
875 params.lineBottom(line_bottom);
876 params.pagebreakTop(pagebreak_top);
877 params.pagebreakBottom(pagebreak_bottom);
878 params.spaceTop(space_top);
879 params.spaceBottom(space_bottom);
880 params.spacing(spacing);
881 // does the layout allow the new alignment?
882 LyXLayout_ptr const & layout = pit->layout();
884 if (align == LYX_ALIGN_LAYOUT)
885 align = layout->align;
886 if (align & layout->alignpossible) {
887 if (align == layout->align)
888 params.align(LYX_ALIGN_LAYOUT);
892 pit->setLabelWidthString(labelwidthstring);
893 params.noindent(noindent);
894 tmppit = boost::prior(pit);
897 redoParagraphs(selection.start.par(), endpit);
900 setCursor(selection.start.par(), selection.start.pos());
901 selection.cursor = cursor;
902 setCursor(selection.end.par(), selection.end.pos());
904 setCursor(tmpcursor.par(), tmpcursor.pos());
910 // set the counter of a paragraph. This includes the labels
911 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
913 LyXTextClass const & textclass = buf->params.getLyXTextClass();
914 LyXLayout_ptr const & layout = pit->layout();
916 if (pit != ownerParagraphs().begin()) {
918 pit->params().appendix(boost::prior(pit)->params().appendix());
919 if (!pit->params().appendix() &&
920 pit->params().startOfAppendix()) {
921 pit->params().appendix(true);
922 textclass.counters().reset();
924 pit->enumdepth = boost::prior(pit)->enumdepth;
925 pit->itemdepth = boost::prior(pit)->itemdepth;
927 pit->params().appendix(pit->params().startOfAppendix());
932 // Maybe we have to increment the enumeration depth.
933 // BUT, enumeration in a footnote is considered in isolation from its
934 // surrounding paragraph so don't increment if this is the
935 // first line of the footnote
936 // AND, bibliographies can't have their depth changed ie. they
937 // are always of depth 0
938 if (pit != ownerParagraphs().begin()
939 && boost::prior(pit)->getDepth() < pit->getDepth()
940 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
941 && pit->enumdepth < 3
942 && layout->labeltype != LABEL_BIBLIO) {
946 // Maybe we have to decrement the enumeration depth, see note above
947 if (pit != ownerParagraphs().begin()
948 && boost::prior(pit)->getDepth() > pit->getDepth()
949 && layout->labeltype != LABEL_BIBLIO) {
950 pit->enumdepth = depthHook(pit, ownerParagraphs(),
951 pit->getDepth())->enumdepth;
954 if (!pit->params().labelString().empty()) {
955 pit->params().labelString(string());
958 if (layout->margintype == MARGIN_MANUAL) {
959 if (pit->params().labelWidthString().empty())
960 pit->setLabelWidthString(layout->labelstring());
962 pit->setLabelWidthString(string());
965 // is it a layout that has an automatic label?
966 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
967 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
971 if (i >= 0 && i <= buf->params.secnumdepth) {
975 textclass.counters().step(layout->latexname());
977 // Is there a label? Useful for Chapter layout
978 if (!pit->params().appendix()) {
979 s << buf->B_(layout->labelstring());
981 s << buf->B_(layout->labelstring_appendix());
984 // Use of an integer is here less than elegant. For now.
985 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
986 if (!pit->params().appendix()) {
987 numbertype = "sectioning";
989 numbertype = "appendix";
990 if (pit->isRightToLeftPar(buf->params))
997 << textclass.counters()
998 .numberLabel(layout->latexname(),
999 numbertype, langtype, head);
1001 pit->params().labelString(STRCONV(s.str()));
1003 // reset enum counters
1004 textclass.counters().reset("enum");
1005 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1006 textclass.counters().reset("enum");
1007 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1009 // Yes I know this is a really, really! bad solution
1011 string enumcounter("enum");
1013 switch (pit->enumdepth) {
1022 enumcounter += "iv";
1025 // not a valid enumdepth...
1029 textclass.counters().step(enumcounter);
1031 s << textclass.counters()
1032 .numberLabel(enumcounter, "enumeration");
1033 pit->params().labelString(STRCONV(s.str()));
1035 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1036 textclass.counters().step("bibitem");
1037 int number = textclass.counters().value("bibitem");
1038 if (pit->bibitem()) {
1039 pit->bibitem()->setCounter(number);
1040 pit->params().labelString(layout->labelstring());
1042 // In biblio should't be following counters but...
1044 string s = buf->B_(layout->labelstring());
1046 // the caption hack:
1047 if (layout->labeltype == LABEL_SENSITIVE) {
1048 ParagraphList::iterator end = ownerParagraphs().end();
1049 ParagraphList::iterator tmppit = pit;
1052 while (tmppit != end && tmppit->inInset()
1053 // the single '=' is intended below
1054 && (in = tmppit->inInset()->owner()))
1056 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1057 in->lyxCode() == InsetOld::WRAP_CODE) {
1061 tmppit = ownerParagraphs().begin();
1062 for ( ; tmppit != end; ++tmppit)
1063 if (&*tmppit == in->parOwner())
1071 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1072 type = static_cast<InsetFloat*>(in)->params().type;
1073 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1074 type = static_cast<InsetWrap*>(in)->params().type;
1078 Floating const & fl = textclass.floats().getType(type);
1080 textclass.counters().step(fl.type());
1082 // Doesn't work... yet.
1083 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1085 // par->SetLayout(0);
1086 // s = layout->labelstring;
1087 s = _("Senseless: ");
1090 pit->params().labelString(s);
1092 // reset the enumeration counter. They are always reset
1093 // when there is any other layout between
1094 // Just fall-through between the cases so that all
1095 // enum counters deeper than enumdepth is also reset.
1096 switch (pit->enumdepth) {
1098 textclass.counters().reset("enumi");
1100 textclass.counters().reset("enumii");
1102 textclass.counters().reset("enumiii");
1104 textclass.counters().reset("enumiv");
1110 // Updates all counters. Paragraphs with changed label string will be rebroken
1111 void LyXText::updateCounters()
1114 bv()->buffer()->params.getLyXTextClass().counters().reset();
1116 ParagraphList::iterator beg = ownerParagraphs().begin();
1117 ParagraphList::iterator end = ownerParagraphs().end();
1118 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1119 string const oldLabel = pit->params().labelString();
1121 size_t maxdepth = 0;
1123 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1125 if (pit->params().depth() > maxdepth)
1126 pit->params().depth(maxdepth);
1128 // setCounter can potentially change the labelString.
1129 setCounter(bv()->buffer(), pit);
1131 string const & newLabel = pit->params().labelString();
1133 if (oldLabel != newLabel)
1139 void LyXText::insertInset(InsetOld * inset)
1141 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1143 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1145 cursor.par()->insertInset(cursor.pos(), inset);
1146 // Just to rebreak and refresh correctly.
1147 // The character will not be inserted a second time
1148 insertChar(Paragraph::META_INSET);
1149 // If we enter a highly editable inset the cursor should be before
1150 // the inset. After an Undo LyX tries to call inset->edit(...)
1151 // and fails if the cursor is behind the inset and getInset
1152 // does not return the inset!
1153 if (isHighlyEditableInset(inset))
1159 void LyXText::cutSelection(bool doclear, bool realcut)
1161 // Stuff what we got on the clipboard. Even if there is no selection.
1163 // There is a problem with having the stuffing here in that the
1164 // larger the selection the slower LyX will get. This can be
1165 // solved by running the line below only when the selection has
1166 // finished. The solution used currently just works, to make it
1167 // faster we need to be more clever and probably also have more
1168 // calls to stuffClipboard. (Lgb)
1169 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1171 // This doesn't make sense, if there is no selection
1172 if (!selection.set())
1175 // OK, we have a selection. This is always between selection.start
1176 // and selection.end
1178 // make sure that the depth behind the selection are restored, too
1179 ParagraphList::iterator endpit = boost::next(selection.end.par());
1180 ParagraphList::iterator undoendpit = endpit;
1181 ParagraphList::iterator pars_end = ownerParagraphs().end();
1183 if (endpit != pars_end && endpit->getDepth()) {
1184 while (endpit != pars_end && endpit->getDepth()) {
1186 undoendpit = endpit;
1188 } else if (endpit != pars_end) {
1189 // because of parindents etc.
1193 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1194 boost::prior(undoendpit));
1196 endpit = selection.end.par();
1197 int endpos = selection.end.pos();
1199 boost::tie(endpit, endpos) = realcut ?
1200 CutAndPaste::cutSelection(bv()->buffer()->params,
1202 selection.start.par(), endpit,
1203 selection.start.pos(), endpos,
1204 bv()->buffer()->params.textclass,
1206 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1208 selection.start.par(), endpit,
1209 selection.start.pos(), endpos,
1211 // sometimes necessary
1213 selection.start.par()->stripLeadingSpaces();
1215 redoParagraphs(selection.start.par(), boost::next(endpit));
1216 // cutSelection can invalidate the cursor so we need to set
1218 // we prefer the end for when tracking changes
1222 // need a valid cursor. (Lgb)
1225 setCursor(cursor.par(), cursor.pos());
1226 selection.cursor = cursor;
1231 void LyXText::copySelection()
1233 // stuff the selection onto the X clipboard, from an explicit copy request
1234 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1236 // this doesnt make sense, if there is no selection
1237 if (!selection.set())
1240 // ok we have a selection. This is always between selection.start
1241 // and sel_end cursor
1243 // copy behind a space if there is one
1244 while (selection.start.par()->size() > selection.start.pos()
1245 && selection.start.par()->isLineSeparator(selection.start.pos())
1246 && (selection.start.par() != selection.end.par()
1247 || selection.start.pos() < selection.end.pos()))
1248 selection.start.pos(selection.start.pos() + 1);
1250 CutAndPaste::copySelection(selection.start.par(),
1251 selection.end.par(),
1252 selection.start.pos(), selection.end.pos(),
1253 bv()->buffer()->params.textclass);
1257 void LyXText::pasteSelection(size_t sel_index)
1259 // this does not make sense, if there is nothing to paste
1260 if (!CutAndPaste::checkPastePossible())
1263 recordUndo(bv(), Undo::INSERT, cursor.par());
1265 ParagraphList::iterator endpit;
1270 boost::tie(ppp, endpit) =
1271 CutAndPaste::pasteSelection(*bv()->buffer(),
1273 cursor.par(), cursor.pos(),
1274 bv()->buffer()->params.textclass,
1276 bufferErrors(*bv()->buffer(), el);
1277 bv()->showErrorList(_("Paste"));
1279 redoParagraphs(cursor.par(), endpit);
1281 setCursor(cursor.par(), cursor.pos());
1284 selection.cursor = cursor;
1285 setCursor(ppp.first, ppp.second);
1291 void LyXText::setSelectionRange(lyx::pos_type length)
1296 selection.cursor = cursor;
1303 // simple replacing. The font of the first selected character is used
1304 void LyXText::replaceSelectionWithString(string const & str)
1306 recordUndo(bv(), Undo::ATOMIC);
1309 if (!selection.set()) { // create a dummy selection
1310 selection.end = cursor;
1311 selection.start = cursor;
1314 // Get font setting before we cut
1315 pos_type pos = selection.end.pos();
1316 LyXFont const font = selection.start.par()
1317 ->getFontSettings(bv()->buffer()->params,
1318 selection.start.pos());
1320 // Insert the new string
1321 string::const_iterator cit = str.begin();
1322 string::const_iterator end = str.end();
1323 for (; cit != end; ++cit) {
1324 selection.end.par()->insertChar(pos, (*cit), font);
1328 // Cut the selection
1329 cutSelection(true, false);
1335 // needed to insert the selection
1336 void LyXText::insertStringAsLines(string const & str)
1338 ParagraphList::iterator pit = cursor.par();
1339 pos_type pos = cursor.pos();
1340 ParagraphList::iterator endpit = boost::next(cursor.par());
1342 recordUndo(bv(), Undo::ATOMIC);
1344 // only to be sure, should not be neccessary
1347 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1349 redoParagraphs(cursor.par(), endpit);
1350 setCursor(cursor.par(), cursor.pos());
1351 selection.cursor = cursor;
1352 setCursor(pit, pos);
1357 // turns double-CR to single CR, others where converted into one
1358 // blank. Then InsertStringAsLines is called
1359 void LyXText::insertStringAsParagraphs(string const & str)
1361 string linestr(str);
1362 bool newline_inserted = false;
1363 string::size_type const siz = linestr.length();
1365 for (string::size_type i = 0; i < siz; ++i) {
1366 if (linestr[i] == '\n') {
1367 if (newline_inserted) {
1368 // we know that \r will be ignored by
1369 // InsertStringA. Of course, it is a dirty
1370 // trick, but it works...
1371 linestr[i - 1] = '\r';
1375 newline_inserted = true;
1377 } else if (IsPrintable(linestr[i])) {
1378 newline_inserted = false;
1381 insertStringAsLines(linestr);
1385 bool LyXText::setCursor(ParagraphList::iterator pit,
1387 bool setfont, bool boundary)
1389 LyXCursor old_cursor = cursor;
1390 setCursorIntern(pit, pos, setfont, boundary);
1391 return deleteEmptyParagraphMechanism(old_cursor);
1395 void LyXText::redoCursor()
1397 #warning maybe the same for selections?
1398 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1402 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1403 pos_type pos, bool boundary)
1405 Assert(pit != ownerParagraphs().end());
1409 cur.boundary(boundary);
1413 // get the cursor y position in text
1415 RowList::iterator row = getRow(pit, pos, y);
1416 RowList::iterator beg = rows().begin();
1418 RowList::iterator old_row = row;
1419 // if we are before the first char of this row and are still in the
1420 // same paragraph and there is a previous row then put the cursor on
1421 // the end of the previous row
1422 cur.iy(y + row->baseline());
1425 getPar(boost::prior(row)) == getPar(row) &&
1426 pos < pit->size() &&
1427 pit->getChar(pos) == Paragraph::META_INSET) {
1428 InsetOld * ins = pit->getInset(pos);
1429 if (ins && (ins->needFullRow() || ins->display())) {
1435 // y is now the beginning of the cursor row
1436 y += row->baseline();
1437 // y is now the cursor baseline
1440 pos_type last = lastPrintablePos(*this, pit, old_row);
1442 // None of these should happen, but we're scaredy-cats
1443 if (pos > pit->size()) {
1444 lyxerr << "dont like 1 please report" << endl;
1447 } else if (pos > last + 1) {
1448 lyxerr << "dont like 2 please report" << endl;
1449 // This shouldn't happen.
1452 } else if (pos < row->pos()) {
1453 lyxerr << "dont like 3 please report" << endl;
1458 // now get the cursors x position
1459 float x = getCursorX(pit, row, pos, last, boundary);
1462 if (old_row != row) {
1463 x = getCursorX(pit, old_row, pos, last, boundary);
1467 /* We take out this for the time being because 1) the redraw code is not
1468 prepared to this yet and 2) because some good policy has yet to be decided
1469 while editting: for instance how to act on rows being created/deleted
1473 //if the cursor is in a visible row, anchor to it
1475 if (topy < y && y < topy + bv()->workHeight())
1481 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1482 pos_type pos, pos_type last, bool boundary) const
1484 pos_type cursor_vpos = 0;
1486 double fill_separator;
1488 double fill_label_hfill;
1489 // This call HAS to be here because of the BidiTables!!!
1490 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1493 pos_type const rit_pos = rit->pos();
1496 cursor_vpos = rit_pos;
1497 else if (pos > last && !boundary)
1498 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1499 ? rit_pos : last + 1;
1500 else if (pos > rit_pos && (pos > last || boundary))
1501 // Place cursor after char at (logical) position pos - 1
1502 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1503 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1505 // Place cursor before char at (logical) position pos
1506 cursor_vpos = (bidi_level(pos) % 2 == 0)
1507 ? log2vis(pos) : log2vis(pos) + 1;
1509 pos_type body_pos = pit->beginningOfBody();
1511 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1514 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1515 pos_type pos = vis2log(vpos);
1516 if (body_pos > 0 && pos == body_pos - 1) {
1517 x += fill_label_hfill +
1518 font_metrics::width(
1519 pit->layout()->labelsep, getLabelFont(pit));
1520 if (pit->isLineSeparator(body_pos - 1))
1521 x -= singleWidth(pit, body_pos - 1);
1524 if (hfillExpansion(*this, pit, rit, pos)) {
1525 x += singleWidth(pit, pos);
1526 if (pos >= body_pos)
1529 x += fill_label_hfill;
1530 } else if (pit->isSeparator(pos)) {
1531 x += singleWidth(pit, pos);
1532 if (pos >= body_pos)
1533 x += fill_separator;
1535 x += singleWidth(pit, pos);
1541 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1542 pos_type pos, bool setfont, bool boundary)
1544 setCursor(cursor, pit, pos, boundary);
1550 void LyXText::setCurrentFont()
1552 pos_type pos = cursor.pos();
1553 ParagraphList::iterator pit = cursor.par();
1555 if (cursor.boundary() && pos > 0)
1559 if (pos == pit->size())
1561 else // potentional bug... BUG (Lgb)
1562 if (pit->isSeparator(pos)) {
1563 if (pos > cursorRow()->pos() &&
1564 bidi_level(pos) % 2 ==
1565 bidi_level(pos - 1) % 2)
1567 else if (pos + 1 < pit->size())
1572 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1573 real_current_font = getFont(pit, pos);
1575 if (cursor.pos() == pit->size() &&
1576 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1577 !cursor.boundary()) {
1578 Language const * lang =
1579 pit->getParLanguage(bv()->buffer()->params);
1580 current_font.setLanguage(lang);
1581 current_font.setNumber(LyXFont::OFF);
1582 real_current_font.setLanguage(lang);
1583 real_current_font.setNumber(LyXFont::OFF);
1588 // returns the column near the specified x-coordinate of the row
1589 // x is set to the real beginning of this column
1590 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1591 RowList::iterator rit, int & x, bool & boundary) const
1594 double fill_separator;
1596 double fill_label_hfill;
1598 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1600 pos_type vc = rit->pos();
1601 pos_type last = lastPrintablePos(*this, pit, rit);
1603 LyXLayout_ptr const & layout = pit->layout();
1605 bool left_side = false;
1607 pos_type body_pos = pit->beginningOfBody();
1608 double last_tmpx = tmpx;
1611 (body_pos - 1 > last ||
1612 !pit->isLineSeparator(body_pos - 1)))
1615 // check for empty row
1621 while (vc <= last && tmpx <= x) {
1624 if (body_pos > 0 && c == body_pos - 1) {
1625 tmpx += fill_label_hfill +
1626 font_metrics::width(layout->labelsep, getLabelFont(pit));
1627 if (pit->isLineSeparator(body_pos - 1))
1628 tmpx -= singleWidth(pit, body_pos - 1);
1631 if (hfillExpansion(*this, pit, rit, c)) {
1632 tmpx += singleWidth(pit, c);
1636 tmpx += fill_label_hfill;
1637 } else if (pit->isSeparator(c)) {
1638 tmpx += singleWidth(pit, c);
1640 tmpx += fill_separator;
1642 tmpx += singleWidth(pit, c);
1647 if ((tmpx + last_tmpx) / 2 > x) {
1652 if (vc > last + 1) // This shouldn't happen.
1656 // This (rtl_support test) is not needed, but gives
1657 // some speedup if rtl_support=false
1658 RowList::iterator next_rit = boost::next(rit);
1660 bool const lastrow = lyxrc.rtl_support &&
1661 (next_rit == rowlist_.end() || getPar(next_rit) != pit);
1663 // If lastrow is false, we don't need to compute
1664 // the value of rtl.
1665 bool const rtl = (lastrow)
1666 ? pit->isRightToLeftPar(bv()->buffer()->params)
1669 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1670 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1672 else if (vc == rit->pos()) {
1674 if (bidi_level(c) % 2 == 1)
1677 c = vis2log(vc - 1);
1678 bool const rtl = (bidi_level(c) % 2 == 1);
1679 if (left_side == rtl) {
1681 boundary = isBoundary(bv()->buffer(), *pit, c);
1685 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1686 if (bidi_level(last) % 2 == 0)
1687 tmpx -= singleWidth(pit, last);
1689 tmpx += singleWidth(pit, last);
1699 void LyXText::setCursorFromCoordinates(int x, int y)
1701 //LyXCursor old_cursor = cursor;
1702 setCursorFromCoordinates(cursor, x, y);
1704 #warning DEPM disabled, otherwise crash when entering new table
1705 //deleteEmptyParagraphMechanism(old_cursor);
1712 * return true if the cursor given is at the end of a row,
1713 * and the next row is filled by an inset that spans an entire
1716 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1718 RowList::iterator row = lt.getRow(cur);
1719 if (boost::next(row) == lt.rows().end())
1722 RowList::iterator next = boost::next(row);
1724 if (next->pos() != cur.pos() || lt.getPar(next) != cur.par())
1727 if (cur.pos() == cur.par()->size()
1728 || !cur.par()->isInset(cur.pos()))
1731 InsetOld const * inset = cur.par()->getInset(cur.pos());
1732 if (inset->needFullRow() || inset->display())
1740 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1742 // Get the row first.
1744 RowList::iterator row = getRowNearY(y);
1745 ParagraphList::iterator pit = getPar(row);
1747 pos_type const column = getColumnNearX(pit, row, x, bound);
1749 cur.pos(row->pos() + column);
1751 cur.y(y + row->baseline());
1753 if (beforeFullRowInset(*this, cur)) {
1754 pos_type const last = lastPrintablePos(*this, pit, row);
1755 RowList::iterator next_row = boost::next(row);
1756 cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1757 cur.iy(y + row->height() + next_row->baseline());
1762 cur.boundary(bound);
1766 void LyXText::cursorLeft(bool internal)
1768 if (cursor.pos() > 0) {
1769 bool boundary = cursor.boundary();
1770 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1771 if (!internal && !boundary &&
1772 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1773 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1774 } else if (cursor.par() != ownerParagraphs().begin()) {
1775 // steps into the paragraph above
1776 ParagraphList::iterator pit = boost::prior(cursor.par());
1777 setCursor(pit, pit->size());
1782 void LyXText::cursorRight(bool internal)
1784 bool const at_end = (cursor.pos() == cursor.par()->size());
1785 bool const at_newline = !at_end &&
1786 cursor.par()->isNewline(cursor.pos());
1788 if (!internal && cursor.boundary() && !at_newline)
1789 setCursor(cursor.par(), cursor.pos(), true, false);
1791 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1793 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1794 setCursor(cursor.par(), cursor.pos(), true, true);
1795 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1796 setCursor(boost::next(cursor.par()), 0);
1800 void LyXText::cursorUp(bool selecting)
1803 int x = cursor.x_fix();
1804 int y = cursor.y() - cursorRow()->baseline() - 1;
1805 setCursorFromCoordinates(x, y);
1808 int y1 = cursor.iy() - topy;
1811 InsetOld * inset_hit = checkInsetHit(x, y1);
1812 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1813 inset_hit->localDispatch(
1814 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1818 setCursorFromCoordinates(bv(), cursor.x_fix(),
1819 cursor.y() - cursorRow()->baseline() - 1);
1824 void LyXText::cursorDown(bool selecting)
1827 int x = cursor.x_fix();
1828 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1829 setCursorFromCoordinates(x, y);
1830 if (!selecting && cursorRow() == cursorIRow()) {
1832 int y1 = cursor.iy() - topy;
1835 InsetOld * inset_hit = checkInsetHit(x, y1);
1836 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1837 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1838 inset_hit->localDispatch(cmd);
1842 setCursorFromCoordinates(bv(), cursor.x_fix(),
1843 cursor.y() - cursorRow()->baseline()
1844 + cursorRow()->height() + 1);
1849 void LyXText::cursorUpParagraph()
1851 if (cursor.pos() > 0)
1852 setCursor(cursor.par(), 0);
1853 else if (cursor.par() != ownerParagraphs().begin())
1854 setCursor(boost::prior(cursor.par()), 0);
1858 void LyXText::cursorDownParagraph()
1860 ParagraphList::iterator par = cursor.par();
1861 ParagraphList::iterator next_par = boost::next(par);
1863 if (next_par != ownerParagraphs().end())
1864 setCursor(next_par, 0);
1866 setCursor(par, par->size());
1870 // fix the cursor `cur' after a characters has been deleted at `where'
1871 // position. Called by deleteEmptyParagraphMechanism
1872 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1874 // if cursor is not in the paragraph where the delete occured,
1876 if (cur.par() != where.par())
1879 // if cursor position is after the place where the delete occured,
1881 if (cur.pos() > where.pos())
1882 cur.pos(cur.pos()-1);
1884 // check also if we don't want to set the cursor on a spot behind the
1885 // pagragraph because we erased the last character.
1886 if (cur.pos() > cur.par()->size())
1887 cur.pos(cur.par()->size());
1889 // recompute row et al. for this cursor
1890 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1894 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1896 // Would be wrong to delete anything if we have a selection.
1897 if (selection.set())
1900 // We allow all kinds of "mumbo-jumbo" when freespacing.
1901 if (old_cursor.par()->isFreeSpacing())
1904 /* Ok I'll put some comments here about what is missing.
1905 I have fixed BackSpace (and thus Delete) to not delete
1906 double-spaces automagically. I have also changed Cut,
1907 Copy and Paste to hopefully do some sensible things.
1908 There are still some small problems that can lead to
1909 double spaces stored in the document file or space at
1910 the beginning of paragraphs. This happens if you have
1911 the cursor betwenn to spaces and then save. Or if you
1912 cut and paste and the selection have a space at the
1913 beginning and then save right after the paste. I am
1914 sure none of these are very hard to fix, but I will
1915 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1916 that I can get some feedback. (Lgb)
1919 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1920 // delete the LineSeparator.
1923 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1924 // delete the LineSeparator.
1927 // If the pos around the old_cursor were spaces, delete one of them.
1928 if (old_cursor.par() != cursor.par()
1929 || old_cursor.pos() != cursor.pos()) {
1931 // Only if the cursor has really moved
1932 if (old_cursor.pos() > 0
1933 && old_cursor.pos() < old_cursor.par()->size()
1934 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1935 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1936 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1937 redoParagraph(old_cursor.par());
1941 #ifdef WITH_WARNINGS
1942 #warning This will not work anymore when we have multiple views of the same buffer
1943 // In this case, we will have to correct also the cursors held by
1944 // other bufferviews. It will probably be easier to do that in a more
1945 // automated way in LyXCursor code. (JMarc 26/09/2001)
1947 // correct all cursors held by the LyXText
1948 fixCursorAfterDelete(cursor, old_cursor);
1949 fixCursorAfterDelete(selection.cursor, old_cursor);
1950 fixCursorAfterDelete(selection.start, old_cursor);
1951 fixCursorAfterDelete(selection.end, old_cursor);
1956 // don't delete anything if this is the ONLY paragraph!
1957 if (ownerParagraphs().size() == 1)
1960 // Do not delete empty paragraphs with keepempty set.
1961 if (old_cursor.par()->allowEmpty())
1964 // only do our magic if we changed paragraph
1965 if (old_cursor.par() == cursor.par())
1968 // record if we have deleted a paragraph
1969 // we can't possibly have deleted a paragraph before this point
1970 bool deleted = false;
1972 if (old_cursor.par()->empty() ||
1973 (old_cursor.par()->size() == 1 &&
1974 old_cursor.par()->isLineSeparator(0))) {
1975 // ok, we will delete anything
1976 LyXCursor tmpcursor;
1980 bool selection_position_was_oldcursor_position = (
1981 selection.cursor.par() == old_cursor.par()
1982 && selection.cursor.pos() == old_cursor.pos());
1984 if (getRow(old_cursor) != rows().begin()) {
1985 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1987 cursor = old_cursor; // that undo can restore the right cursor position
1988 #warning FIXME. --end() iterator is usable here
1989 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1990 while (endpit != ownerParagraphs().end() &&
1991 endpit->getDepth()) {
1995 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
1996 boost::prior(endpit));
2000 removeRow(getRow(old_cursor));
2002 ownerParagraphs().erase(old_cursor.par());
2004 /* Breakagain the next par. Needed because of
2005 * the parindent that can occur or dissappear.
2006 * The next row can change its height, if
2007 * there is another layout before */
2008 RowList::iterator tmprit = boost::next(prevrow);
2009 if (tmprit != rows().end()) {
2010 redoParagraph(getPar(tmprit));
2013 setHeightOfRow(prevrow);
2015 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2018 cursor = old_cursor; // that undo can restore the right cursor position
2019 #warning FIXME. --end() iterator is usable here
2020 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2021 while (endpit != ownerParagraphs().end() &&
2022 endpit->getDepth()) {
2026 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2030 removeRow(getRow(old_cursor));
2032 ownerParagraphs().erase(old_cursor.par());
2034 /* Breakagain the next par. Needed because of
2035 the parindent that can occur or dissappear.
2036 The next row can change its height, if
2037 there is another layout before */
2038 if (nextrow != rows().end()) {
2039 redoParagraph(getPar(nextrow));
2045 setCursorIntern(cursor.par(), cursor.pos());
2047 if (selection_position_was_oldcursor_position) {
2048 // correct selection
2049 selection.cursor = cursor;
2053 if (old_cursor.par()->stripLeadingSpaces()) {
2054 redoParagraph(old_cursor.par());
2056 setCursorIntern(cursor.par(), cursor.pos());
2057 selection.cursor = cursor;
2064 ParagraphList & LyXText::ownerParagraphs() const
2067 return inset_owner->paragraphs;
2069 return bv_owner->buffer()->paragraphs;
2073 bool LyXText::isInInset() const
2075 // Sub-level has non-null bv owner and non-null inset owner.
2076 return inset_owner != 0 && bv_owner != 0;
2080 int defaultRowHeight()
2082 LyXFont const font(LyXFont::ALL_SANE);
2083 return int(font_metrics::maxAscent(font)
2084 + font_metrics::maxDescent(font) * 1.5);