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_ = endRow();
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_ = endRow();
78 void LyXText::init(BufferView * bview)
86 anchor_row_ = endRow();
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 == firstRow()) {
248 anchor_row_ = boost::next(rit);
249 anchor_row_offset_ -= rit->height();
251 anchor_row_ = boost::prior(rit);
252 anchor_row_offset_ += anchor_row_->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(ParagraphList::iterator pit,
265 RowList::iterator rit)
267 RowList::iterator end = endRow(pit);
268 for (++rit; rit != end; ) {
269 RowList::iterator rit2 = boost::next(rit);
276 void LyXText::insertParagraph(ParagraphList::iterator pit,
277 RowList::iterator rit)
279 // insert a new row, starting at position 0
280 rit = rowlist_.insert(rit, Row(0));
282 // and now append the whole paragraph before the new row
284 pos_type const last = pit->size();
288 pos_type z = rowBreakPoint(pit, *rit);
290 RowList::iterator tmprow = rit;
294 rit = rowlist_.insert(boost::next(rit), Row(z));
299 // Set the dimensions of the row
300 // fixed fill setting now by calling inset->update() in
301 // singleWidth when needed!
302 tmprow->fill(fill(pit, tmprow, workWidth()));
303 setHeightOfRow(pit, tmprow);
309 InsetOld * LyXText::getInset() const
311 ParagraphList::iterator pit = cursor.par();
312 pos_type const pos = cursor.pos();
314 if (pos < pit->size() && pit->isInset(pos)) {
315 return pit->getInset(pos);
321 void LyXText::toggleInset()
323 InsetOld * inset = getInset();
324 // is there an editable inset at cursor position?
325 if (!isEditableInset(inset)) {
326 // No, try to see if we are inside a collapsable inset
327 if (inset_owner && inset_owner->owner()
328 && inset_owner->owner()->isOpen()) {
329 bv()->unlockInset(inset_owner->owner());
330 inset_owner->owner()->close(bv());
331 bv()->getLyXText()->cursorRight(bv());
335 //bv()->owner()->message(inset->editMessage());
337 // do we want to keep this?? (JMarc)
338 if (!isHighlyEditableInset(inset))
339 recordUndo(bv(), Undo::ATOMIC);
350 /* used in setlayout */
351 // Asger is not sure we want to do this...
352 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
355 LyXLayout_ptr const & layout = par.layout();
356 pos_type const psize = par.size();
359 for (pos_type pos = 0; pos < psize; ++pos) {
360 if (pos < par.beginningOfBody())
361 layoutfont = layout->labelfont;
363 layoutfont = layout->font;
365 LyXFont tmpfont = par.getFontSettings(params, pos);
366 tmpfont.reduce(layoutfont);
367 par.setFont(pos, tmpfont);
372 ParagraphList::iterator
373 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
374 LyXCursor & send_cur,
375 string const & layout)
377 ParagraphList::iterator endpit = boost::next(send_cur.par());
378 ParagraphList::iterator undoendpit = endpit;
379 ParagraphList::iterator pars_end = ownerParagraphs().end();
381 if (endpit != pars_end && endpit->getDepth()) {
382 while (endpit != pars_end && endpit->getDepth()) {
386 } else if (endpit != pars_end) {
387 // because of parindents etc.
391 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
393 // ok we have a selection. This is always between sstart_cur
394 // and sel_end cursor
396 ParagraphList::iterator pit = sstart_cur.par();
397 ParagraphList::iterator epit = boost::next(send_cur.par());
399 LyXLayout_ptr const & lyxlayout =
400 bv()->buffer()->params.getLyXTextClass()[layout];
403 pit->applyLayout(lyxlayout);
404 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
405 pit->params().spaceTop(lyxlayout->fill_top ?
406 VSpace(VSpace::VFILL)
407 : VSpace(VSpace::NONE));
408 pit->params().spaceBottom(lyxlayout->fill_bottom ?
409 VSpace(VSpace::VFILL)
410 : VSpace(VSpace::NONE));
411 if (lyxlayout->margintype == MARGIN_MANUAL)
412 pit->setLabelWidthString(lyxlayout->labelstring());
415 } while (pit != epit);
421 // set layout over selection and make a total rebreak of those paragraphs
422 void LyXText::setLayout(string const & layout)
424 LyXCursor tmpcursor = cursor; // store the current cursor
426 // if there is no selection just set the layout
427 // of the current paragraph
428 if (!selection.set()) {
429 selection.start = cursor; // dummy selection
430 selection.end = cursor;
433 // special handling of new environment insets
434 BufferParams const & params = bv()->buffer()->params;
435 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
436 if (lyxlayout->is_environment) {
437 // move everything in a new environment inset
438 lyxerr << "setting layout " << layout << endl;
439 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
440 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
441 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
442 InsetOld * inset = new InsetEnvironment(params, layout);
443 if (bv()->insertInset(inset)) {
445 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
452 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
453 selection.end, layout);
454 redoParagraphs(selection.start.par(), endpit);
456 // we have to reset the selection, because the
457 // geometry could have changed
458 setCursor(selection.start.par(), selection.start.pos(), false);
459 selection.cursor = cursor;
460 setCursor(selection.end.par(), selection.end.pos(), false);
464 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
468 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
470 ParagraphList::iterator pit = cursor.par();
471 ParagraphList::iterator end = cursor.par();
472 ParagraphList::iterator start = pit;
474 if (selection.set()) {
475 pit = selection.start.par();
476 end = selection.end.par();
480 ParagraphList::iterator pastend = boost::next(end);
483 recordUndo(bv(), Undo::ATOMIC, start, end);
485 bool changed = false;
487 int prev_after_depth = 0;
488 #warning parlist ... could be nicer ?
489 if (start != ownerParagraphs().begin()) {
490 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
494 int const depth = pit->params().depth();
495 if (type == bv_funcs::INC_DEPTH) {
496 if (depth < prev_after_depth
497 && pit->layout()->labeltype != LABEL_BIBLIO) {
500 pit->params().depth(depth + 1);
505 pit->params().depth(depth - 1);
508 prev_after_depth = pit->getMaxDepthAfter();
520 redoParagraphs(start, pastend);
522 // We need to actually move the text->cursor. I don't
523 // understand why ...
524 LyXCursor tmpcursor = cursor;
526 // we have to reset the visual selection because the
527 // geometry could have changed
528 if (selection.set()) {
529 setCursor(selection.start.par(), selection.start.pos());
530 selection.cursor = cursor;
531 setCursor(selection.end.par(), selection.end.pos());
534 // this handles the counter labels, and also fixes up
535 // depth values for follow-on (child) paragraphs
539 setCursor(tmpcursor.par(), tmpcursor.pos());
545 // set font over selection and make a total rebreak of those paragraphs
546 void LyXText::setFont(LyXFont const & font, bool toggleall)
548 // if there is no selection just set the current_font
549 if (!selection.set()) {
550 // Determine basis font
552 if (cursor.pos() < cursor.par()->beginningOfBody()) {
553 layoutfont = getLabelFont(cursor.par());
555 layoutfont = getLayoutFont(cursor.par());
557 // Update current font
558 real_current_font.update(font,
559 bv()->buffer()->params.language,
562 // Reduce to implicit settings
563 current_font = real_current_font;
564 current_font.reduce(layoutfont);
565 // And resolve it completely
566 real_current_font.realize(layoutfont);
571 LyXCursor tmpcursor = cursor; // store the current cursor
573 // ok we have a selection. This is always between sel_start_cursor
574 // and sel_end cursor
576 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
578 cursor = selection.start;
579 while (cursor.par() != selection.end.par() ||
580 cursor.pos() < selection.end.pos())
582 if (cursor.pos() < cursor.par()->size()) {
583 // an open footnote should behave like a closed one
584 setCharFont(cursor.par(), cursor.pos(),
586 cursor.pos(cursor.pos() + 1);
589 cursor.par(boost::next(cursor.par()));
594 redoParagraph(selection.start.par());
596 // we have to reset the selection, because the
597 // geometry could have changed, but we keep
598 // it for user convenience
599 setCursor(selection.start.par(), selection.start.pos());
600 selection.cursor = cursor;
601 setCursor(selection.end.par(), selection.end.pos());
603 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
604 tmpcursor.boundary());
608 // rebreaks all paragraphs between the specified pars
609 // This function is needed after SetLayout and SetFont etc.
610 void LyXText::redoParagraphs(ParagraphList::iterator start,
611 ParagraphList::iterator end)
613 for ( ; start != end; ++start)
614 redoParagraph(start);
618 void LyXText::redoParagraph(ParagraphList::iterator pit)
620 RowList::iterator first = beginRow(pit);
621 RowList::iterator last = endRow(pit);
623 // remove paragraph from rowlist
624 while (first != last) {
625 RowList::iterator rit2 = first;
630 // reinsert the paragraph
631 insertParagraph(pit, last);
635 void LyXText::fullRebreak()
637 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
639 selection.cursor = cursor;
643 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
645 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
646 //Assert(mi.base.textwidth);
653 anchor_row_ = endRow();
654 anchor_row_offset_ = 0;
656 ParagraphList::iterator pit = ownerParagraphs().begin();
657 ParagraphList::iterator end = ownerParagraphs().end();
659 for (; pit != end; ++pit) {
660 InsetList::iterator ii = pit->insetlist.begin();
661 InsetList::iterator iend = pit->insetlist.end();
662 for (; ii != iend; ++ii) {
665 #warning FIXME: pos != 0
666 m.base.font = getFont(pit, 0);
667 ii->inset->metrics(m, dim);
674 dim.asc = firstRow()->ascent_of_text();
675 dim.des = height - dim.asc;
676 dim.wid = std::max(mi.base.textwidth, int(width));
680 // important for the screen
683 // the cursor set functions have a special mechanism. When they
684 // realize, that you left an empty paragraph, they will delete it.
685 // They also delete the corresponding row
687 // need the selection cursor:
688 void LyXText::setSelection()
690 TextCursor::setSelection();
695 void LyXText::clearSelection()
697 TextCursor::clearSelection();
699 // reset this in the bv_owner!
700 if (bv_owner && bv_owner->text)
701 bv_owner->text->xsel_cache.set(false);
705 void LyXText::cursorHome()
707 setCursor(cursor.par(), cursorRow()->pos());
711 void LyXText::cursorEnd()
713 if (cursor.par()->empty())
716 RowList::iterator rit = cursorRow();
717 RowList::iterator next_rit = boost::next(rit);
718 RowList::iterator end = boost::next(rit);
719 ParagraphList::iterator pit = cursor.par();
720 pos_type last_pos = lastPos(*this, pit, rit);
722 if (next_rit == end) {
726 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
731 setCursor(pit, last_pos);
735 void LyXText::cursorTop()
737 setCursor(ownerParagraphs().begin(), 0);
741 void LyXText::cursorBottom()
743 ParagraphList::iterator lastpit =
744 boost::prior(ownerParagraphs().end());
745 setCursor(lastpit, lastpit->size());
749 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
751 // If the mask is completely neutral, tell user
752 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
753 // Could only happen with user style
754 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
758 // Try implicit word selection
759 // If there is a change in the language the implicit word selection
761 LyXCursor resetCursor = cursor;
762 bool implicitSelection = (font.language() == ignore_language
763 && font.number() == LyXFont::IGNORE)
764 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
767 setFont(font, toggleall);
769 // Implicit selections are cleared afterwards
770 //and cursor is set to the original position.
771 if (implicitSelection) {
773 cursor = resetCursor;
774 setCursor(cursor.par(), cursor.pos());
775 selection.cursor = cursor;
780 string LyXText::getStringToIndex()
782 // Try implicit word selection
783 // If there is a change in the language the implicit word selection
785 LyXCursor const reset_cursor = cursor;
786 bool const implicitSelection =
787 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
790 if (!selection.set())
791 bv()->owner()->message(_("Nothing to index!"));
792 else if (selection.start.par() != selection.end.par())
793 bv()->owner()->message(_("Cannot index more than one paragraph!"));
795 idxstring = selectionAsString(bv()->buffer(), false);
797 // Reset cursors to their original position.
798 cursor = reset_cursor;
799 setCursor(cursor.par(), cursor.pos());
800 selection.cursor = cursor;
802 // Clear the implicit selection.
803 if (implicitSelection)
810 // the DTP switches for paragraphs. LyX will store them in the first
811 // physical paragraph. When a paragraph is broken, the top settings rest,
812 // the bottom settings are given to the new one. So I can make sure,
813 // they do not duplicate themself and you cannnot make dirty things with
816 void LyXText::setParagraph(bool line_top, bool line_bottom,
817 bool pagebreak_top, bool pagebreak_bottom,
818 VSpace const & space_top,
819 VSpace const & space_bottom,
820 Spacing const & spacing,
822 string const & labelwidthstring,
825 LyXCursor tmpcursor = cursor;
826 if (!selection.set()) {
827 selection.start = cursor;
828 selection.end = cursor;
831 // make sure that the depth behind the selection are restored, too
832 ParagraphList::iterator endpit = boost::next(selection.end.par());
833 ParagraphList::iterator undoendpit = endpit;
834 ParagraphList::iterator pars_end = ownerParagraphs().end();
836 if (endpit != pars_end && endpit->getDepth()) {
837 while (endpit != pars_end && endpit->getDepth()) {
841 } else if (endpit != pars_end) {
842 // because of parindents etc.
846 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
847 boost::prior(undoendpit));
850 ParagraphList::iterator tmppit = selection.end.par();
852 while (tmppit != boost::prior(selection.start.par())) {
853 setCursor(tmppit, 0);
855 ParagraphList::iterator pit = cursor.par();
856 ParagraphParameters & params = pit->params();
858 params.lineTop(line_top);
859 params.lineBottom(line_bottom);
860 params.pagebreakTop(pagebreak_top);
861 params.pagebreakBottom(pagebreak_bottom);
862 params.spaceTop(space_top);
863 params.spaceBottom(space_bottom);
864 params.spacing(spacing);
865 // does the layout allow the new alignment?
866 LyXLayout_ptr const & layout = pit->layout();
868 if (align == LYX_ALIGN_LAYOUT)
869 align = layout->align;
870 if (align & layout->alignpossible) {
871 if (align == layout->align)
872 params.align(LYX_ALIGN_LAYOUT);
876 pit->setLabelWidthString(labelwidthstring);
877 params.noindent(noindent);
878 tmppit = boost::prior(pit);
881 redoParagraphs(selection.start.par(), endpit);
884 setCursor(selection.start.par(), selection.start.pos());
885 selection.cursor = cursor;
886 setCursor(selection.end.par(), selection.end.pos());
888 setCursor(tmpcursor.par(), tmpcursor.pos());
894 // set the counter of a paragraph. This includes the labels
895 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
897 LyXTextClass const & textclass = buf->params.getLyXTextClass();
898 LyXLayout_ptr const & layout = pit->layout();
900 if (pit != ownerParagraphs().begin()) {
902 pit->params().appendix(boost::prior(pit)->params().appendix());
903 if (!pit->params().appendix() &&
904 pit->params().startOfAppendix()) {
905 pit->params().appendix(true);
906 textclass.counters().reset();
908 pit->enumdepth = boost::prior(pit)->enumdepth;
909 pit->itemdepth = boost::prior(pit)->itemdepth;
911 pit->params().appendix(pit->params().startOfAppendix());
916 // Maybe we have to increment the enumeration depth.
917 // BUT, enumeration in a footnote is considered in isolation from its
918 // surrounding paragraph so don't increment if this is the
919 // first line of the footnote
920 // AND, bibliographies can't have their depth changed ie. they
921 // are always of depth 0
922 if (pit != ownerParagraphs().begin()
923 && boost::prior(pit)->getDepth() < pit->getDepth()
924 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
925 && pit->enumdepth < 3
926 && layout->labeltype != LABEL_BIBLIO) {
930 // Maybe we have to decrement the enumeration depth, see note above
931 if (pit != ownerParagraphs().begin()
932 && boost::prior(pit)->getDepth() > pit->getDepth()
933 && layout->labeltype != LABEL_BIBLIO) {
934 pit->enumdepth = depthHook(pit, ownerParagraphs(),
935 pit->getDepth())->enumdepth;
938 if (!pit->params().labelString().empty()) {
939 pit->params().labelString(string());
942 if (layout->margintype == MARGIN_MANUAL) {
943 if (pit->params().labelWidthString().empty())
944 pit->setLabelWidthString(layout->labelstring());
946 pit->setLabelWidthString(string());
949 // is it a layout that has an automatic label?
950 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
951 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
955 if (i >= 0 && i <= buf->params.secnumdepth) {
959 textclass.counters().step(layout->latexname());
961 // Is there a label? Useful for Chapter layout
962 if (!pit->params().appendix()) {
963 s << buf->B_(layout->labelstring());
965 s << buf->B_(layout->labelstring_appendix());
968 // Use of an integer is here less than elegant. For now.
969 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
970 if (!pit->params().appendix()) {
971 numbertype = "sectioning";
973 numbertype = "appendix";
974 if (pit->isRightToLeftPar(buf->params))
981 << textclass.counters()
982 .numberLabel(layout->latexname(),
983 numbertype, langtype, head);
985 pit->params().labelString(STRCONV(s.str()));
987 // reset enum counters
988 textclass.counters().reset("enum");
989 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
990 textclass.counters().reset("enum");
991 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
993 // Yes I know this is a really, really! bad solution
995 string enumcounter("enum");
997 switch (pit->enumdepth) {
1006 enumcounter += "iv";
1009 // not a valid enumdepth...
1013 textclass.counters().step(enumcounter);
1015 s << textclass.counters()
1016 .numberLabel(enumcounter, "enumeration");
1017 pit->params().labelString(STRCONV(s.str()));
1019 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1020 textclass.counters().step("bibitem");
1021 int number = textclass.counters().value("bibitem");
1022 if (pit->bibitem()) {
1023 pit->bibitem()->setCounter(number);
1024 pit->params().labelString(layout->labelstring());
1026 // In biblio should't be following counters but...
1028 string s = buf->B_(layout->labelstring());
1030 // the caption hack:
1031 if (layout->labeltype == LABEL_SENSITIVE) {
1032 ParagraphList::iterator end = ownerParagraphs().end();
1033 ParagraphList::iterator tmppit = pit;
1036 while (tmppit != end && tmppit->inInset()
1037 // the single '=' is intended below
1038 && (in = tmppit->inInset()->owner()))
1040 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1041 in->lyxCode() == InsetOld::WRAP_CODE) {
1045 tmppit = ownerParagraphs().begin();
1046 for ( ; tmppit != end; ++tmppit)
1047 if (&*tmppit == in->parOwner())
1055 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1056 type = static_cast<InsetFloat*>(in)->params().type;
1057 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1058 type = static_cast<InsetWrap*>(in)->params().type;
1062 Floating const & fl = textclass.floats().getType(type);
1064 textclass.counters().step(fl.type());
1066 // Doesn't work... yet.
1067 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1069 // par->SetLayout(0);
1070 // s = layout->labelstring;
1071 s = _("Senseless: ");
1074 pit->params().labelString(s);
1076 // reset the enumeration counter. They are always reset
1077 // when there is any other layout between
1078 // Just fall-through between the cases so that all
1079 // enum counters deeper than enumdepth is also reset.
1080 switch (pit->enumdepth) {
1082 textclass.counters().reset("enumi");
1084 textclass.counters().reset("enumii");
1086 textclass.counters().reset("enumiii");
1088 textclass.counters().reset("enumiv");
1094 // Updates all counters. Paragraphs with changed label string will be rebroken
1095 void LyXText::updateCounters()
1098 bv()->buffer()->params.getLyXTextClass().counters().reset();
1100 ParagraphList::iterator beg = ownerParagraphs().begin();
1101 ParagraphList::iterator end = ownerParagraphs().end();
1102 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1103 string const oldLabel = pit->params().labelString();
1105 size_t maxdepth = 0;
1107 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1109 if (pit->params().depth() > maxdepth)
1110 pit->params().depth(maxdepth);
1112 // setCounter can potentially change the labelString.
1113 setCounter(bv()->buffer(), pit);
1115 string const & newLabel = pit->params().labelString();
1117 if (oldLabel != newLabel)
1123 void LyXText::insertInset(InsetOld * inset)
1125 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1127 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1129 cursor.par()->insertInset(cursor.pos(), inset);
1130 // Just to rebreak and refresh correctly.
1131 // The character will not be inserted a second time
1132 insertChar(Paragraph::META_INSET);
1133 // If we enter a highly editable inset the cursor should be before
1134 // the inset. After an Undo LyX tries to call inset->edit(...)
1135 // and fails if the cursor is behind the inset and getInset
1136 // does not return the inset!
1137 if (isHighlyEditableInset(inset))
1143 void LyXText::cutSelection(bool doclear, bool realcut)
1145 // Stuff what we got on the clipboard. Even if there is no selection.
1147 // There is a problem with having the stuffing here in that the
1148 // larger the selection the slower LyX will get. This can be
1149 // solved by running the line below only when the selection has
1150 // finished. The solution used currently just works, to make it
1151 // faster we need to be more clever and probably also have more
1152 // calls to stuffClipboard. (Lgb)
1153 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1155 // This doesn't make sense, if there is no selection
1156 if (!selection.set())
1159 // OK, we have a selection. This is always between selection.start
1160 // and selection.end
1162 // make sure that the depth behind the selection are restored, too
1163 ParagraphList::iterator endpit = boost::next(selection.end.par());
1164 ParagraphList::iterator undoendpit = endpit;
1165 ParagraphList::iterator pars_end = ownerParagraphs().end();
1167 if (endpit != pars_end && endpit->getDepth()) {
1168 while (endpit != pars_end && endpit->getDepth()) {
1170 undoendpit = endpit;
1172 } else if (endpit != pars_end) {
1173 // because of parindents etc.
1177 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1178 boost::prior(undoendpit));
1180 endpit = selection.end.par();
1181 int endpos = selection.end.pos();
1183 boost::tie(endpit, endpos) = realcut ?
1184 CutAndPaste::cutSelection(bv()->buffer()->params,
1186 selection.start.par(), endpit,
1187 selection.start.pos(), endpos,
1188 bv()->buffer()->params.textclass,
1190 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1192 selection.start.par(), endpit,
1193 selection.start.pos(), endpos,
1195 // sometimes necessary
1197 selection.start.par()->stripLeadingSpaces();
1199 redoParagraphs(selection.start.par(), boost::next(endpit));
1200 // cutSelection can invalidate the cursor so we need to set
1202 // we prefer the end for when tracking changes
1206 // need a valid cursor. (Lgb)
1209 setCursor(cursor.par(), cursor.pos());
1210 selection.cursor = cursor;
1215 void LyXText::copySelection()
1217 // stuff the selection onto the X clipboard, from an explicit copy request
1218 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1220 // this doesnt make sense, if there is no selection
1221 if (!selection.set())
1224 // ok we have a selection. This is always between selection.start
1225 // and sel_end cursor
1227 // copy behind a space if there is one
1228 while (selection.start.par()->size() > selection.start.pos()
1229 && selection.start.par()->isLineSeparator(selection.start.pos())
1230 && (selection.start.par() != selection.end.par()
1231 || selection.start.pos() < selection.end.pos()))
1232 selection.start.pos(selection.start.pos() + 1);
1234 CutAndPaste::copySelection(selection.start.par(),
1235 selection.end.par(),
1236 selection.start.pos(), selection.end.pos(),
1237 bv()->buffer()->params.textclass);
1241 void LyXText::pasteSelection(size_t sel_index)
1243 // this does not make sense, if there is nothing to paste
1244 if (!CutAndPaste::checkPastePossible())
1247 recordUndo(bv(), Undo::INSERT, cursor.par());
1249 ParagraphList::iterator endpit;
1254 boost::tie(ppp, endpit) =
1255 CutAndPaste::pasteSelection(*bv()->buffer(),
1257 cursor.par(), cursor.pos(),
1258 bv()->buffer()->params.textclass,
1260 bufferErrors(*bv()->buffer(), el);
1261 bv()->showErrorList(_("Paste"));
1263 redoParagraphs(cursor.par(), endpit);
1265 setCursor(cursor.par(), cursor.pos());
1268 selection.cursor = cursor;
1269 setCursor(ppp.first, ppp.second);
1275 void LyXText::setSelectionRange(lyx::pos_type length)
1280 selection.cursor = cursor;
1287 // simple replacing. The font of the first selected character is used
1288 void LyXText::replaceSelectionWithString(string const & str)
1290 recordUndo(bv(), Undo::ATOMIC);
1293 if (!selection.set()) { // create a dummy selection
1294 selection.end = cursor;
1295 selection.start = cursor;
1298 // Get font setting before we cut
1299 pos_type pos = selection.end.pos();
1300 LyXFont const font = selection.start.par()
1301 ->getFontSettings(bv()->buffer()->params,
1302 selection.start.pos());
1304 // Insert the new string
1305 string::const_iterator cit = str.begin();
1306 string::const_iterator end = str.end();
1307 for (; cit != end; ++cit) {
1308 selection.end.par()->insertChar(pos, (*cit), font);
1312 // Cut the selection
1313 cutSelection(true, false);
1319 // needed to insert the selection
1320 void LyXText::insertStringAsLines(string const & str)
1322 ParagraphList::iterator pit = cursor.par();
1323 pos_type pos = cursor.pos();
1324 ParagraphList::iterator endpit = boost::next(cursor.par());
1326 recordUndo(bv(), Undo::ATOMIC);
1328 // only to be sure, should not be neccessary
1331 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1333 redoParagraphs(cursor.par(), endpit);
1334 setCursor(cursor.par(), cursor.pos());
1335 selection.cursor = cursor;
1336 setCursor(pit, pos);
1341 // turns double-CR to single CR, others where converted into one
1342 // blank. Then InsertStringAsLines is called
1343 void LyXText::insertStringAsParagraphs(string const & str)
1345 string linestr(str);
1346 bool newline_inserted = false;
1347 string::size_type const siz = linestr.length();
1349 for (string::size_type i = 0; i < siz; ++i) {
1350 if (linestr[i] == '\n') {
1351 if (newline_inserted) {
1352 // we know that \r will be ignored by
1353 // InsertStringA. Of course, it is a dirty
1354 // trick, but it works...
1355 linestr[i - 1] = '\r';
1359 newline_inserted = true;
1361 } else if (IsPrintable(linestr[i])) {
1362 newline_inserted = false;
1365 insertStringAsLines(linestr);
1369 bool LyXText::setCursor(ParagraphList::iterator pit,
1371 bool setfont, bool boundary)
1373 LyXCursor old_cursor = cursor;
1374 setCursorIntern(pit, pos, setfont, boundary);
1375 return deleteEmptyParagraphMechanism(old_cursor);
1379 void LyXText::redoCursor()
1381 #warning maybe the same for selections?
1382 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1386 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1387 pos_type pos, bool boundary)
1389 Assert(pit != ownerParagraphs().end());
1393 cur.boundary(boundary);
1397 // get the cursor y position in text
1399 RowList::iterator row = getRow(pit, pos, y);
1400 RowList::iterator old_row = row;
1401 // if we are before the first char of this row and are still in the
1402 // same paragraph and there is a previous row then put the cursor on
1403 // the end of the previous row
1404 cur.iy(y + row->baseline());
1405 if (row != beginRow(pit)
1407 && pos < pit->size()
1408 && pit->getChar(pos) == Paragraph::META_INSET) {
1409 InsetOld * ins = pit->getInset(pos);
1410 if (ins && (ins->needFullRow() || ins->display())) {
1416 // y is now the beginning of the cursor row
1417 y += row->baseline();
1418 // y is now the cursor baseline
1421 pos_type last = lastPrintablePos(*this, pit, old_row);
1423 // None of these should happen, but we're scaredy-cats
1424 if (pos > pit->size()) {
1425 lyxerr << "dont like 1 please report" << endl;
1428 } else if (pos > last + 1) {
1429 lyxerr << "dont like 2 please report" << endl;
1430 // This shouldn't happen.
1433 } else if (pos < row->pos()) {
1434 lyxerr << "dont like 3 please report" << endl;
1439 // now get the cursors x position
1440 float x = getCursorX(pit, row, pos, last, boundary);
1443 if (old_row != row) {
1444 x = getCursorX(pit, old_row, pos, last, boundary);
1448 /* We take out this for the time being because 1) the redraw code is not
1449 prepared to this yet and 2) because some good policy has yet to be decided
1450 while editting: for instance how to act on rows being created/deleted
1454 //if the cursor is in a visible row, anchor to it
1456 if (topy < y && y < topy + bv()->workHeight())
1462 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1463 pos_type pos, pos_type last, bool boundary) const
1465 pos_type cursor_vpos = 0;
1467 double fill_separator;
1469 double fill_label_hfill;
1470 // This call HAS to be here because of the BidiTables!!!
1471 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1474 pos_type const rit_pos = rit->pos();
1477 cursor_vpos = rit_pos;
1478 else if (pos > last && !boundary)
1479 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1480 ? rit_pos : last + 1;
1481 else if (pos > rit_pos && (pos > last || boundary))
1482 // Place cursor after char at (logical) position pos - 1
1483 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1484 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1486 // Place cursor before char at (logical) position pos
1487 cursor_vpos = (bidi_level(pos) % 2 == 0)
1488 ? log2vis(pos) : log2vis(pos) + 1;
1490 pos_type body_pos = pit->beginningOfBody();
1492 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1495 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1496 pos_type pos = vis2log(vpos);
1497 if (body_pos > 0 && pos == body_pos - 1) {
1498 x += fill_label_hfill +
1499 font_metrics::width(
1500 pit->layout()->labelsep, getLabelFont(pit));
1501 if (pit->isLineSeparator(body_pos - 1))
1502 x -= singleWidth(pit, body_pos - 1);
1505 if (hfillExpansion(*this, pit, rit, pos)) {
1506 x += singleWidth(pit, pos);
1507 if (pos >= body_pos)
1510 x += fill_label_hfill;
1511 } else if (pit->isSeparator(pos)) {
1512 x += singleWidth(pit, pos);
1513 if (pos >= body_pos)
1514 x += fill_separator;
1516 x += singleWidth(pit, pos);
1522 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1523 pos_type pos, bool setfont, bool boundary)
1525 setCursor(cursor, pit, pos, boundary);
1531 void LyXText::setCurrentFont()
1533 pos_type pos = cursor.pos();
1534 ParagraphList::iterator pit = cursor.par();
1536 if (cursor.boundary() && pos > 0)
1540 if (pos == pit->size())
1542 else // potentional bug... BUG (Lgb)
1543 if (pit->isSeparator(pos)) {
1544 if (pos > cursorRow()->pos() &&
1545 bidi_level(pos) % 2 ==
1546 bidi_level(pos - 1) % 2)
1548 else if (pos + 1 < pit->size())
1553 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1554 real_current_font = getFont(pit, pos);
1556 if (cursor.pos() == pit->size() &&
1557 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1558 !cursor.boundary()) {
1559 Language const * lang =
1560 pit->getParLanguage(bv()->buffer()->params);
1561 current_font.setLanguage(lang);
1562 current_font.setNumber(LyXFont::OFF);
1563 real_current_font.setLanguage(lang);
1564 real_current_font.setNumber(LyXFont::OFF);
1569 // returns the column near the specified x-coordinate of the row
1570 // x is set to the real beginning of this column
1571 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1572 RowList::iterator rit, int & x, bool & boundary) const
1575 double fill_separator;
1577 double fill_label_hfill;
1579 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1581 pos_type vc = rit->pos();
1582 pos_type last = lastPrintablePos(*this, pit, rit);
1584 LyXLayout_ptr const & layout = pit->layout();
1586 bool left_side = false;
1588 pos_type body_pos = pit->beginningOfBody();
1589 double last_tmpx = tmpx;
1592 (body_pos - 1 > last ||
1593 !pit->isLineSeparator(body_pos - 1)))
1596 // check for empty row
1602 while (vc <= last && tmpx <= x) {
1605 if (body_pos > 0 && c == body_pos - 1) {
1606 tmpx += fill_label_hfill +
1607 font_metrics::width(layout->labelsep, getLabelFont(pit));
1608 if (pit->isLineSeparator(body_pos - 1))
1609 tmpx -= singleWidth(pit, body_pos - 1);
1612 if (hfillExpansion(*this, pit, rit, c)) {
1613 tmpx += singleWidth(pit, c);
1617 tmpx += fill_label_hfill;
1618 } else if (pit->isSeparator(c)) {
1619 tmpx += singleWidth(pit, c);
1621 tmpx += fill_separator;
1623 tmpx += singleWidth(pit, c);
1628 if ((tmpx + last_tmpx) / 2 > x) {
1633 if (vc > last + 1) // This shouldn't happen.
1637 // This (rtl_support test) is not needed, but gives
1638 // some speedup if rtl_support == false
1639 bool const lastrow = lyxrc.rtl_support
1640 && boost::next(rit) == endRow(pit);
1642 // If lastrow is false, we don't need to compute
1643 // the value of rtl.
1644 bool const rtl = (lastrow)
1645 ? pit->isRightToLeftPar(bv()->buffer()->params)
1648 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1649 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1651 else if (vc == rit->pos()) {
1653 if (bidi_level(c) % 2 == 1)
1656 c = vis2log(vc - 1);
1657 bool const rtl = (bidi_level(c) % 2 == 1);
1658 if (left_side == rtl) {
1660 boundary = isBoundary(bv()->buffer(), *pit, c);
1664 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1665 if (bidi_level(last) % 2 == 0)
1666 tmpx -= singleWidth(pit, last);
1668 tmpx += singleWidth(pit, last);
1678 void LyXText::setCursorFromCoordinates(int x, int y)
1680 //LyXCursor old_cursor = cursor;
1681 setCursorFromCoordinates(cursor, x, y);
1683 #warning DEPM disabled, otherwise crash when entering new table
1684 //deleteEmptyParagraphMechanism(old_cursor);
1691 * return true if the cursor given is at the end of a row,
1692 * and the next row is filled by an inset that spans an entire
1695 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1697 RowList::iterator row = lt.getRow(cur);
1698 if (boost::next(row) == lt.endRow())
1701 RowList::iterator next = boost::next(row);
1703 if (next == lt.endRow(cur.par()) || next->pos() != cur.pos())
1706 if (cur.pos() == cur.par()->size()
1707 || !cur.par()->isInset(cur.pos()))
1710 InsetOld const * inset = cur.par()->getInset(cur.pos());
1711 if (inset->needFullRow() || inset->display())
1719 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1721 // Get the row first.
1723 RowList::iterator row = getRowNearY(y);
1724 ParagraphList::iterator pit = getPar(row);
1726 pos_type const column = getColumnNearX(pit, row, x, bound);
1728 cur.pos(row->pos() + column);
1730 cur.y(y + row->baseline());
1732 if (beforeFullRowInset(*this, cur)) {
1733 pos_type const last = lastPrintablePos(*this, pit, row);
1734 RowList::iterator next_row = boost::next(row);
1735 cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1736 cur.iy(y + row->height() + next_row->baseline());
1741 cur.boundary(bound);
1745 void LyXText::cursorLeft(bool internal)
1747 if (cursor.pos() > 0) {
1748 bool boundary = cursor.boundary();
1749 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1750 if (!internal && !boundary &&
1751 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1752 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1753 } else if (cursor.par() != ownerParagraphs().begin()) {
1754 // steps into the paragraph above
1755 ParagraphList::iterator pit = boost::prior(cursor.par());
1756 setCursor(pit, pit->size());
1761 void LyXText::cursorRight(bool internal)
1763 bool const at_end = (cursor.pos() == cursor.par()->size());
1764 bool const at_newline = !at_end &&
1765 cursor.par()->isNewline(cursor.pos());
1767 if (!internal && cursor.boundary() && !at_newline)
1768 setCursor(cursor.par(), cursor.pos(), true, false);
1770 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1772 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1773 setCursor(cursor.par(), cursor.pos(), true, true);
1774 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1775 setCursor(boost::next(cursor.par()), 0);
1779 void LyXText::cursorUp(bool selecting)
1782 int x = cursor.x_fix();
1783 int y = cursor.y() - cursorRow()->baseline() - 1;
1784 setCursorFromCoordinates(x, y);
1787 int y1 = cursor.iy() - topy;
1790 InsetOld * inset_hit = checkInsetHit(x, y1);
1791 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1792 inset_hit->localDispatch(
1793 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1797 setCursorFromCoordinates(cursor.x_fix(),
1798 cursor.y() - cursorRow()->baseline() - 1);
1803 void LyXText::cursorDown(bool selecting)
1806 int x = cursor.x_fix();
1807 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1808 setCursorFromCoordinates(x, y);
1809 if (!selecting && cursorRow() == cursorIRow()) {
1811 int y1 = cursor.iy() - topy;
1814 InsetOld * inset_hit = checkInsetHit(x, y1);
1815 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1816 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1817 inset_hit->localDispatch(cmd);
1821 setCursorFromCoordinates(cursor.x_fix(),
1822 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1827 void LyXText::cursorUpParagraph()
1829 if (cursor.pos() > 0)
1830 setCursor(cursor.par(), 0);
1831 else if (cursor.par() != ownerParagraphs().begin())
1832 setCursor(boost::prior(cursor.par()), 0);
1836 void LyXText::cursorDownParagraph()
1838 ParagraphList::iterator par = cursor.par();
1839 ParagraphList::iterator next_par = boost::next(par);
1841 if (next_par != ownerParagraphs().end())
1842 setCursor(next_par, 0);
1844 setCursor(par, par->size());
1848 // fix the cursor `cur' after a characters has been deleted at `where'
1849 // position. Called by deleteEmptyParagraphMechanism
1850 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1852 // if cursor is not in the paragraph where the delete occured,
1854 if (cur.par() != where.par())
1857 // if cursor position is after the place where the delete occured,
1859 if (cur.pos() > where.pos())
1860 cur.pos(cur.pos()-1);
1862 // check also if we don't want to set the cursor on a spot behind the
1863 // pagragraph because we erased the last character.
1864 if (cur.pos() > cur.par()->size())
1865 cur.pos(cur.par()->size());
1867 // recompute row et al. for this cursor
1868 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1872 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1874 // Would be wrong to delete anything if we have a selection.
1875 if (selection.set())
1878 // We allow all kinds of "mumbo-jumbo" when freespacing.
1879 if (old_cursor.par()->isFreeSpacing())
1882 /* Ok I'll put some comments here about what is missing.
1883 I have fixed BackSpace (and thus Delete) to not delete
1884 double-spaces automagically. I have also changed Cut,
1885 Copy and Paste to hopefully do some sensible things.
1886 There are still some small problems that can lead to
1887 double spaces stored in the document file or space at
1888 the beginning of paragraphs. This happens if you have
1889 the cursor betwenn to spaces and then save. Or if you
1890 cut and paste and the selection have a space at the
1891 beginning and then save right after the paste. I am
1892 sure none of these are very hard to fix, but I will
1893 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1894 that I can get some feedback. (Lgb)
1897 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1898 // delete the LineSeparator.
1901 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1902 // delete the LineSeparator.
1905 // If the pos around the old_cursor were spaces, delete one of them.
1906 if (old_cursor.par() != cursor.par()
1907 || old_cursor.pos() != cursor.pos()) {
1909 // Only if the cursor has really moved
1910 if (old_cursor.pos() > 0
1911 && old_cursor.pos() < old_cursor.par()->size()
1912 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1913 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1914 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1915 redoParagraph(old_cursor.par());
1919 #ifdef WITH_WARNINGS
1920 #warning This will not work anymore when we have multiple views of the same buffer
1921 // In this case, we will have to correct also the cursors held by
1922 // other bufferviews. It will probably be easier to do that in a more
1923 // automated way in LyXCursor code. (JMarc 26/09/2001)
1925 // correct all cursors held by the LyXText
1926 fixCursorAfterDelete(cursor, old_cursor);
1927 fixCursorAfterDelete(selection.cursor, old_cursor);
1928 fixCursorAfterDelete(selection.start, old_cursor);
1929 fixCursorAfterDelete(selection.end, old_cursor);
1934 // don't delete anything if this is the ONLY paragraph!
1935 if (ownerParagraphs().size() == 1)
1938 // Do not delete empty paragraphs with keepempty set.
1939 if (old_cursor.par()->allowEmpty())
1942 // only do our magic if we changed paragraph
1943 if (old_cursor.par() == cursor.par())
1946 // record if we have deleted a paragraph
1947 // we can't possibly have deleted a paragraph before this point
1948 bool deleted = false;
1950 if (old_cursor.par()->empty() ||
1951 (old_cursor.par()->size() == 1 &&
1952 old_cursor.par()->isLineSeparator(0))) {
1953 // ok, we will delete anything
1954 LyXCursor tmpcursor;
1958 bool selection_position_was_oldcursor_position = (
1959 selection.cursor.par() == old_cursor.par()
1960 && selection.cursor.pos() == old_cursor.pos());
1962 if (getRow(old_cursor) != firstRow()) {
1963 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
1965 cursor = old_cursor; // that undo can restore the right cursor position
1966 #warning FIXME. --end() iterator is usable here
1967 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1968 while (endpit != ownerParagraphs().end() &&
1969 endpit->getDepth()) {
1973 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
1974 boost::prior(endpit));
1978 removeRow(getRow(old_cursor));
1980 ownerParagraphs().erase(old_cursor.par());
1982 RowList::iterator nextrow = boost::next(getRow(old_cursor));
1985 cursor = old_cursor; // that undo can restore the right cursor position
1986 #warning FIXME. --end() iterator is usable here
1987 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1988 while (endpit != ownerParagraphs().end() &&
1989 endpit->getDepth()) {
1993 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1997 removeRow(getRow(old_cursor));
1999 ownerParagraphs().erase(old_cursor.par());
2003 setCursorIntern(cursor.par(), cursor.pos());
2005 if (selection_position_was_oldcursor_position) {
2006 // correct selection
2007 selection.cursor = cursor;
2011 if (old_cursor.par()->stripLeadingSpaces()) {
2012 redoParagraph(old_cursor.par());
2014 setCursorIntern(cursor.par(), cursor.pos());
2015 selection.cursor = cursor;
2022 ParagraphList & LyXText::ownerParagraphs() const
2025 return inset_owner->paragraphs;
2027 return bv_owner->buffer()->paragraphs;
2031 bool LyXText::isInInset() const
2033 // Sub-level has non-null bv owner and non-null inset owner.
2034 return inset_owner != 0 && bv_owner != 0;
2038 int defaultRowHeight()
2040 LyXFont const font(LyXFont::ALL_SANE);
2041 return int(font_metrics::maxAscent(font)
2042 + font_metrics::maxDescent(font) * 1.5);