1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
31 #include "FloatList.h"
33 #include "ParagraphParameters.h"
35 #include "lyxrow_funcs.h"
36 #include "metricsinfo.h"
37 #include "paragraph_funcs.h"
39 #include "insets/insetbibitem.h"
40 #include "insets/insetenv.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
48 #include <boost/tuple/tuple.hpp>
52 using namespace lyx::support;
62 LyXText::LyXText(BufferView * bv)
63 : height(0), width(0), anchor_row_offset_(0),
64 inset_owner(0), the_locking_inset(0), bv_owner(bv)
66 anchor_row_ = rows().end();
70 LyXText::LyXText(BufferView * bv, InsetText * inset)
71 : height(0), width(0), anchor_row_offset_(0),
72 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
74 anchor_row_ = rows().end();
78 void LyXText::init(BufferView * bview)
86 anchor_row_ = rows().end();
87 anchor_row_offset_ = 0;
89 current_font = getFont(ownerParagraphs().begin(), 0);
91 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
92 setCursorIntern(ownerParagraphs().begin(), 0);
93 selection.cursor = cursor;
99 // Gets the fully instantiated font at a given position in a paragraph
100 // Basically the same routine as Paragraph::getFont() in paragraph.C.
101 // The difference is that this one is used for displaying, and thus we
102 // are allowed to make cosmetic improvements. For instance make footnotes
104 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
108 LyXLayout_ptr const & layout = pit->layout();
110 BufferParams const & params = bv()->buffer()->params;
112 // We specialize the 95% common case:
113 if (!pit->getDepth()) {
114 if (layout->labeltype == LABEL_MANUAL
115 && pos < pit->beginningOfBody()) {
117 LyXFont f = pit->getFontSettings(params, pos);
119 pit->inInset()->getDrawFont(f);
120 return f.realize(layout->reslabelfont);
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->resfont);
129 // The uncommon case need not be optimized as much
133 if (pos < pit->beginningOfBody()) {
135 layoutfont = layout->labelfont;
138 layoutfont = layout->font;
141 LyXFont tmpfont = pit->getFontSettings(params, pos);
142 tmpfont.realize(layoutfont);
145 pit->inInset()->getDrawFont(tmpfont);
147 // Realize with the fonts of lesser depth.
148 tmpfont.realize(outerFont(pit, ownerParagraphs()));
149 tmpfont.realize(defaultfont_);
155 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
157 LyXLayout_ptr const & layout = pit->layout();
159 if (!pit->getDepth())
160 return layout->resfont;
162 LyXFont font = layout->font;
163 // Realize with the fonts of lesser depth.
164 font.realize(outerFont(pit, ownerParagraphs()));
165 font.realize(defaultfont_);
171 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
173 LyXLayout_ptr const & layout = pit->layout();
175 if (!pit->getDepth())
176 return layout->reslabelfont;
178 LyXFont font = layout->labelfont;
179 // Realize with the fonts of lesser depth.
180 font.realize(outerFont(pit, ownerParagraphs()));
181 font.realize(defaultfont_);
187 void LyXText::setCharFont(ParagraphList::iterator pit,
188 pos_type pos, LyXFont const & fnt,
191 BufferParams const & params = bv()->buffer()->params;
192 LyXFont font = getFont(pit, pos);
193 font.update(fnt, params.language, toggleall);
194 // Let the insets convert their font
195 if (pit->isInset(pos)) {
196 InsetOld * inset = pit->getInset(pos);
197 if (isEditableInset(inset)) {
198 static_cast<UpdatableInset *>(inset)
199 ->setFont(bv(), fnt, toggleall, true);
203 // Plug through to version below:
204 setCharFont(pit, pos, font);
208 void LyXText::setCharFont(
209 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
212 LyXLayout_ptr const & layout = pit->layout();
214 // Get concrete layout font to reduce against
217 if (pos < pit->beginningOfBody())
218 layoutfont = layout->labelfont;
220 layoutfont = layout->font;
222 // Realize against environment font information
223 if (pit->getDepth()) {
224 ParagraphList::iterator tp = pit;
225 while (!layoutfont.resolved() &&
226 tp != ownerParagraphs().end() &&
228 tp = outerHook(tp, ownerParagraphs());
229 if (tp != ownerParagraphs().end())
230 layoutfont.realize(tp->layout()->font);
234 layoutfont.realize(defaultfont_);
236 // Now, reduce font against full layout font
237 font.reduce(layoutfont);
239 pit->setFont(pos, font);
243 // removes the row and reset the touched counters
244 void LyXText::removeRow(RowList::iterator rit)
246 if (anchor_row_ == rit) {
247 if (rit != rows().begin()) {
248 anchor_row_ = boost::prior(rit);
249 anchor_row_offset_ += anchor_row_->height();
251 anchor_row_ = boost::next(rit);
252 anchor_row_offset_ -= rit->height();
256 // the text becomes smaller
257 height -= rit->height();
263 // remove all following rows of the paragraph of the specified row.
264 void LyXText::removeParagraph(RowList::iterator rit)
266 ParagraphList::iterator pit = getPar(rit);
267 RowList::iterator end = endRow(pit);
269 for (++rit; rit != end; ) {
270 RowList::iterator rit2 = boost::next(rit);
277 void LyXText::insertParagraph(ParagraphList::iterator pit,
278 RowList::iterator rit)
280 // insert a new row, starting at position 0
281 rit = rowlist_.insert(rit, Row(0));
283 // and now append the whole paragraph before the new row
285 pos_type const last = pit->size();
289 pos_type z = rowBreakPoint(pit, *rit);
291 RowList::iterator tmprow = rit;
295 rit = rowlist_.insert(boost::next(rit), Row(z));
300 // Set the dimensions of the row
301 // fixed fill setting now by calling inset->update() in
302 // singleWidth when needed!
303 tmprow->fill(fill(pit, tmprow, workWidth()));
304 setHeightOfRow(pit, tmprow);
310 InsetOld * LyXText::getInset() const
312 ParagraphList::iterator pit = cursor.par();
313 pos_type const pos = cursor.pos();
315 if (pos < pit->size() && pit->isInset(pos)) {
316 return pit->getInset(pos);
322 void LyXText::toggleInset()
324 InsetOld * inset = getInset();
325 // is there an editable inset at cursor position?
326 if (!isEditableInset(inset)) {
327 // No, try to see if we are inside a collapsable inset
328 if (inset_owner && inset_owner->owner()
329 && inset_owner->owner()->isOpen()) {
330 bv()->unlockInset(inset_owner->owner());
331 inset_owner->owner()->close(bv());
332 bv()->getLyXText()->cursorRight(bv());
336 //bv()->owner()->message(inset->editMessage());
338 // do we want to keep this?? (JMarc)
339 if (!isHighlyEditableInset(inset))
340 recordUndo(bv(), Undo::ATOMIC);
351 /* used in setlayout */
352 // Asger is not sure we want to do this...
353 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
356 LyXLayout_ptr const & layout = par.layout();
357 pos_type const psize = par.size();
360 for (pos_type pos = 0; pos < psize; ++pos) {
361 if (pos < par.beginningOfBody())
362 layoutfont = layout->labelfont;
364 layoutfont = layout->font;
366 LyXFont tmpfont = par.getFontSettings(params, pos);
367 tmpfont.reduce(layoutfont);
368 par.setFont(pos, tmpfont);
373 ParagraphList::iterator
374 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
375 LyXCursor & send_cur,
376 string const & layout)
378 ParagraphList::iterator endpit = boost::next(send_cur.par());
379 ParagraphList::iterator undoendpit = endpit;
380 ParagraphList::iterator pars_end = ownerParagraphs().end();
382 if (endpit != pars_end && endpit->getDepth()) {
383 while (endpit != pars_end && endpit->getDepth()) {
387 } else if (endpit != pars_end) {
388 // because of parindents etc.
392 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
394 // ok we have a selection. This is always between sstart_cur
395 // and sel_end cursor
397 ParagraphList::iterator pit = sstart_cur.par();
398 ParagraphList::iterator epit = boost::next(send_cur.par());
400 LyXLayout_ptr const & lyxlayout =
401 bv()->buffer()->params.getLyXTextClass()[layout];
404 pit->applyLayout(lyxlayout);
405 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
406 pit->params().spaceTop(lyxlayout->fill_top ?
407 VSpace(VSpace::VFILL)
408 : VSpace(VSpace::NONE));
409 pit->params().spaceBottom(lyxlayout->fill_bottom ?
410 VSpace(VSpace::VFILL)
411 : VSpace(VSpace::NONE));
412 if (lyxlayout->margintype == MARGIN_MANUAL)
413 pit->setLabelWidthString(lyxlayout->labelstring());
416 } while (pit != epit);
422 // set layout over selection and make a total rebreak of those paragraphs
423 void LyXText::setLayout(string const & layout)
425 LyXCursor tmpcursor = cursor; // store the current cursor
427 // if there is no selection just set the layout
428 // of the current paragraph
429 if (!selection.set()) {
430 selection.start = cursor; // dummy selection
431 selection.end = cursor;
434 // special handling of new environment insets
435 BufferParams const & params = bv()->buffer()->params;
436 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
437 if (lyxlayout->is_environment) {
438 // move everything in a new environment inset
439 lyxerr << "setting layout " << layout << endl;
440 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
441 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
442 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
443 InsetOld * inset = new InsetEnvironment(params, layout);
444 if (bv()->insertInset(inset)) {
446 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
453 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
454 selection.end, layout);
455 redoParagraphs(selection.start.par(), endpit);
457 // we have to reset the selection, because the
458 // geometry could have changed
459 setCursor(selection.start.par(), selection.start.pos(), false);
460 selection.cursor = cursor;
461 setCursor(selection.end.par(), selection.end.pos(), false);
465 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
469 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
471 ParagraphList::iterator pit = cursor.par();
472 ParagraphList::iterator end = cursor.par();
473 ParagraphList::iterator start = pit;
475 if (selection.set()) {
476 pit = selection.start.par();
477 end = selection.end.par();
481 ParagraphList::iterator pastend = boost::next(end);
484 recordUndo(bv(), Undo::ATOMIC, start, end);
486 bool changed = false;
488 int prev_after_depth = 0;
489 #warning parlist ... could be nicer ?
490 if (start != ownerParagraphs().begin()) {
491 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
495 int const depth = pit->params().depth();
496 if (type == bv_funcs::INC_DEPTH) {
497 if (depth < prev_after_depth
498 && pit->layout()->labeltype != LABEL_BIBLIO) {
501 pit->params().depth(depth + 1);
506 pit->params().depth(depth - 1);
509 prev_after_depth = pit->getMaxDepthAfter();
521 redoParagraphs(start, pastend);
523 // We need to actually move the text->cursor. I don't
524 // understand why ...
525 LyXCursor tmpcursor = cursor;
527 // we have to reset the visual selection because the
528 // geometry could have changed
529 if (selection.set()) {
530 setCursor(selection.start.par(), selection.start.pos());
531 selection.cursor = cursor;
532 setCursor(selection.end.par(), selection.end.pos());
535 // this handles the counter labels, and also fixes up
536 // depth values for follow-on (child) paragraphs
540 setCursor(tmpcursor.par(), tmpcursor.pos());
546 // set font over selection and make a total rebreak of those paragraphs
547 void LyXText::setFont(LyXFont const & font, bool toggleall)
549 // if there is no selection just set the current_font
550 if (!selection.set()) {
551 // Determine basis font
553 if (cursor.pos() < cursor.par()->beginningOfBody()) {
554 layoutfont = getLabelFont(cursor.par());
556 layoutfont = getLayoutFont(cursor.par());
558 // Update current font
559 real_current_font.update(font,
560 bv()->buffer()->params.language,
563 // Reduce to implicit settings
564 current_font = real_current_font;
565 current_font.reduce(layoutfont);
566 // And resolve it completely
567 real_current_font.realize(layoutfont);
572 LyXCursor tmpcursor = cursor; // store the current cursor
574 // ok we have a selection. This is always between sel_start_cursor
575 // and sel_end cursor
577 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
579 cursor = selection.start;
580 while (cursor.par() != selection.end.par() ||
581 cursor.pos() < selection.end.pos())
583 if (cursor.pos() < cursor.par()->size()) {
584 // an open footnote should behave like a closed one
585 setCharFont(cursor.par(), cursor.pos(),
587 cursor.pos(cursor.pos() + 1);
590 cursor.par(boost::next(cursor.par()));
595 redoParagraph(selection.start.par());
597 // we have to reset the selection, because the
598 // geometry could have changed, but we keep
599 // it for user convenience
600 setCursor(selection.start.par(), selection.start.pos());
601 selection.cursor = cursor;
602 setCursor(selection.end.par(), selection.end.pos());
604 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
605 tmpcursor.boundary());
609 // rebreaks all paragraphs between the specified pars
610 // This function is needed after SetLayout and SetFont etc.
611 void LyXText::redoParagraphs(ParagraphList::iterator start,
612 ParagraphList::iterator end)
614 for ( ; start != end; ++start)
615 redoParagraph(start);
619 void LyXText::redoParagraph(ParagraphList::iterator pit)
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 RowList::iterator end = boost::next(rit);
736 ParagraphList::iterator pit = cursor.par();
737 pos_type last_pos = lastPos(*this, pit, rit);
739 if (next_rit == end) {
743 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
748 setCursor(pit, last_pos);
752 void LyXText::cursorTop()
754 setCursor(ownerParagraphs().begin(), 0);
758 void LyXText::cursorBottom()
760 ParagraphList::iterator lastpit =
761 boost::prior(ownerParagraphs().end());
762 setCursor(lastpit, lastpit->size());
766 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
768 // If the mask is completely neutral, tell user
769 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
770 // Could only happen with user style
771 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
775 // Try implicit word selection
776 // If there is a change in the language the implicit word selection
778 LyXCursor resetCursor = cursor;
779 bool implicitSelection = (font.language() == ignore_language
780 && font.number() == LyXFont::IGNORE)
781 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
784 setFont(font, toggleall);
786 // Implicit selections are cleared afterwards
787 //and cursor is set to the original position.
788 if (implicitSelection) {
790 cursor = resetCursor;
791 setCursor(cursor.par(), cursor.pos());
792 selection.cursor = cursor;
797 string LyXText::getStringToIndex()
799 // Try implicit word selection
800 // If there is a change in the language the implicit word selection
802 LyXCursor const reset_cursor = cursor;
803 bool const implicitSelection =
804 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
807 if (!selection.set())
808 bv()->owner()->message(_("Nothing to index!"));
809 else if (selection.start.par() != selection.end.par())
810 bv()->owner()->message(_("Cannot index more than one paragraph!"));
812 idxstring = selectionAsString(bv()->buffer(), false);
814 // Reset cursors to their original position.
815 cursor = reset_cursor;
816 setCursor(cursor.par(), cursor.pos());
817 selection.cursor = cursor;
819 // Clear the implicit selection.
820 if (implicitSelection)
827 // the DTP switches for paragraphs. LyX will store them in the first
828 // physical paragraph. When a paragraph is broken, the top settings rest,
829 // the bottom settings are given to the new one. So I can make sure,
830 // they do not duplicate themself and you cannnot make dirty things with
833 void LyXText::setParagraph(bool line_top, bool line_bottom,
834 bool pagebreak_top, bool pagebreak_bottom,
835 VSpace const & space_top,
836 VSpace const & space_bottom,
837 Spacing const & spacing,
839 string const & labelwidthstring,
842 LyXCursor tmpcursor = cursor;
843 if (!selection.set()) {
844 selection.start = cursor;
845 selection.end = cursor;
848 // make sure that the depth behind the selection are restored, too
849 ParagraphList::iterator endpit = boost::next(selection.end.par());
850 ParagraphList::iterator undoendpit = endpit;
851 ParagraphList::iterator pars_end = ownerParagraphs().end();
853 if (endpit != pars_end && endpit->getDepth()) {
854 while (endpit != pars_end && endpit->getDepth()) {
858 } else if (endpit != pars_end) {
859 // because of parindents etc.
863 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
864 boost::prior(undoendpit));
867 ParagraphList::iterator tmppit = selection.end.par();
869 while (tmppit != boost::prior(selection.start.par())) {
870 setCursor(tmppit, 0);
872 ParagraphList::iterator pit = cursor.par();
873 ParagraphParameters & params = pit->params();
875 params.lineTop(line_top);
876 params.lineBottom(line_bottom);
877 params.pagebreakTop(pagebreak_top);
878 params.pagebreakBottom(pagebreak_bottom);
879 params.spaceTop(space_top);
880 params.spaceBottom(space_bottom);
881 params.spacing(spacing);
882 // does the layout allow the new alignment?
883 LyXLayout_ptr const & layout = pit->layout();
885 if (align == LYX_ALIGN_LAYOUT)
886 align = layout->align;
887 if (align & layout->alignpossible) {
888 if (align == layout->align)
889 params.align(LYX_ALIGN_LAYOUT);
893 pit->setLabelWidthString(labelwidthstring);
894 params.noindent(noindent);
895 tmppit = boost::prior(pit);
898 redoParagraphs(selection.start.par(), endpit);
901 setCursor(selection.start.par(), selection.start.pos());
902 selection.cursor = cursor;
903 setCursor(selection.end.par(), selection.end.pos());
905 setCursor(tmpcursor.par(), tmpcursor.pos());
911 // set the counter of a paragraph. This includes the labels
912 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
914 LyXTextClass const & textclass = buf->params.getLyXTextClass();
915 LyXLayout_ptr const & layout = pit->layout();
917 if (pit != ownerParagraphs().begin()) {
919 pit->params().appendix(boost::prior(pit)->params().appendix());
920 if (!pit->params().appendix() &&
921 pit->params().startOfAppendix()) {
922 pit->params().appendix(true);
923 textclass.counters().reset();
925 pit->enumdepth = boost::prior(pit)->enumdepth;
926 pit->itemdepth = boost::prior(pit)->itemdepth;
928 pit->params().appendix(pit->params().startOfAppendix());
933 // Maybe we have to increment the enumeration depth.
934 // BUT, enumeration in a footnote is considered in isolation from its
935 // surrounding paragraph so don't increment if this is the
936 // first line of the footnote
937 // AND, bibliographies can't have their depth changed ie. they
938 // are always of depth 0
939 if (pit != ownerParagraphs().begin()
940 && boost::prior(pit)->getDepth() < pit->getDepth()
941 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
942 && pit->enumdepth < 3
943 && layout->labeltype != LABEL_BIBLIO) {
947 // Maybe we have to decrement the enumeration depth, see note above
948 if (pit != ownerParagraphs().begin()
949 && boost::prior(pit)->getDepth() > pit->getDepth()
950 && layout->labeltype != LABEL_BIBLIO) {
951 pit->enumdepth = depthHook(pit, ownerParagraphs(),
952 pit->getDepth())->enumdepth;
955 if (!pit->params().labelString().empty()) {
956 pit->params().labelString(string());
959 if (layout->margintype == MARGIN_MANUAL) {
960 if (pit->params().labelWidthString().empty())
961 pit->setLabelWidthString(layout->labelstring());
963 pit->setLabelWidthString(string());
966 // is it a layout that has an automatic label?
967 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
968 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
972 if (i >= 0 && i <= buf->params.secnumdepth) {
976 textclass.counters().step(layout->latexname());
978 // Is there a label? Useful for Chapter layout
979 if (!pit->params().appendix()) {
980 s << buf->B_(layout->labelstring());
982 s << buf->B_(layout->labelstring_appendix());
985 // Use of an integer is here less than elegant. For now.
986 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
987 if (!pit->params().appendix()) {
988 numbertype = "sectioning";
990 numbertype = "appendix";
991 if (pit->isRightToLeftPar(buf->params))
998 << textclass.counters()
999 .numberLabel(layout->latexname(),
1000 numbertype, langtype, head);
1002 pit->params().labelString(STRCONV(s.str()));
1004 // reset enum counters
1005 textclass.counters().reset("enum");
1006 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1007 textclass.counters().reset("enum");
1008 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1010 // Yes I know this is a really, really! bad solution
1012 string enumcounter("enum");
1014 switch (pit->enumdepth) {
1023 enumcounter += "iv";
1026 // not a valid enumdepth...
1030 textclass.counters().step(enumcounter);
1032 s << textclass.counters()
1033 .numberLabel(enumcounter, "enumeration");
1034 pit->params().labelString(STRCONV(s.str()));
1036 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1037 textclass.counters().step("bibitem");
1038 int number = textclass.counters().value("bibitem");
1039 if (pit->bibitem()) {
1040 pit->bibitem()->setCounter(number);
1041 pit->params().labelString(layout->labelstring());
1043 // In biblio should't be following counters but...
1045 string s = buf->B_(layout->labelstring());
1047 // the caption hack:
1048 if (layout->labeltype == LABEL_SENSITIVE) {
1049 ParagraphList::iterator end = ownerParagraphs().end();
1050 ParagraphList::iterator tmppit = pit;
1053 while (tmppit != end && tmppit->inInset()
1054 // the single '=' is intended below
1055 && (in = tmppit->inInset()->owner()))
1057 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1058 in->lyxCode() == InsetOld::WRAP_CODE) {
1062 tmppit = ownerParagraphs().begin();
1063 for ( ; tmppit != end; ++tmppit)
1064 if (&*tmppit == in->parOwner())
1072 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1073 type = static_cast<InsetFloat*>(in)->params().type;
1074 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1075 type = static_cast<InsetWrap*>(in)->params().type;
1079 Floating const & fl = textclass.floats().getType(type);
1081 textclass.counters().step(fl.type());
1083 // Doesn't work... yet.
1084 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1086 // par->SetLayout(0);
1087 // s = layout->labelstring;
1088 s = _("Senseless: ");
1091 pit->params().labelString(s);
1093 // reset the enumeration counter. They are always reset
1094 // when there is any other layout between
1095 // Just fall-through between the cases so that all
1096 // enum counters deeper than enumdepth is also reset.
1097 switch (pit->enumdepth) {
1099 textclass.counters().reset("enumi");
1101 textclass.counters().reset("enumii");
1103 textclass.counters().reset("enumiii");
1105 textclass.counters().reset("enumiv");
1111 // Updates all counters. Paragraphs with changed label string will be rebroken
1112 void LyXText::updateCounters()
1115 bv()->buffer()->params.getLyXTextClass().counters().reset();
1117 ParagraphList::iterator beg = ownerParagraphs().begin();
1118 ParagraphList::iterator end = ownerParagraphs().end();
1119 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1120 string const oldLabel = pit->params().labelString();
1122 size_t maxdepth = 0;
1124 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1126 if (pit->params().depth() > maxdepth)
1127 pit->params().depth(maxdepth);
1129 // setCounter can potentially change the labelString.
1130 setCounter(bv()->buffer(), pit);
1132 string const & newLabel = pit->params().labelString();
1134 if (oldLabel != newLabel)
1140 void LyXText::insertInset(InsetOld * inset)
1142 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1144 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1146 cursor.par()->insertInset(cursor.pos(), inset);
1147 // Just to rebreak and refresh correctly.
1148 // The character will not be inserted a second time
1149 insertChar(Paragraph::META_INSET);
1150 // If we enter a highly editable inset the cursor should be before
1151 // the inset. After an Undo LyX tries to call inset->edit(...)
1152 // and fails if the cursor is behind the inset and getInset
1153 // does not return the inset!
1154 if (isHighlyEditableInset(inset))
1160 void LyXText::cutSelection(bool doclear, bool realcut)
1162 // Stuff what we got on the clipboard. Even if there is no selection.
1164 // There is a problem with having the stuffing here in that the
1165 // larger the selection the slower LyX will get. This can be
1166 // solved by running the line below only when the selection has
1167 // finished. The solution used currently just works, to make it
1168 // faster we need to be more clever and probably also have more
1169 // calls to stuffClipboard. (Lgb)
1170 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1172 // This doesn't make sense, if there is no selection
1173 if (!selection.set())
1176 // OK, we have a selection. This is always between selection.start
1177 // and selection.end
1179 // make sure that the depth behind the selection are restored, too
1180 ParagraphList::iterator endpit = boost::next(selection.end.par());
1181 ParagraphList::iterator undoendpit = endpit;
1182 ParagraphList::iterator pars_end = ownerParagraphs().end();
1184 if (endpit != pars_end && endpit->getDepth()) {
1185 while (endpit != pars_end && endpit->getDepth()) {
1187 undoendpit = endpit;
1189 } else if (endpit != pars_end) {
1190 // because of parindents etc.
1194 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1195 boost::prior(undoendpit));
1197 endpit = selection.end.par();
1198 int endpos = selection.end.pos();
1200 boost::tie(endpit, endpos) = realcut ?
1201 CutAndPaste::cutSelection(bv()->buffer()->params,
1203 selection.start.par(), endpit,
1204 selection.start.pos(), endpos,
1205 bv()->buffer()->params.textclass,
1207 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1209 selection.start.par(), endpit,
1210 selection.start.pos(), endpos,
1212 // sometimes necessary
1214 selection.start.par()->stripLeadingSpaces();
1216 redoParagraphs(selection.start.par(), boost::next(endpit));
1217 // cutSelection can invalidate the cursor so we need to set
1219 // we prefer the end for when tracking changes
1223 // need a valid cursor. (Lgb)
1226 setCursor(cursor.par(), cursor.pos());
1227 selection.cursor = cursor;
1232 void LyXText::copySelection()
1234 // stuff the selection onto the X clipboard, from an explicit copy request
1235 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1237 // this doesnt make sense, if there is no selection
1238 if (!selection.set())
1241 // ok we have a selection. This is always between selection.start
1242 // and sel_end cursor
1244 // copy behind a space if there is one
1245 while (selection.start.par()->size() > selection.start.pos()
1246 && selection.start.par()->isLineSeparator(selection.start.pos())
1247 && (selection.start.par() != selection.end.par()
1248 || selection.start.pos() < selection.end.pos()))
1249 selection.start.pos(selection.start.pos() + 1);
1251 CutAndPaste::copySelection(selection.start.par(),
1252 selection.end.par(),
1253 selection.start.pos(), selection.end.pos(),
1254 bv()->buffer()->params.textclass);
1258 void LyXText::pasteSelection(size_t sel_index)
1260 // this does not make sense, if there is nothing to paste
1261 if (!CutAndPaste::checkPastePossible())
1264 recordUndo(bv(), Undo::INSERT, cursor.par());
1266 ParagraphList::iterator endpit;
1271 boost::tie(ppp, endpit) =
1272 CutAndPaste::pasteSelection(*bv()->buffer(),
1274 cursor.par(), cursor.pos(),
1275 bv()->buffer()->params.textclass,
1277 bufferErrors(*bv()->buffer(), el);
1278 bv()->showErrorList(_("Paste"));
1280 redoParagraphs(cursor.par(), endpit);
1282 setCursor(cursor.par(), cursor.pos());
1285 selection.cursor = cursor;
1286 setCursor(ppp.first, ppp.second);
1292 void LyXText::setSelectionRange(lyx::pos_type length)
1297 selection.cursor = cursor;
1304 // simple replacing. The font of the first selected character is used
1305 void LyXText::replaceSelectionWithString(string const & str)
1307 recordUndo(bv(), Undo::ATOMIC);
1310 if (!selection.set()) { // create a dummy selection
1311 selection.end = cursor;
1312 selection.start = cursor;
1315 // Get font setting before we cut
1316 pos_type pos = selection.end.pos();
1317 LyXFont const font = selection.start.par()
1318 ->getFontSettings(bv()->buffer()->params,
1319 selection.start.pos());
1321 // Insert the new string
1322 string::const_iterator cit = str.begin();
1323 string::const_iterator end = str.end();
1324 for (; cit != end; ++cit) {
1325 selection.end.par()->insertChar(pos, (*cit), font);
1329 // Cut the selection
1330 cutSelection(true, false);
1336 // needed to insert the selection
1337 void LyXText::insertStringAsLines(string const & str)
1339 ParagraphList::iterator pit = cursor.par();
1340 pos_type pos = cursor.pos();
1341 ParagraphList::iterator endpit = boost::next(cursor.par());
1343 recordUndo(bv(), Undo::ATOMIC);
1345 // only to be sure, should not be neccessary
1348 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1350 redoParagraphs(cursor.par(), endpit);
1351 setCursor(cursor.par(), cursor.pos());
1352 selection.cursor = cursor;
1353 setCursor(pit, pos);
1358 // turns double-CR to single CR, others where converted into one
1359 // blank. Then InsertStringAsLines is called
1360 void LyXText::insertStringAsParagraphs(string const & str)
1362 string linestr(str);
1363 bool newline_inserted = false;
1364 string::size_type const siz = linestr.length();
1366 for (string::size_type i = 0; i < siz; ++i) {
1367 if (linestr[i] == '\n') {
1368 if (newline_inserted) {
1369 // we know that \r will be ignored by
1370 // InsertStringA. Of course, it is a dirty
1371 // trick, but it works...
1372 linestr[i - 1] = '\r';
1376 newline_inserted = true;
1378 } else if (IsPrintable(linestr[i])) {
1379 newline_inserted = false;
1382 insertStringAsLines(linestr);
1386 bool LyXText::setCursor(ParagraphList::iterator pit,
1388 bool setfont, bool boundary)
1390 LyXCursor old_cursor = cursor;
1391 setCursorIntern(pit, pos, setfont, boundary);
1392 return deleteEmptyParagraphMechanism(old_cursor);
1396 void LyXText::redoCursor()
1398 #warning maybe the same for selections?
1399 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1403 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1404 pos_type pos, bool boundary)
1406 Assert(pit != ownerParagraphs().end());
1410 cur.boundary(boundary);
1414 // get the cursor y position in text
1416 RowList::iterator row = getRow(pit, pos, y);
1417 RowList::iterator beg = rows().begin();
1419 RowList::iterator old_row = row;
1420 // if we are before the first char of this row and are still in the
1421 // same paragraph and there is a previous row then put the cursor on
1422 // the end of the previous row
1423 cur.iy(y + row->baseline());
1426 getPar(boost::prior(row)) == getPar(row) &&
1427 pos < pit->size() &&
1428 pit->getChar(pos) == Paragraph::META_INSET) {
1429 InsetOld * ins = pit->getInset(pos);
1430 if (ins && (ins->needFullRow() || ins->display())) {
1436 // y is now the beginning of the cursor row
1437 y += row->baseline();
1438 // y is now the cursor baseline
1441 pos_type last = lastPrintablePos(*this, pit, old_row);
1443 // None of these should happen, but we're scaredy-cats
1444 if (pos > pit->size()) {
1445 lyxerr << "dont like 1 please report" << endl;
1448 } else if (pos > last + 1) {
1449 lyxerr << "dont like 2 please report" << endl;
1450 // This shouldn't happen.
1453 } else if (pos < row->pos()) {
1454 lyxerr << "dont like 3 please report" << endl;
1459 // now get the cursors x position
1460 float x = getCursorX(pit, row, pos, last, boundary);
1463 if (old_row != row) {
1464 x = getCursorX(pit, old_row, pos, last, boundary);
1468 /* We take out this for the time being because 1) the redraw code is not
1469 prepared to this yet and 2) because some good policy has yet to be decided
1470 while editting: for instance how to act on rows being created/deleted
1474 //if the cursor is in a visible row, anchor to it
1476 if (topy < y && y < topy + bv()->workHeight())
1482 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1483 pos_type pos, pos_type last, bool boundary) const
1485 pos_type cursor_vpos = 0;
1487 double fill_separator;
1489 double fill_label_hfill;
1490 // This call HAS to be here because of the BidiTables!!!
1491 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1494 pos_type const rit_pos = rit->pos();
1497 cursor_vpos = rit_pos;
1498 else if (pos > last && !boundary)
1499 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1500 ? rit_pos : last + 1;
1501 else if (pos > rit_pos && (pos > last || boundary))
1502 // Place cursor after char at (logical) position pos - 1
1503 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1504 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1506 // Place cursor before char at (logical) position pos
1507 cursor_vpos = (bidi_level(pos) % 2 == 0)
1508 ? log2vis(pos) : log2vis(pos) + 1;
1510 pos_type body_pos = pit->beginningOfBody();
1512 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1515 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1516 pos_type pos = vis2log(vpos);
1517 if (body_pos > 0 && pos == body_pos - 1) {
1518 x += fill_label_hfill +
1519 font_metrics::width(
1520 pit->layout()->labelsep, getLabelFont(pit));
1521 if (pit->isLineSeparator(body_pos - 1))
1522 x -= singleWidth(pit, body_pos - 1);
1525 if (hfillExpansion(*this, pit, rit, pos)) {
1526 x += singleWidth(pit, pos);
1527 if (pos >= body_pos)
1530 x += fill_label_hfill;
1531 } else if (pit->isSeparator(pos)) {
1532 x += singleWidth(pit, pos);
1533 if (pos >= body_pos)
1534 x += fill_separator;
1536 x += singleWidth(pit, pos);
1542 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1543 pos_type pos, bool setfont, bool boundary)
1545 setCursor(cursor, pit, pos, boundary);
1551 void LyXText::setCurrentFont()
1553 pos_type pos = cursor.pos();
1554 ParagraphList::iterator pit = cursor.par();
1556 if (cursor.boundary() && pos > 0)
1560 if (pos == pit->size())
1562 else // potentional bug... BUG (Lgb)
1563 if (pit->isSeparator(pos)) {
1564 if (pos > cursorRow()->pos() &&
1565 bidi_level(pos) % 2 ==
1566 bidi_level(pos - 1) % 2)
1568 else if (pos + 1 < pit->size())
1573 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1574 real_current_font = getFont(pit, pos);
1576 if (cursor.pos() == pit->size() &&
1577 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1578 !cursor.boundary()) {
1579 Language const * lang =
1580 pit->getParLanguage(bv()->buffer()->params);
1581 current_font.setLanguage(lang);
1582 current_font.setNumber(LyXFont::OFF);
1583 real_current_font.setLanguage(lang);
1584 real_current_font.setNumber(LyXFont::OFF);
1589 // returns the column near the specified x-coordinate of the row
1590 // x is set to the real beginning of this column
1591 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1592 RowList::iterator rit, int & x, bool & boundary) const
1595 double fill_separator;
1597 double fill_label_hfill;
1599 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1601 pos_type vc = rit->pos();
1602 pos_type last = lastPrintablePos(*this, pit, rit);
1604 LyXLayout_ptr const & layout = pit->layout();
1606 bool left_side = false;
1608 pos_type body_pos = pit->beginningOfBody();
1609 double last_tmpx = tmpx;
1612 (body_pos - 1 > last ||
1613 !pit->isLineSeparator(body_pos - 1)))
1616 // check for empty row
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(pit));
1628 if (pit->isLineSeparator(body_pos - 1))
1629 tmpx -= singleWidth(pit, body_pos - 1);
1632 if (hfillExpansion(*this, pit, rit, c)) {
1633 tmpx += singleWidth(pit, c);
1637 tmpx += fill_label_hfill;
1638 } else if (pit->isSeparator(c)) {
1639 tmpx += singleWidth(pit, c);
1641 tmpx += fill_separator;
1643 tmpx += singleWidth(pit, 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() || getPar(next_rit) != pit);
1664 // If lastrow is false, we don't need to compute
1665 // the value of rtl.
1666 bool const rtl = (lastrow)
1667 ? pit->isRightToLeftPar(bv()->buffer()->params)
1670 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1671 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1673 else if (vc == rit->pos()) {
1675 if (bidi_level(c) % 2 == 1)
1678 c = vis2log(vc - 1);
1679 bool const rtl = (bidi_level(c) % 2 == 1);
1680 if (left_side == rtl) {
1682 boundary = isBoundary(bv()->buffer(), *pit, c);
1686 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1687 if (bidi_level(last) % 2 == 0)
1688 tmpx -= singleWidth(pit, last);
1690 tmpx += singleWidth(pit, last);
1700 void LyXText::setCursorFromCoordinates(int x, int y)
1702 //LyXCursor old_cursor = cursor;
1703 setCursorFromCoordinates(cursor, x, y);
1705 #warning DEPM disabled, otherwise crash when entering new table
1706 //deleteEmptyParagraphMechanism(old_cursor);
1713 * return true if the cursor given is at the end of a row,
1714 * and the next row is filled by an inset that spans an entire
1717 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1719 RowList::iterator row = lt.getRow(cur);
1720 if (boost::next(row) == lt.rows().end())
1723 RowList::iterator next = boost::next(row);
1725 if (next->pos() != cur.pos() || lt.getPar(next) != cur.par())
1728 if (cur.pos() == cur.par()->size()
1729 || !cur.par()->isInset(cur.pos()))
1732 InsetOld const * inset = cur.par()->getInset(cur.pos());
1733 if (inset->needFullRow() || inset->display())
1741 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1743 // Get the row first.
1745 RowList::iterator row = getRowNearY(y);
1746 ParagraphList::iterator pit = getPar(row);
1748 pos_type const column = getColumnNearX(pit, row, x, bound);
1750 cur.pos(row->pos() + column);
1752 cur.y(y + row->baseline());
1754 if (beforeFullRowInset(*this, cur)) {
1755 pos_type const last = lastPrintablePos(*this, pit, row);
1756 RowList::iterator next_row = boost::next(row);
1757 cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1758 cur.iy(y + row->height() + next_row->baseline());
1763 cur.boundary(bound);
1767 void LyXText::cursorLeft(bool internal)
1769 if (cursor.pos() > 0) {
1770 bool boundary = cursor.boundary();
1771 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1772 if (!internal && !boundary &&
1773 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1774 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1775 } else if (cursor.par() != ownerParagraphs().begin()) {
1776 // steps into the paragraph above
1777 ParagraphList::iterator pit = boost::prior(cursor.par());
1778 setCursor(pit, pit->size());
1783 void LyXText::cursorRight(bool internal)
1785 bool const at_end = (cursor.pos() == cursor.par()->size());
1786 bool const at_newline = !at_end &&
1787 cursor.par()->isNewline(cursor.pos());
1789 if (!internal && cursor.boundary() && !at_newline)
1790 setCursor(cursor.par(), cursor.pos(), true, false);
1792 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1794 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1795 setCursor(cursor.par(), cursor.pos(), true, true);
1796 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1797 setCursor(boost::next(cursor.par()), 0);
1801 void LyXText::cursorUp(bool selecting)
1804 int x = cursor.x_fix();
1805 int y = cursor.y() - cursorRow()->baseline() - 1;
1806 setCursorFromCoordinates(x, y);
1809 int y1 = cursor.iy() - topy;
1812 InsetOld * inset_hit = checkInsetHit(x, y1);
1813 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1814 inset_hit->localDispatch(
1815 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1819 setCursorFromCoordinates(bv(), cursor.x_fix(),
1820 cursor.y() - cursorRow()->baseline() - 1);
1825 void LyXText::cursorDown(bool selecting)
1828 int x = cursor.x_fix();
1829 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1830 setCursorFromCoordinates(x, y);
1831 if (!selecting && cursorRow() == cursorIRow()) {
1833 int y1 = cursor.iy() - topy;
1836 InsetOld * inset_hit = checkInsetHit(x, y1);
1837 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1838 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1839 inset_hit->localDispatch(cmd);
1843 setCursorFromCoordinates(bv(), cursor.x_fix(),
1844 cursor.y() - cursorRow()->baseline()
1845 + cursorRow()->height() + 1);
1850 void LyXText::cursorUpParagraph()
1852 if (cursor.pos() > 0)
1853 setCursor(cursor.par(), 0);
1854 else if (cursor.par() != ownerParagraphs().begin())
1855 setCursor(boost::prior(cursor.par()), 0);
1859 void LyXText::cursorDownParagraph()
1861 ParagraphList::iterator par = cursor.par();
1862 ParagraphList::iterator next_par = boost::next(par);
1864 if (next_par != ownerParagraphs().end())
1865 setCursor(next_par, 0);
1867 setCursor(par, par->size());
1871 // fix the cursor `cur' after a characters has been deleted at `where'
1872 // position. Called by deleteEmptyParagraphMechanism
1873 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1875 // if cursor is not in the paragraph where the delete occured,
1877 if (cur.par() != where.par())
1880 // if cursor position is after the place where the delete occured,
1882 if (cur.pos() > where.pos())
1883 cur.pos(cur.pos()-1);
1885 // check also if we don't want to set the cursor on a spot behind the
1886 // pagragraph because we erased the last character.
1887 if (cur.pos() > cur.par()->size())
1888 cur.pos(cur.par()->size());
1890 // recompute row et al. for this cursor
1891 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1895 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1897 // Would be wrong to delete anything if we have a selection.
1898 if (selection.set())
1901 // We allow all kinds of "mumbo-jumbo" when freespacing.
1902 if (old_cursor.par()->isFreeSpacing())
1905 /* Ok I'll put some comments here about what is missing.
1906 I have fixed BackSpace (and thus Delete) to not delete
1907 double-spaces automagically. I have also changed Cut,
1908 Copy and Paste to hopefully do some sensible things.
1909 There are still some small problems that can lead to
1910 double spaces stored in the document file or space at
1911 the beginning of paragraphs. This happens if you have
1912 the cursor betwenn to spaces and then save. Or if you
1913 cut and paste and the selection have a space at the
1914 beginning and then save right after the paste. I am
1915 sure none of these are very hard to fix, but I will
1916 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1917 that I can get some feedback. (Lgb)
1920 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1921 // delete the LineSeparator.
1924 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1925 // delete the LineSeparator.
1928 // If the pos around the old_cursor were spaces, delete one of them.
1929 if (old_cursor.par() != cursor.par()
1930 || old_cursor.pos() != cursor.pos()) {
1932 // Only if the cursor has really moved
1933 if (old_cursor.pos() > 0
1934 && old_cursor.pos() < old_cursor.par()->size()
1935 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1936 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1937 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1938 redoParagraph(old_cursor.par());
1942 #ifdef WITH_WARNINGS
1943 #warning This will not work anymore when we have multiple views of the same buffer
1944 // In this case, we will have to correct also the cursors held by
1945 // other bufferviews. It will probably be easier to do that in a more
1946 // automated way in LyXCursor code. (JMarc 26/09/2001)
1948 // correct all cursors held by the LyXText
1949 fixCursorAfterDelete(cursor, old_cursor);
1950 fixCursorAfterDelete(selection.cursor, old_cursor);
1951 fixCursorAfterDelete(selection.start, old_cursor);
1952 fixCursorAfterDelete(selection.end, old_cursor);
1957 // don't delete anything if this is the ONLY paragraph!
1958 if (ownerParagraphs().size() == 1)
1961 // Do not delete empty paragraphs with keepempty set.
1962 if (old_cursor.par()->allowEmpty())
1965 // only do our magic if we changed paragraph
1966 if (old_cursor.par() == cursor.par())
1969 // record if we have deleted a paragraph
1970 // we can't possibly have deleted a paragraph before this point
1971 bool deleted = false;
1973 if (old_cursor.par()->empty() ||
1974 (old_cursor.par()->size() == 1 &&
1975 old_cursor.par()->isLineSeparator(0))) {
1976 // ok, we will delete anything
1977 LyXCursor tmpcursor;
1981 bool selection_position_was_oldcursor_position = (
1982 selection.cursor.par() == old_cursor.par()
1983 && selection.cursor.pos() == old_cursor.pos());
1985 if (getRow(old_cursor) != rows().begin()) {
1986 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1988 cursor = old_cursor; // that undo can restore the right cursor position
1989 #warning FIXME. --end() iterator is usable here
1990 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1991 while (endpit != ownerParagraphs().end() &&
1992 endpit->getDepth()) {
1996 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
1997 boost::prior(endpit));
2001 removeRow(getRow(old_cursor));
2003 ownerParagraphs().erase(old_cursor.par());
2005 /* Breakagain the next par. Needed because of
2006 * the parindent that can occur or dissappear.
2007 * The next row can change its height, if
2008 * there is another layout before */
2009 RowList::iterator tmprit = boost::next(prevrow);
2010 if (tmprit != rows().end()) {
2011 redoParagraph(getPar(tmprit));
2014 setHeightOfRow(getPar(prevrow), prevrow);
2016 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2019 cursor = old_cursor; // that undo can restore the right cursor position
2020 #warning FIXME. --end() iterator is usable here
2021 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2022 while (endpit != ownerParagraphs().end() &&
2023 endpit->getDepth()) {
2027 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2031 removeRow(getRow(old_cursor));
2033 ownerParagraphs().erase(old_cursor.par());
2035 /* Breakagain the next par. Needed because of
2036 the parindent that can occur or dissappear.
2037 The next row can change its height, if
2038 there is another layout before */
2039 if (nextrow != rows().end()) {
2040 redoParagraph(getPar(nextrow));
2046 setCursorIntern(cursor.par(), cursor.pos());
2048 if (selection_position_was_oldcursor_position) {
2049 // correct selection
2050 selection.cursor = cursor;
2054 if (old_cursor.par()->stripLeadingSpaces()) {
2055 redoParagraph(old_cursor.par());
2057 setCursorIntern(cursor.par(), cursor.pos());
2058 selection.cursor = cursor;
2065 ParagraphList & LyXText::ownerParagraphs() const
2068 return inset_owner->paragraphs;
2070 return bv_owner->buffer()->paragraphs;
2074 bool LyXText::isInInset() const
2076 // Sub-level has non-null bv owner and non-null inset owner.
2077 return inset_owner != 0 && bv_owner != 0;
2081 int defaultRowHeight()
2083 LyXFont const font(LyXFont::ALL_SANE);
2084 return int(font_metrics::maxAscent(font)
2085 + font_metrics::maxDescent(font) * 1.5);