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());
93 setCursorIntern(rowlist_.begin()->par(), 0);
94 selection.cursor = cursor;
100 // Gets the fully instantiated font at a given position in a paragraph
101 // Basically the same routine as Paragraph::getFont() in paragraph.C.
102 // The difference is that this one is used for displaying, and thus we
103 // are allowed to make cosmetic improvements. For instance make footnotes
105 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
109 LyXLayout_ptr const & layout = pit->layout();
111 BufferParams const & params = bv()->buffer()->params;
113 // We specialize the 95% common case:
114 if (!pit->getDepth()) {
115 if (layout->labeltype == LABEL_MANUAL
116 && pos < pit->beginningOfBody()) {
118 LyXFont f = pit->getFontSettings(params, pos);
120 pit->inInset()->getDrawFont(f);
121 return f.realize(layout->reslabelfont);
123 LyXFont f = pit->getFontSettings(params, pos);
125 pit->inInset()->getDrawFont(f);
126 return f.realize(layout->resfont);
130 // The uncommon case need not be optimized as much
134 if (pos < pit->beginningOfBody()) {
136 layoutfont = layout->labelfont;
139 layoutfont = layout->font;
142 LyXFont tmpfont = pit->getFontSettings(params, pos);
143 tmpfont.realize(layoutfont);
146 pit->inInset()->getDrawFont(tmpfont);
148 // Realize with the fonts of lesser depth.
149 tmpfont.realize(outerFont(pit, ownerParagraphs()));
150 tmpfont.realize(defaultfont_);
156 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
158 LyXLayout_ptr const & layout = pit->layout();
160 if (!pit->getDepth())
161 return layout->resfont;
163 LyXFont font = layout->font;
164 // Realize with the fonts of lesser depth.
165 font.realize(outerFont(pit, ownerParagraphs()));
166 font.realize(defaultfont_);
172 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
174 LyXLayout_ptr const & layout = pit->layout();
176 if (!pit->getDepth())
177 return layout->reslabelfont;
179 LyXFont font = layout->labelfont;
180 // Realize with the fonts of lesser depth.
181 font.realize(outerFont(pit, ownerParagraphs()));
182 font.realize(defaultfont_);
188 void LyXText::setCharFont(ParagraphList::iterator pit,
189 pos_type pos, LyXFont const & fnt,
192 BufferParams const & params = bv()->buffer()->params;
193 LyXFont font = getFont(pit, pos);
194 font.update(fnt, params.language, toggleall);
195 // Let the insets convert their font
196 if (pit->isInset(pos)) {
197 InsetOld * inset = pit->getInset(pos);
198 if (isEditableInset(inset)) {
199 static_cast<UpdatableInset *>(inset)
200 ->setFont(bv(), fnt, toggleall, true);
204 // Plug through to version below:
205 setCharFont(pit, pos, font);
209 void LyXText::setCharFont(
210 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
213 LyXLayout_ptr const & layout = pit->layout();
215 // Get concrete layout font to reduce against
218 if (pos < pit->beginningOfBody())
219 layoutfont = layout->labelfont;
221 layoutfont = layout->font;
223 // Realize against environment font information
224 if (pit->getDepth()) {
225 ParagraphList::iterator tp = pit;
226 while (!layoutfont.resolved() &&
227 tp != ownerParagraphs().end() &&
229 tp = outerHook(tp, ownerParagraphs());
230 if (tp != ownerParagraphs().end())
231 layoutfont.realize(tp->layout()->font);
235 layoutfont.realize(defaultfont_);
237 // Now, reduce font against full layout font
238 font.reduce(layoutfont);
240 pit->setFont(pos, font);
244 // removes the row and reset the touched counters
245 void LyXText::removeRow(RowList::iterator rit)
247 if (anchor_row_ == rit) {
248 if (rit != rows().begin()) {
249 anchor_row_ = boost::prior(rit);
250 anchor_row_offset_ += anchor_row_->height();
252 anchor_row_ = boost::next(rit);
253 anchor_row_offset_ -= rit->height();
257 // the text becomes smaller
258 height -= rit->height();
264 // remove all following rows of the paragraph of the specified row.
265 void LyXText::removeParagraph(RowList::iterator rit)
267 ParagraphList::iterator tmppit = rit->par();
270 while (rit != rows().end() && rit->par() == tmppit) {
271 RowList::iterator tmprit = boost::next(rit);
278 void LyXText::insertParagraph(ParagraphList::iterator pit,
279 RowList::iterator rit)
281 // insert a new row, starting at position 0
282 rit = rowlist_.insert(rit, Row(pit, 0));
284 // and now append the whole paragraph before the new row
285 Assert(rit != rowlist_.end());
287 pos_type const last = rit->par()->size();
291 pos_type z = rowBreakPoint(*rit);
293 RowList::iterator tmprow = rit;
297 Row newrow(rit->par(), z);
298 rit = rowlist_.insert(boost::next(rit), newrow);
303 // Set the dimensions of the row
304 // fixed fill setting now by calling inset->update() in
305 // SingleWidth when needed!
306 tmprow->fill(fill(tmprow, workWidth()));
307 setHeightOfRow(tmprow);
313 InsetOld * LyXText::getInset() const
315 ParagraphList::iterator pit = cursor.par();
316 pos_type const pos = cursor.pos();
318 if (pos < pit->size() && pit->isInset(pos)) {
319 return pit->getInset(pos);
325 void LyXText::toggleInset()
327 InsetOld * inset = getInset();
328 // is there an editable inset at cursor position?
329 if (!isEditableInset(inset)) {
330 // No, try to see if we are inside a collapsable inset
331 if (inset_owner && inset_owner->owner()
332 && inset_owner->owner()->isOpen()) {
333 bv()->unlockInset(inset_owner->owner());
334 inset_owner->owner()->close(bv());
335 bv()->getLyXText()->cursorRight(bv());
339 //bv()->owner()->message(inset->editMessage());
341 // do we want to keep this?? (JMarc)
342 if (!isHighlyEditableInset(inset))
343 recordUndo(bv(), Undo::ATOMIC);
354 /* used in setlayout */
355 // Asger is not sure we want to do this...
356 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
359 LyXLayout_ptr const & layout = par.layout();
360 pos_type const psize = par.size();
363 for (pos_type pos = 0; pos < psize; ++pos) {
364 if (pos < par.beginningOfBody())
365 layoutfont = layout->labelfont;
367 layoutfont = layout->font;
369 LyXFont tmpfont = par.getFontSettings(params, pos);
370 tmpfont.reduce(layoutfont);
371 par.setFont(pos, tmpfont);
376 ParagraphList::iterator
377 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
378 LyXCursor & send_cur,
379 string const & layout)
381 ParagraphList::iterator endpit = boost::next(send_cur.par());
382 ParagraphList::iterator undoendpit = endpit;
383 ParagraphList::iterator pars_end = ownerParagraphs().end();
385 if (endpit != pars_end && endpit->getDepth()) {
386 while (endpit != pars_end && endpit->getDepth()) {
390 } else if (endpit != pars_end) {
391 // because of parindents etc.
395 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
397 // ok we have a selection. This is always between sstart_cur
398 // and sel_end cursor
400 ParagraphList::iterator pit = sstart_cur.par();
401 ParagraphList::iterator epit = boost::next(send_cur.par());
403 LyXLayout_ptr const & lyxlayout =
404 bv()->buffer()->params.getLyXTextClass()[layout];
407 pit->applyLayout(lyxlayout);
408 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
409 pit->params().spaceTop(lyxlayout->fill_top ?
410 VSpace(VSpace::VFILL)
411 : VSpace(VSpace::NONE));
412 pit->params().spaceBottom(lyxlayout->fill_bottom ?
413 VSpace(VSpace::VFILL)
414 : VSpace(VSpace::NONE));
415 if (lyxlayout->margintype == MARGIN_MANUAL)
416 pit->setLabelWidthString(lyxlayout->labelstring());
419 } while (pit != epit);
425 // set layout over selection and make a total rebreak of those paragraphs
426 void LyXText::setLayout(string const & layout)
428 LyXCursor tmpcursor = cursor; // store the current cursor
430 // if there is no selection just set the layout
431 // of the current paragraph
432 if (!selection.set()) {
433 selection.start = cursor; // dummy selection
434 selection.end = cursor;
437 // special handling of new environment insets
438 BufferParams const & params = bv()->buffer()->params;
439 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
440 if (lyxlayout->is_environment) {
441 // move everything in a new environment inset
442 lyxerr << "setting layout " << layout << endl;
443 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
444 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
445 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
446 InsetOld * inset = new InsetEnvironment(params, layout);
447 if (bv()->insertInset(inset)) {
449 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
456 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
457 selection.end, layout);
458 redoParagraphs(selection.start.par(), endpit);
460 // we have to reset the selection, because the
461 // geometry could have changed
462 setCursor(selection.start.par(), selection.start.pos(), false);
463 selection.cursor = cursor;
464 setCursor(selection.end.par(), selection.end.pos(), false);
468 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
472 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
474 ParagraphList::iterator pit = cursor.par();
475 ParagraphList::iterator end = cursor.par();
476 ParagraphList::iterator start = pit;
478 if (selection.set()) {
479 pit = selection.start.par();
480 end = selection.end.par();
484 ParagraphList::iterator pastend = boost::next(end);
487 recordUndo(bv(), Undo::ATOMIC, start, end);
489 bool changed = false;
491 int prev_after_depth = 0;
492 #warning parlist ... could be nicer ?
493 if (start != ownerParagraphs().begin()) {
494 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
498 int const depth = pit->params().depth();
499 if (type == bv_funcs::INC_DEPTH) {
500 if (depth < prev_after_depth
501 && pit->layout()->labeltype != LABEL_BIBLIO) {
504 pit->params().depth(depth + 1);
509 pit->params().depth(depth - 1);
512 prev_after_depth = pit->getMaxDepthAfter();
524 redoParagraphs(start, pastend);
526 // We need to actually move the text->cursor. I don't
527 // understand why ...
528 LyXCursor tmpcursor = cursor;
530 // we have to reset the visual selection because the
531 // geometry could have changed
532 if (selection.set()) {
533 setCursor(selection.start.par(), selection.start.pos());
534 selection.cursor = cursor;
535 setCursor(selection.end.par(), selection.end.pos());
538 // this handles the counter labels, and also fixes up
539 // depth values for follow-on (child) paragraphs
543 setCursor(tmpcursor.par(), tmpcursor.pos());
549 // set font over selection and make a total rebreak of those paragraphs
550 void LyXText::setFont(LyXFont const & font, bool toggleall)
552 // if there is no selection just set the current_font
553 if (!selection.set()) {
554 // Determine basis font
556 if (cursor.pos() < cursor.par()->beginningOfBody()) {
557 layoutfont = getLabelFont(cursor.par());
559 layoutfont = getLayoutFont(cursor.par());
561 // Update current font
562 real_current_font.update(font,
563 bv()->buffer()->params.language,
566 // Reduce to implicit settings
567 current_font = real_current_font;
568 current_font.reduce(layoutfont);
569 // And resolve it completely
570 real_current_font.realize(layoutfont);
575 LyXCursor tmpcursor = cursor; // store the current cursor
577 // ok we have a selection. This is always between sel_start_cursor
578 // and sel_end cursor
580 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
582 cursor = selection.start;
583 while (cursor.par() != selection.end.par() ||
584 cursor.pos() < selection.end.pos())
586 if (cursor.pos() < cursor.par()->size()) {
587 // an open footnote should behave like a closed one
588 setCharFont(cursor.par(), cursor.pos(),
590 cursor.pos(cursor.pos() + 1);
593 cursor.par(boost::next(cursor.par()));
598 redoParagraph(selection.start.par());
600 // we have to reset the selection, because the
601 // geometry could have changed, but we keep
602 // it for user convenience
603 setCursor(selection.start.par(), selection.start.pos());
604 selection.cursor = cursor;
605 setCursor(selection.end.par(), selection.end.pos());
607 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
608 tmpcursor.boundary());
612 RowList::iterator LyXText::firstRow(ParagraphList::iterator pit)
614 RowList::iterator rit;
615 for (rit = rows().begin(); rit != rows().end(); ++rit)
616 if (rit->par() == pit)
622 // rebreaks all paragraphs between the specified pars
623 // This function is needed after SetLayout and SetFont etc.
624 void LyXText::redoParagraphs(ParagraphList::iterator start,
625 ParagraphList::iterator end)
627 for ( ; start != end; ++start)
628 redoParagraph(start);
632 void LyXText::redoParagraph(ParagraphList::iterator pit)
634 RowList::iterator rit = firstRow(pit);
636 // remove paragraph from rowlist
637 while (rit != rows().end() && rit->par() == pit) {
638 RowList::iterator rit2 = rit++;
642 // reinsert the paragraph
643 insertParagraph(pit, rit);
646 setHeightOfRow(rows().begin());
650 void LyXText::fullRebreak()
652 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
654 selection.cursor = cursor;
658 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
660 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
661 //Assert(mi.base.textwidth);
668 anchor_row_ = rows().end();
669 anchor_row_offset_ = 0;
671 ParagraphList::iterator pit = ownerParagraphs().begin();
672 ParagraphList::iterator end = ownerParagraphs().end();
674 for (; pit != end; ++pit) {
675 InsetList::iterator ii = pit->insetlist.begin();
676 InsetList::iterator iend = pit->insetlist.end();
677 for (; ii != iend; ++ii) {
680 #warning FIXME: pos != 0
681 m.base.font = getFont(pit, 0);
682 ii->inset->metrics(m, dim);
689 dim.asc = rows().begin()->ascent_of_text();
690 dim.des = height - dim.asc;
691 dim.wid = std::max(mi.base.textwidth, int(width));
695 // important for the screen
698 // the cursor set functions have a special mechanism. When they
699 // realize, that you left an empty paragraph, they will delete it.
700 // They also delete the corresponding row
702 // need the selection cursor:
703 void LyXText::setSelection()
705 TextCursor::setSelection();
710 void LyXText::clearSelection()
712 TextCursor::clearSelection();
714 // reset this in the bv_owner!
715 if (bv_owner && bv_owner->text)
716 bv_owner->text->xsel_cache.set(false);
720 void LyXText::cursorHome()
722 setCursor(cursor.par(), cursorRow()->pos());
726 void LyXText::cursorEnd()
728 if (cursor.par()->empty())
731 RowList::iterator rit = cursorRow();
732 RowList::iterator next_rit = boost::next(rit);
733 ParagraphList::iterator pit = rit->par();
734 pos_type last_pos = lastPos(*this, rit);
736 if (next_rit == rows().end() || next_rit->par() != pit) {
740 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
745 setCursor(pit, last_pos);
749 void LyXText::cursorTop()
751 setCursor(ownerParagraphs().begin(), 0);
755 void LyXText::cursorBottom()
757 ParagraphList::iterator lastpit =
758 boost::prior(ownerParagraphs().end());
759 setCursor(lastpit, lastpit->size());
763 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
765 // If the mask is completely neutral, tell user
766 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
767 // Could only happen with user style
768 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
772 // Try implicit word selection
773 // If there is a change in the language the implicit word selection
775 LyXCursor resetCursor = cursor;
776 bool implicitSelection = (font.language() == ignore_language
777 && font.number() == LyXFont::IGNORE)
778 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
781 setFont(font, toggleall);
783 // Implicit selections are cleared afterwards
784 //and cursor is set to the original position.
785 if (implicitSelection) {
787 cursor = resetCursor;
788 setCursor(cursor.par(), cursor.pos());
789 selection.cursor = cursor;
794 string LyXText::getStringToIndex()
796 // Try implicit word selection
797 // If there is a change in the language the implicit word selection
799 LyXCursor const reset_cursor = cursor;
800 bool const implicitSelection =
801 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
804 if (!selection.set())
805 bv()->owner()->message(_("Nothing to index!"));
806 else if (selection.start.par() != selection.end.par())
807 bv()->owner()->message(_("Cannot index more than one paragraph!"));
809 idxstring = selectionAsString(bv()->buffer(), false);
811 // Reset cursors to their original position.
812 cursor = reset_cursor;
813 setCursor(cursor.par(), cursor.pos());
814 selection.cursor = cursor;
816 // Clear the implicit selection.
817 if (implicitSelection)
824 // the DTP switches for paragraphs. LyX will store them in the first
825 // physical paragraph. When a paragraph is broken, the top settings rest,
826 // the bottom settings are given to the new one. So I can make sure,
827 // they do not duplicate themself and you cannnot make dirty things with
830 void LyXText::setParagraph(bool line_top, bool line_bottom,
831 bool pagebreak_top, bool pagebreak_bottom,
832 VSpace const & space_top,
833 VSpace const & space_bottom,
834 Spacing const & spacing,
836 string const & labelwidthstring,
839 LyXCursor tmpcursor = cursor;
840 if (!selection.set()) {
841 selection.start = cursor;
842 selection.end = cursor;
845 // make sure that the depth behind the selection are restored, too
846 ParagraphList::iterator endpit = boost::next(selection.end.par());
847 ParagraphList::iterator undoendpit = endpit;
848 ParagraphList::iterator pars_end = ownerParagraphs().end();
850 if (endpit != pars_end && endpit->getDepth()) {
851 while (endpit != pars_end && endpit->getDepth()) {
855 } else if (endpit != pars_end) {
856 // because of parindents etc.
860 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
861 boost::prior(undoendpit));
864 ParagraphList::iterator tmppit = selection.end.par();
866 while (tmppit != boost::prior(selection.start.par())) {
867 setCursor(tmppit, 0);
869 ParagraphList::iterator pit = cursor.par();
870 ParagraphParameters & params = pit->params();
872 params.lineTop(line_top);
873 params.lineBottom(line_bottom);
874 params.pagebreakTop(pagebreak_top);
875 params.pagebreakBottom(pagebreak_bottom);
876 params.spaceTop(space_top);
877 params.spaceBottom(space_bottom);
878 params.spacing(spacing);
879 // does the layout allow the new alignment?
880 LyXLayout_ptr const & layout = pit->layout();
882 if (align == LYX_ALIGN_LAYOUT)
883 align = layout->align;
884 if (align & layout->alignpossible) {
885 if (align == layout->align)
886 params.align(LYX_ALIGN_LAYOUT);
890 pit->setLabelWidthString(labelwidthstring);
891 params.noindent(noindent);
892 tmppit = boost::prior(pit);
895 redoParagraphs(selection.start.par(), endpit);
898 setCursor(selection.start.par(), selection.start.pos());
899 selection.cursor = cursor;
900 setCursor(selection.end.par(), selection.end.pos());
902 setCursor(tmpcursor.par(), tmpcursor.pos());
908 // set the counter of a paragraph. This includes the labels
909 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
911 LyXTextClass const & textclass = buf->params.getLyXTextClass();
912 LyXLayout_ptr const & layout = pit->layout();
914 if (pit != ownerParagraphs().begin()) {
916 pit->params().appendix(boost::prior(pit)->params().appendix());
917 if (!pit->params().appendix() &&
918 pit->params().startOfAppendix()) {
919 pit->params().appendix(true);
920 textclass.counters().reset();
922 pit->enumdepth = boost::prior(pit)->enumdepth;
923 pit->itemdepth = boost::prior(pit)->itemdepth;
925 pit->params().appendix(pit->params().startOfAppendix());
930 // Maybe we have to increment the enumeration depth.
931 // BUT, enumeration in a footnote is considered in isolation from its
932 // surrounding paragraph so don't increment if this is the
933 // first line of the footnote
934 // AND, bibliographies can't have their depth changed ie. they
935 // are always of depth 0
936 if (pit != ownerParagraphs().begin()
937 && boost::prior(pit)->getDepth() < pit->getDepth()
938 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
939 && pit->enumdepth < 3
940 && layout->labeltype != LABEL_BIBLIO) {
944 // Maybe we have to decrement the enumeration depth, see note above
945 if (pit != ownerParagraphs().begin()
946 && boost::prior(pit)->getDepth() > pit->getDepth()
947 && layout->labeltype != LABEL_BIBLIO) {
948 pit->enumdepth = depthHook(pit, ownerParagraphs(),
949 pit->getDepth())->enumdepth;
952 if (!pit->params().labelString().empty()) {
953 pit->params().labelString(string());
956 if (layout->margintype == MARGIN_MANUAL) {
957 if (pit->params().labelWidthString().empty())
958 pit->setLabelWidthString(layout->labelstring());
960 pit->setLabelWidthString(string());
963 // is it a layout that has an automatic label?
964 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
965 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
969 if (i >= 0 && i <= buf->params.secnumdepth) {
973 textclass.counters().step(layout->latexname());
975 // Is there a label? Useful for Chapter layout
976 if (!pit->params().appendix()) {
977 s << buf->B_(layout->labelstring());
979 s << buf->B_(layout->labelstring_appendix());
982 // Use of an integer is here less than elegant. For now.
983 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
984 if (!pit->params().appendix()) {
985 numbertype = "sectioning";
987 numbertype = "appendix";
988 if (pit->isRightToLeftPar(buf->params))
995 << textclass.counters()
996 .numberLabel(layout->latexname(),
997 numbertype, langtype, head);
999 pit->params().labelString(STRCONV(s.str()));
1001 // reset enum counters
1002 textclass.counters().reset("enum");
1003 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1004 textclass.counters().reset("enum");
1005 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1007 // Yes I know this is a really, really! bad solution
1009 string enumcounter("enum");
1011 switch (pit->enumdepth) {
1020 enumcounter += "iv";
1023 // not a valid enumdepth...
1027 textclass.counters().step(enumcounter);
1029 s << textclass.counters()
1030 .numberLabel(enumcounter, "enumeration");
1031 pit->params().labelString(STRCONV(s.str()));
1033 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1034 textclass.counters().step("bibitem");
1035 int number = textclass.counters().value("bibitem");
1036 if (pit->bibitem()) {
1037 pit->bibitem()->setCounter(number);
1038 pit->params().labelString(layout->labelstring());
1040 // In biblio should't be following counters but...
1042 string s = buf->B_(layout->labelstring());
1044 // the caption hack:
1045 if (layout->labeltype == LABEL_SENSITIVE) {
1046 ParagraphList::iterator end = ownerParagraphs().end();
1047 ParagraphList::iterator tmppit = pit;
1050 while (tmppit != end && tmppit->inInset()
1051 // the single '=' is intended below
1052 && (in = tmppit->inInset()->owner()))
1054 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1055 in->lyxCode() == InsetOld::WRAP_CODE) {
1059 tmppit = ownerParagraphs().begin();
1060 for ( ; tmppit != end; ++tmppit)
1061 if (&*tmppit == in->parOwner())
1069 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1070 type = static_cast<InsetFloat*>(in)->params().type;
1071 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1072 type = static_cast<InsetWrap*>(in)->params().type;
1076 Floating const & fl = textclass.floats().getType(type);
1078 textclass.counters().step(fl.type());
1080 // Doesn't work... yet.
1081 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1083 // par->SetLayout(0);
1084 // s = layout->labelstring;
1085 s = _("Senseless: ");
1088 pit->params().labelString(s);
1090 // reset the enumeration counter. They are always reset
1091 // when there is any other layout between
1092 // Just fall-through between the cases so that all
1093 // enum counters deeper than enumdepth is also reset.
1094 switch (pit->enumdepth) {
1096 textclass.counters().reset("enumi");
1098 textclass.counters().reset("enumii");
1100 textclass.counters().reset("enumiii");
1102 textclass.counters().reset("enumiv");
1108 // Updates all counters. Paragraphs with changed label string will be rebroken
1109 void LyXText::updateCounters()
1112 bv()->buffer()->params.getLyXTextClass().counters().reset();
1114 ParagraphList::iterator beg = ownerParagraphs().begin();
1115 ParagraphList::iterator end = ownerParagraphs().end();
1116 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1117 string const oldLabel = pit->params().labelString();
1119 size_t maxdepth = 0;
1121 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1123 if (pit->params().depth() > maxdepth)
1124 pit->params().depth(maxdepth);
1126 // setCounter can potentially change the labelString.
1127 setCounter(bv()->buffer(), pit);
1129 string const & newLabel = pit->params().labelString();
1131 if (oldLabel != newLabel)
1137 void LyXText::insertInset(InsetOld * inset)
1139 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1141 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1143 cursor.par()->insertInset(cursor.pos(), inset);
1144 // Just to rebreak and refresh correctly.
1145 // The character will not be inserted a second time
1146 insertChar(Paragraph::META_INSET);
1147 // If we enter a highly editable inset the cursor should be before
1148 // the inset. After an Undo LyX tries to call inset->edit(...)
1149 // and fails if the cursor is behind the inset and getInset
1150 // does not return the inset!
1151 if (isHighlyEditableInset(inset))
1157 void LyXText::cutSelection(bool doclear, bool realcut)
1159 // Stuff what we got on the clipboard. Even if there is no selection.
1161 // There is a problem with having the stuffing here in that the
1162 // larger the selection the slower LyX will get. This can be
1163 // solved by running the line below only when the selection has
1164 // finished. The solution used currently just works, to make it
1165 // faster we need to be more clever and probably also have more
1166 // calls to stuffClipboard. (Lgb)
1167 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1169 // This doesn't make sense, if there is no selection
1170 if (!selection.set())
1173 // OK, we have a selection. This is always between selection.start
1174 // and selection.end
1176 // make sure that the depth behind the selection are restored, too
1177 ParagraphList::iterator endpit = boost::next(selection.end.par());
1178 ParagraphList::iterator undoendpit = endpit;
1179 ParagraphList::iterator pars_end = ownerParagraphs().end();
1181 if (endpit != pars_end && endpit->getDepth()) {
1182 while (endpit != pars_end && endpit->getDepth()) {
1184 undoendpit = endpit;
1186 } else if (endpit != pars_end) {
1187 // because of parindents etc.
1191 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1192 boost::prior(undoendpit));
1194 endpit = selection.end.par();
1195 int endpos = selection.end.pos();
1197 boost::tie(endpit, endpos) = realcut ?
1198 CutAndPaste::cutSelection(bv()->buffer()->params,
1200 selection.start.par(), endpit,
1201 selection.start.pos(), endpos,
1202 bv()->buffer()->params.textclass,
1204 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1206 selection.start.par(), endpit,
1207 selection.start.pos(), endpos,
1209 // sometimes necessary
1211 selection.start.par()->stripLeadingSpaces();
1213 redoParagraphs(selection.start.par(), boost::next(endpit));
1214 // cutSelection can invalidate the cursor so we need to set
1216 // we prefer the end for when tracking changes
1220 // need a valid cursor. (Lgb)
1223 setCursor(cursor.par(), cursor.pos());
1224 selection.cursor = cursor;
1229 void LyXText::copySelection()
1231 // stuff the selection onto the X clipboard, from an explicit copy request
1232 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1234 // this doesnt make sense, if there is no selection
1235 if (!selection.set())
1238 // ok we have a selection. This is always between selection.start
1239 // and sel_end cursor
1241 // copy behind a space if there is one
1242 while (selection.start.par()->size() > selection.start.pos()
1243 && selection.start.par()->isLineSeparator(selection.start.pos())
1244 && (selection.start.par() != selection.end.par()
1245 || selection.start.pos() < selection.end.pos()))
1246 selection.start.pos(selection.start.pos() + 1);
1248 CutAndPaste::copySelection(selection.start.par(),
1249 selection.end.par(),
1250 selection.start.pos(), selection.end.pos(),
1251 bv()->buffer()->params.textclass);
1255 void LyXText::pasteSelection(size_t sel_index)
1257 // this does not make sense, if there is nothing to paste
1258 if (!CutAndPaste::checkPastePossible())
1261 recordUndo(bv(), Undo::INSERT, cursor.par());
1263 ParagraphList::iterator endpit;
1268 boost::tie(ppp, endpit) =
1269 CutAndPaste::pasteSelection(*bv()->buffer(),
1271 cursor.par(), cursor.pos(),
1272 bv()->buffer()->params.textclass,
1274 bufferErrors(*bv()->buffer(), el);
1275 bv()->showErrorList(_("Paste"));
1277 redoParagraphs(cursor.par(), endpit);
1279 setCursor(cursor.par(), cursor.pos());
1282 selection.cursor = cursor;
1283 setCursor(ppp.first, ppp.second);
1289 void LyXText::setSelectionRange(lyx::pos_type length)
1294 selection.cursor = cursor;
1301 // simple replacing. The font of the first selected character is used
1302 void LyXText::replaceSelectionWithString(string const & str)
1304 recordUndo(bv(), Undo::ATOMIC);
1307 if (!selection.set()) { // create a dummy selection
1308 selection.end = cursor;
1309 selection.start = cursor;
1312 // Get font setting before we cut
1313 pos_type pos = selection.end.pos();
1314 LyXFont const font = selection.start.par()
1315 ->getFontSettings(bv()->buffer()->params,
1316 selection.start.pos());
1318 // Insert the new string
1319 string::const_iterator cit = str.begin();
1320 string::const_iterator end = str.end();
1321 for (; cit != end; ++cit) {
1322 selection.end.par()->insertChar(pos, (*cit), font);
1326 // Cut the selection
1327 cutSelection(true, false);
1333 // needed to insert the selection
1334 void LyXText::insertStringAsLines(string const & str)
1336 ParagraphList::iterator pit = cursor.par();
1337 pos_type pos = cursor.pos();
1338 ParagraphList::iterator endpit = boost::next(cursor.par());
1340 recordUndo(bv(), Undo::ATOMIC);
1342 // only to be sure, should not be neccessary
1345 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1347 redoParagraphs(cursor.par(), endpit);
1348 setCursor(cursor.par(), cursor.pos());
1349 selection.cursor = cursor;
1350 setCursor(pit, pos);
1355 // turns double-CR to single CR, others where converted into one
1356 // blank. Then InsertStringAsLines is called
1357 void LyXText::insertStringAsParagraphs(string const & str)
1359 string linestr(str);
1360 bool newline_inserted = false;
1361 string::size_type const siz = linestr.length();
1363 for (string::size_type i = 0; i < siz; ++i) {
1364 if (linestr[i] == '\n') {
1365 if (newline_inserted) {
1366 // we know that \r will be ignored by
1367 // InsertStringA. Of course, it is a dirty
1368 // trick, but it works...
1369 linestr[i - 1] = '\r';
1373 newline_inserted = true;
1375 } else if (IsPrintable(linestr[i])) {
1376 newline_inserted = false;
1379 insertStringAsLines(linestr);
1383 bool LyXText::setCursor(ParagraphList::iterator pit,
1385 bool setfont, bool boundary)
1387 LyXCursor old_cursor = cursor;
1388 setCursorIntern(pit, pos, setfont, boundary);
1389 return deleteEmptyParagraphMechanism(old_cursor);
1393 void LyXText::redoCursor()
1395 #warning maybe the same for selections?
1396 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1400 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1401 pos_type pos, bool boundary)
1403 Assert(pit != ownerParagraphs().end());
1407 cur.boundary(boundary);
1411 // get the cursor y position in text
1413 RowList::iterator row = getRow(pit, pos, y);
1414 RowList::iterator beg = rows().begin();
1416 RowList::iterator old_row = row;
1417 // if we are before the first char of this row and are still in the
1418 // same paragraph and there is a previous row then put the cursor on
1419 // the end of the previous row
1420 cur.iy(y + row->baseline());
1423 boost::prior(row)->par() == row->par() &&
1424 pos < pit->size() &&
1425 pit->getChar(pos) == Paragraph::META_INSET) {
1426 InsetOld * ins = pit->getInset(pos);
1427 if (ins && (ins->needFullRow() || ins->display())) {
1433 // y is now the beginning of the cursor row
1434 y += row->baseline();
1435 // y is now the cursor baseline
1438 pos_type last = lastPrintablePos(*this, old_row);
1440 // None of these should happen, but we're scaredy-cats
1441 if (pos > pit->size()) {
1442 lyxerr << "dont like 1 please report" << endl;
1445 } else if (pos > last + 1) {
1446 lyxerr << "dont like 2 please report" << endl;
1447 // This shouldn't happen.
1450 } else if (pos < row->pos()) {
1451 lyxerr << "dont like 3 please report" << endl;
1456 // now get the cursors x position
1457 float x = getCursorX(row, pos, last, boundary);
1460 if (old_row != row) {
1461 x = getCursorX(old_row, pos, last, boundary);
1465 /* We take out this for the time being because 1) the redraw code is not
1466 prepared to this yet and 2) because some good policy has yet to be decided
1467 while editting: for instance how to act on rows being created/deleted
1471 //if the cursor is in a visible row, anchor to it
1473 if (topy < y && y < topy + bv()->workHeight())
1479 float LyXText::getCursorX(RowList::iterator rit,
1480 pos_type pos, pos_type last, bool boundary) const
1482 pos_type cursor_vpos = 0;
1484 double fill_separator;
1486 double fill_label_hfill;
1487 // This call HAS to be here because of the BidiTables!!!
1488 prepareToPrint(rit, x, fill_separator, fill_hfill,
1491 ParagraphList::iterator rit_par = rit->par();
1492 pos_type const rit_pos = rit->pos();
1495 cursor_vpos = rit_pos;
1496 else if (pos > last && !boundary)
1497 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1498 ? rit_pos : last + 1;
1499 else if (pos > rit_pos && (pos > last || boundary))
1500 // Place cursor after char at (logical) position pos - 1
1501 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1502 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1504 // Place cursor before char at (logical) position pos
1505 cursor_vpos = (bidi_level(pos) % 2 == 0)
1506 ? log2vis(pos) : log2vis(pos) + 1;
1508 pos_type body_pos = rit_par->beginningOfBody();
1510 (body_pos - 1 > last || !rit_par->isLineSeparator(body_pos - 1)))
1513 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1514 pos_type pos = vis2log(vpos);
1515 if (body_pos > 0 && pos == body_pos - 1) {
1516 x += fill_label_hfill +
1517 font_metrics::width(
1518 rit_par->layout()->labelsep, getLabelFont(rit_par));
1519 if (rit_par->isLineSeparator(body_pos - 1))
1520 x -= singleWidth(rit_par, body_pos - 1);
1523 if (hfillExpansion(*this, rit, pos)) {
1524 x += singleWidth(rit_par, pos);
1525 if (pos >= body_pos)
1528 x += fill_label_hfill;
1529 } else if (rit_par->isSeparator(pos)) {
1530 x += singleWidth(rit_par, pos);
1531 if (pos >= body_pos)
1532 x += fill_separator;
1534 x += singleWidth(rit_par, pos);
1540 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1541 pos_type pos, bool setfont, bool boundary)
1543 setCursor(cursor, pit, pos, boundary);
1549 void LyXText::setCurrentFont()
1551 pos_type pos = cursor.pos();
1552 ParagraphList::iterator pit = cursor.par();
1554 if (cursor.boundary() && pos > 0)
1558 if (pos == pit->size())
1560 else // potentional bug... BUG (Lgb)
1561 if (pit->isSeparator(pos)) {
1562 if (pos > cursorRow()->pos() &&
1563 bidi_level(pos) % 2 ==
1564 bidi_level(pos - 1) % 2)
1566 else if (pos + 1 < pit->size())
1571 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1572 real_current_font = getFont(pit, pos);
1574 if (cursor.pos() == pit->size() &&
1575 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1576 !cursor.boundary()) {
1577 Language const * lang =
1578 pit->getParLanguage(bv()->buffer()->params);
1579 current_font.setLanguage(lang);
1580 current_font.setNumber(LyXFont::OFF);
1581 real_current_font.setLanguage(lang);
1582 real_current_font.setNumber(LyXFont::OFF);
1587 // returns the column near the specified x-coordinate of the row
1588 // x is set to the real beginning of this column
1590 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1593 double fill_separator;
1595 double fill_label_hfill;
1597 prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1599 pos_type vc = rit->pos();
1600 pos_type last = lastPrintablePos(*this, rit);
1603 ParagraphList::iterator rit_par = rit->par();
1604 LyXLayout_ptr const & layout = rit->par()->layout();
1606 bool left_side = false;
1608 pos_type body_pos = rit_par->beginningOfBody();
1609 double last_tmpx = tmpx;
1612 (body_pos - 1 > last ||
1613 !rit_par->isLineSeparator(body_pos - 1)))
1616 // check for empty row
1617 if (!rit_par->size()) {
1622 while (vc <= last && tmpx <= x) {
1625 if (body_pos > 0 && c == body_pos - 1) {
1626 tmpx += fill_label_hfill +
1627 font_metrics::width(layout->labelsep, getLabelFont(rit_par));
1628 if (rit_par->isLineSeparator(body_pos - 1))
1629 tmpx -= singleWidth(rit_par, body_pos - 1);
1632 if (hfillExpansion(*this, rit, c)) {
1633 tmpx += singleWidth(rit_par, c);
1637 tmpx += fill_label_hfill;
1638 } else if (rit_par->isSeparator(c)) {
1639 tmpx += singleWidth(rit_par, c);
1641 tmpx += fill_separator;
1643 tmpx += singleWidth(rit_par, c);
1648 if ((tmpx + last_tmpx) / 2 > x) {
1653 if (vc > last + 1) // This shouldn't happen.
1657 // This (rtl_support test) is not needed, but gives
1658 // some speedup if rtl_support=false
1659 RowList::iterator next_rit = boost::next(rit);
1661 bool const lastrow = lyxrc.rtl_support &&
1662 (next_rit == rowlist_.end() ||
1663 next_rit->par() != rit_par);
1665 // If lastrow is false, we don't need to compute
1666 // the value of rtl.
1667 bool const rtl = (lastrow)
1668 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1671 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1672 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1674 else if (vc == rit->pos()) {
1676 if (bidi_level(c) % 2 == 1)
1679 c = vis2log(vc - 1);
1680 bool const rtl = (bidi_level(c) % 2 == 1);
1681 if (left_side == rtl) {
1683 boundary = isBoundary(bv()->buffer(), *rit_par, c);
1687 if (rit->pos() <= last && c > last
1688 && rit_par->isNewline(last)) {
1689 if (bidi_level(last) % 2 == 0)
1690 tmpx -= singleWidth(rit_par, last);
1692 tmpx += singleWidth(rit_par, last);
1702 void LyXText::setCursorFromCoordinates(int x, int y)
1704 //LyXCursor old_cursor = cursor;
1705 setCursorFromCoordinates(cursor, x, y);
1707 #warning DEPM disabled, otherwise crash when entering new table
1708 //deleteEmptyParagraphMechanism(old_cursor);
1715 * return true if the cursor given is at the end of a row,
1716 * and the next row is filled by an inset that spans an entire
1719 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1721 RowList::iterator row = lt.getRow(cur);
1722 if (boost::next(row) == lt.rows().end())
1725 Row const & next = *boost::next(row);
1727 if (next.pos() != cur.pos() || next.par() != cur.par())
1730 if (cur.pos() == cur.par()->size()
1731 || !cur.par()->isInset(cur.pos()))
1734 InsetOld const * inset = cur.par()->getInset(cur.pos());
1735 if (inset->needFullRow() || inset->display())
1743 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1745 // Get the row first.
1747 RowList::iterator row = getRowNearY(y);
1749 pos_type const column = getColumnNearX(row, x, bound);
1750 cur.par(row->par());
1751 cur.pos(row->pos() + column);
1753 cur.y(y + row->baseline());
1755 if (beforeFullRowInset(*this, cur)) {
1756 pos_type const last = lastPrintablePos(*this, row);
1757 RowList::iterator next_row = boost::next(row);
1759 float x = getCursorX(next_row, cur.pos(), last, bound);
1761 cur.iy(y + row->height() + next_row->baseline());
1766 cur.boundary(bound);
1770 void LyXText::cursorLeft(bool internal)
1772 if (cursor.pos() > 0) {
1773 bool boundary = cursor.boundary();
1774 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1775 if (!internal && !boundary &&
1776 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1777 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1778 } else if (cursor.par() != ownerParagraphs().begin()) {
1779 // steps into the paragraph above
1780 ParagraphList::iterator pit = boost::prior(cursor.par());
1781 setCursor(pit, pit->size());
1786 void LyXText::cursorRight(bool internal)
1788 bool const at_end = (cursor.pos() == cursor.par()->size());
1789 bool const at_newline = !at_end &&
1790 cursor.par()->isNewline(cursor.pos());
1792 if (!internal && cursor.boundary() && !at_newline)
1793 setCursor(cursor.par(), cursor.pos(), true, false);
1795 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1797 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1798 setCursor(cursor.par(), cursor.pos(), true, true);
1799 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1800 setCursor(boost::next(cursor.par()), 0);
1804 void LyXText::cursorUp(bool selecting)
1807 int x = cursor.x_fix();
1808 int y = cursor.y() - cursorRow()->baseline() - 1;
1809 setCursorFromCoordinates(x, y);
1812 int y1 = cursor.iy() - topy;
1815 InsetOld * inset_hit = checkInsetHit(x, y1);
1816 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1817 inset_hit->localDispatch(
1818 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1822 setCursorFromCoordinates(bv(), cursor.x_fix(),
1823 cursor.y() - cursorRow()->baseline() - 1);
1828 void LyXText::cursorDown(bool selecting)
1831 int x = cursor.x_fix();
1832 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1833 setCursorFromCoordinates(x, y);
1834 if (!selecting && cursorRow() == cursorIRow()) {
1836 int y1 = cursor.iy() - topy;
1839 InsetOld * inset_hit = checkInsetHit(x, y1);
1840 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1841 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1842 inset_hit->localDispatch(cmd);
1846 setCursorFromCoordinates(bv(), cursor.x_fix(),
1847 cursor.y() - cursorRow()->baseline()
1848 + cursorRow()->height() + 1);
1853 void LyXText::cursorUpParagraph()
1855 if (cursor.pos() > 0)
1856 setCursor(cursor.par(), 0);
1857 else if (cursor.par() != ownerParagraphs().begin())
1858 setCursor(boost::prior(cursor.par()), 0);
1862 void LyXText::cursorDownParagraph()
1864 ParagraphList::iterator par = cursor.par();
1865 ParagraphList::iterator next_par = boost::next(par);
1867 if (next_par != ownerParagraphs().end())
1868 setCursor(next_par, 0);
1870 setCursor(par, par->size());
1874 // fix the cursor `cur' after a characters has been deleted at `where'
1875 // position. Called by deleteEmptyParagraphMechanism
1876 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1878 // if cursor is not in the paragraph where the delete occured,
1880 if (cur.par() != where.par())
1883 // if cursor position is after the place where the delete occured,
1885 if (cur.pos() > where.pos())
1886 cur.pos(cur.pos()-1);
1888 // check also if we don't want to set the cursor on a spot behind the
1889 // pagragraph because we erased the last character.
1890 if (cur.pos() > cur.par()->size())
1891 cur.pos(cur.par()->size());
1893 // recompute row et al. for this cursor
1894 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1898 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1900 // Would be wrong to delete anything if we have a selection.
1901 if (selection.set())
1904 // We allow all kinds of "mumbo-jumbo" when freespacing.
1905 if (old_cursor.par()->isFreeSpacing())
1908 /* Ok I'll put some comments here about what is missing.
1909 I have fixed BackSpace (and thus Delete) to not delete
1910 double-spaces automagically. I have also changed Cut,
1911 Copy and Paste to hopefully do some sensible things.
1912 There are still some small problems that can lead to
1913 double spaces stored in the document file or space at
1914 the beginning of paragraphs. This happens if you have
1915 the cursor betwenn to spaces and then save. Or if you
1916 cut and paste and the selection have a space at the
1917 beginning and then save right after the paste. I am
1918 sure none of these are very hard to fix, but I will
1919 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1920 that I can get some feedback. (Lgb)
1923 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1924 // delete the LineSeparator.
1927 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1928 // delete the LineSeparator.
1931 // If the pos around the old_cursor were spaces, delete one of them.
1932 if (old_cursor.par() != cursor.par()
1933 || old_cursor.pos() != cursor.pos()) {
1935 // Only if the cursor has really moved
1936 if (old_cursor.pos() > 0
1937 && old_cursor.pos() < old_cursor.par()->size()
1938 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1939 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1940 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1941 redoParagraph(old_cursor.par());
1945 #ifdef WITH_WARNINGS
1946 #warning This will not work anymore when we have multiple views of the same buffer
1947 // In this case, we will have to correct also the cursors held by
1948 // other bufferviews. It will probably be easier to do that in a more
1949 // automated way in LyXCursor code. (JMarc 26/09/2001)
1951 // correct all cursors held by the LyXText
1952 fixCursorAfterDelete(cursor, old_cursor);
1953 fixCursorAfterDelete(selection.cursor, old_cursor);
1954 fixCursorAfterDelete(selection.start, old_cursor);
1955 fixCursorAfterDelete(selection.end, old_cursor);
1960 // don't delete anything if this is the ONLY paragraph!
1961 if (ownerParagraphs().size() == 1)
1964 // Do not delete empty paragraphs with keepempty set.
1965 if (old_cursor.par()->allowEmpty())
1968 // only do our magic if we changed paragraph
1969 if (old_cursor.par() == cursor.par())
1972 // record if we have deleted a paragraph
1973 // we can't possibly have deleted a paragraph before this point
1974 bool deleted = false;
1976 if (old_cursor.par()->empty() ||
1977 (old_cursor.par()->size() == 1 &&
1978 old_cursor.par()->isLineSeparator(0))) {
1979 // ok, we will delete anything
1980 LyXCursor tmpcursor;
1984 bool selection_position_was_oldcursor_position = (
1985 selection.cursor.par() == old_cursor.par()
1986 && selection.cursor.pos() == old_cursor.pos());
1988 if (getRow(old_cursor) != rows().begin()) {
1989 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1991 cursor = old_cursor; // that undo can restore the right cursor position
1992 #warning FIXME. --end() iterator is usable here
1993 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1994 while (endpit != ownerParagraphs().end() &&
1995 endpit->getDepth()) {
1999 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2000 boost::prior(endpit));
2004 removeRow(getRow(old_cursor));
2006 ownerParagraphs().erase(old_cursor.par());
2008 /* Breakagain the next par. Needed because of
2009 * the parindent that can occur or dissappear.
2010 * The next row can change its height, if
2011 * there is another layout before */
2012 RowList::iterator tmprit = boost::next(prevrow);
2013 if (tmprit != rows().end()) {
2014 redoParagraph(tmprit->par());
2017 setHeightOfRow(prevrow);
2019 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2022 cursor = old_cursor; // that undo can restore the right cursor position
2023 #warning FIXME. --end() iterator is usable here
2024 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2025 while (endpit != ownerParagraphs().end() &&
2026 endpit->getDepth()) {
2030 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2034 removeRow(getRow(old_cursor));
2036 ownerParagraphs().erase(old_cursor.par());
2038 /* Breakagain the next par. Needed because of
2039 the parindent that can occur or dissappear.
2040 The next row can change its height, if
2041 there is another layout before */
2042 if (nextrow != rows().end()) {
2043 redoParagraph(nextrow->par());
2049 setCursorIntern(cursor.par(), cursor.pos());
2051 if (selection_position_was_oldcursor_position) {
2052 // correct selection
2053 selection.cursor = cursor;
2057 if (old_cursor.par()->stripLeadingSpaces()) {
2058 redoParagraph(old_cursor.par());
2060 setCursorIntern(cursor.par(), cursor.pos());
2061 selection.cursor = cursor;
2068 ParagraphList & LyXText::ownerParagraphs() const
2071 return inset_owner->paragraphs;
2073 return bv_owner->buffer()->paragraphs;
2077 bool LyXText::isInInset() const
2079 // Sub-level has non-null bv owner and non-null inset owner.
2080 return inset_owner != 0 && bv_owner != 0;
2084 int defaultRowHeight()
2086 LyXFont const font(LyXFont::ALL_SANE);
2087 return int(font_metrics::maxAscent(font)
2088 + font_metrics::maxDescent(font) * 1.5);