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"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
36 #include "lyxrow_funcs.h"
37 #include "metricsinfo.h"
38 #include "paragraph_funcs.h"
40 #include "insets/insetbibitem.h"
41 #include "insets/insetenv.h"
42 #include "insets/insetfloat.h"
43 #include "insets/insetwrap.h"
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
49 #include <boost/tuple/tuple.hpp>
53 using namespace lyx::support;
63 LyXText::LyXText(BufferView * bv)
64 : height(0), width(0), anchor_row_offset_(0),
65 inset_owner(0), the_locking_inset(0), bv_owner(bv)
67 anchor_row_ = rows().end();
71 LyXText::LyXText(BufferView * bv, InsetText * inset)
72 : height(0), width(0), anchor_row_offset_(0),
73 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
75 anchor_row_ = rows().end();
79 void LyXText::init(BufferView * bview)
87 anchor_row_ = rows().end();
88 anchor_row_offset_ = 0;
90 current_font = getFont(ownerParagraphs().begin(), 0);
92 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
94 setCursorIntern(rowlist_.begin()->par(), 0);
95 selection.cursor = cursor;
101 // Gets the fully instantiated font at a given position in a paragraph
102 // Basically the same routine as Paragraph::getFont() in paragraph.C.
103 // The difference is that this one is used for displaying, and thus we
104 // are allowed to make cosmetic improvements. For instance make footnotes
106 // If position is -1, we get the layout font of the paragraph.
107 // If position is -2, we get the font of the manual label of the paragraph.
108 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
112 LyXLayout_ptr const & layout = pit->layout();
114 BufferParams const & params = bv()->buffer()->params;
116 // We specialize the 95% common case:
117 if (!pit->getDepth()) {
118 if (layout->labeltype == LABEL_MANUAL
119 && pos < pit->beginningOfBody()) {
121 LyXFont f = pit->getFontSettings(params, pos);
123 pit->inInset()->getDrawFont(f);
124 return f.realize(layout->reslabelfont);
126 LyXFont f = pit->getFontSettings(params, pos);
128 pit->inInset()->getDrawFont(f);
129 return f.realize(layout->resfont);
133 // The uncommon case need not be optimized as much
137 if (pos < pit->beginningOfBody()) {
139 layoutfont = layout->labelfont;
142 layoutfont = layout->font;
145 LyXFont tmpfont = pit->getFontSettings(params, pos);
146 tmpfont.realize(layoutfont);
149 pit->inInset()->getDrawFont(tmpfont);
151 // Realize with the fonts of lesser depth.
152 tmpfont.realize(outerFont(pit, ownerParagraphs()));
153 tmpfont.realize(defaultfont_);
159 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
161 LyXLayout_ptr const & layout = pit->layout();
163 if (!pit->getDepth())
164 return layout->resfont;
166 LyXFont font = layout->font;
167 // Realize with the fonts of lesser depth.
168 font.realize(outerFont(pit, ownerParagraphs()));
169 font.realize(defaultfont_);
175 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
177 LyXLayout_ptr const & layout = pit->layout();
179 if (!pit->getDepth())
180 return layout->reslabelfont;
182 LyXFont font = layout->labelfont;
183 // Realize with the fonts of lesser depth.
184 font.realize(outerFont(pit, ownerParagraphs()));
185 font.realize(defaultfont_);
191 void LyXText::setCharFont(ParagraphList::iterator pit,
192 pos_type pos, LyXFont const & fnt,
195 BufferParams const & params = bv()->buffer()->params;
196 LyXFont font = getFont(pit, pos);
197 font.update(fnt, params.language, toggleall);
198 // Let the insets convert their font
199 if (pit->isInset(pos)) {
200 InsetOld * inset = pit->getInset(pos);
201 if (isEditableInset(inset)) {
202 static_cast<UpdatableInset *>(inset)
203 ->setFont(bv(), fnt, toggleall, true);
207 // Plug through to version below:
208 setCharFont(pit, pos, font);
212 void LyXText::setCharFont(
213 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
216 LyXLayout_ptr const & layout = pit->layout();
218 // Get concrete layout font to reduce against
221 if (pos < pit->beginningOfBody())
222 layoutfont = layout->labelfont;
224 layoutfont = layout->font;
226 // Realize against environment font information
227 if (pit->getDepth()) {
228 ParagraphList::iterator tp = pit;
229 while (!layoutfont.resolved() &&
230 tp != ownerParagraphs().end() &&
232 tp = outerHook(tp, ownerParagraphs());
233 if (tp != ownerParagraphs().end())
234 layoutfont.realize(tp->layout()->font);
238 layoutfont.realize(defaultfont_);
240 // Now, reduce font against full layout font
241 font.reduce(layoutfont);
243 pit->setFont(pos, font);
247 // removes the row and reset the touched counters
248 void LyXText::removeRow(RowList::iterator rit)
250 if (anchor_row_ == rit) {
251 if (rit != rows().begin()) {
252 anchor_row_ = boost::prior(rit);
253 anchor_row_offset_ += anchor_row_->height();
255 anchor_row_ = boost::next(rit);
256 anchor_row_offset_ -= rit->height();
260 // the text becomes smaller
261 height -= rit->height();
267 // remove all following rows of the paragraph of the specified row.
268 void LyXText::removeParagraph(RowList::iterator rit)
270 ParagraphList::iterator tmppit = rit->par();
273 while (rit != rows().end() && rit->par() == tmppit) {
274 RowList::iterator tmprit = boost::next(rit);
281 void LyXText::insertParagraph(ParagraphList::iterator pit,
282 RowList::iterator rit)
284 // insert a new row, starting at position 0
285 rit = rowlist_.insert(rit, Row(pit, 0));
287 // and now append the whole paragraph before the new row
288 appendParagraph(rit);
292 InsetOld * LyXText::getInset() const
294 ParagraphList::iterator pit = cursor.par();
295 pos_type const pos = cursor.pos();
297 if (pos < pit->size() && pit->isInset(pos)) {
298 return pit->getInset(pos);
304 void LyXText::toggleInset()
306 InsetOld * inset = getInset();
307 // is there an editable inset at cursor position?
308 if (!isEditableInset(inset)) {
309 // No, try to see if we are inside a collapsable inset
310 if (inset_owner && inset_owner->owner()
311 && inset_owner->owner()->isOpen()) {
312 bv()->unlockInset(inset_owner->owner());
313 inset_owner->owner()->close(bv());
314 bv()->getLyXText()->cursorRight(bv());
318 //bv()->owner()->message(inset->editMessage());
320 // do we want to keep this?? (JMarc)
321 if (!isHighlyEditableInset(inset))
322 recordUndo(bv(), Undo::ATOMIC);
333 /* used in setlayout */
334 // Asger is not sure we want to do this...
335 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
338 LyXLayout_ptr const & layout = par.layout();
339 pos_type const psize = par.size();
342 for (pos_type pos = 0; pos < psize; ++pos) {
343 if (pos < par.beginningOfBody())
344 layoutfont = layout->labelfont;
346 layoutfont = layout->font;
348 LyXFont tmpfont = par.getFontSettings(params, pos);
349 tmpfont.reduce(layoutfont);
350 par.setFont(pos, tmpfont);
355 ParagraphList::iterator
356 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
357 LyXCursor & send_cur,
358 string const & layout)
360 ParagraphList::iterator endpit = boost::next(send_cur.par());
361 ParagraphList::iterator undoendpit = endpit;
362 ParagraphList::iterator pars_end = ownerParagraphs().end();
364 if (endpit != pars_end && endpit->getDepth()) {
365 while (endpit != pars_end && endpit->getDepth()) {
369 } else if (endpit != pars_end) {
370 // because of parindents etc.
374 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
376 // ok we have a selection. This is always between sstart_cur
377 // and sel_end cursor
379 ParagraphList::iterator pit = sstart_cur.par();
380 ParagraphList::iterator epit = boost::next(send_cur.par());
382 LyXLayout_ptr const & lyxlayout =
383 bv()->buffer()->params.getLyXTextClass()[layout];
386 pit->applyLayout(lyxlayout);
387 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
388 ParagraphList::iterator fppit = pit;
389 fppit->params().spaceTop(lyxlayout->fill_top ?
390 VSpace(VSpace::VFILL)
391 : VSpace(VSpace::NONE));
392 fppit->params().spaceBottom(lyxlayout->fill_bottom ?
393 VSpace(VSpace::VFILL)
394 : VSpace(VSpace::NONE));
395 if (lyxlayout->margintype == MARGIN_MANUAL)
396 pit->setLabelWidthString(lyxlayout->labelstring());
399 } while (pit != epit);
405 // set layout over selection and make a total rebreak of those paragraphs
406 void LyXText::setLayout(string const & layout)
408 LyXCursor tmpcursor = cursor; // store the current cursor
410 // if there is no selection just set the layout
411 // of the current paragraph
412 if (!selection.set()) {
413 selection.start = cursor; // dummy selection
414 selection.end = cursor;
417 // special handling of new environment insets
418 BufferParams const & params = bv()->buffer()->params;
419 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
420 if (lyxlayout->is_environment) {
421 // move everything in a new environment inset
422 lyxerr << "setting layout " << layout << endl;
423 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
424 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
425 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
426 InsetOld * inset = new InsetEnvironment(params, layout);
427 if (bv()->insertInset(inset)) {
429 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
436 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
437 selection.end, layout);
438 redoParagraphs(selection.start.par(), endpit);
440 // we have to reset the selection, because the
441 // geometry could have changed
442 setCursor(selection.start.par(), selection.start.pos(), false);
443 selection.cursor = cursor;
444 setCursor(selection.end.par(), selection.end.pos(), false);
448 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
452 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
454 ParagraphList::iterator pit(cursor.par());
455 ParagraphList::iterator end(cursor.par());
456 ParagraphList::iterator start = pit;
458 if (selection.set()) {
459 pit = selection.start.par();
460 end = selection.end.par();
464 ParagraphList::iterator pastend = boost::next(end);
467 recordUndo(bv(), Undo::ATOMIC, start, end);
469 bool changed = false;
471 int prev_after_depth = 0;
472 #warning parlist ... could be nicer ?
473 if (start != ownerParagraphs().begin()) {
474 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
478 int const depth = pit->params().depth();
479 if (type == bv_funcs::INC_DEPTH) {
480 if (depth < prev_after_depth
481 && pit->layout()->labeltype != LABEL_BIBLIO) {
484 pit->params().depth(depth + 1);
491 pit->params().depth(depth - 1);
494 prev_after_depth = pit->getMaxDepthAfter();
507 redoParagraphs(start, pastend);
509 // We need to actually move the text->cursor. I don't
510 // understand why ...
511 LyXCursor tmpcursor = cursor;
513 // we have to reset the visual selection because the
514 // geometry could have changed
515 if (selection.set()) {
516 setCursor(selection.start.par(), selection.start.pos());
517 selection.cursor = cursor;
518 setCursor(selection.end.par(), selection.end.pos());
521 // this handles the counter labels, and also fixes up
522 // depth values for follow-on (child) paragraphs
526 setCursor(tmpcursor.par(), tmpcursor.pos());
532 // set font over selection and make a total rebreak of those paragraphs
533 void LyXText::setFont(LyXFont const & font, bool toggleall)
535 // if there is no selection just set the current_font
536 if (!selection.set()) {
537 // Determine basis font
539 if (cursor.pos() < cursor.par()->beginningOfBody()) {
540 layoutfont = getLabelFont(cursor.par());
542 layoutfont = getLayoutFont(cursor.par());
544 // Update current font
545 real_current_font.update(font,
546 bv()->buffer()->params.language,
549 // Reduce to implicit settings
550 current_font = real_current_font;
551 current_font.reduce(layoutfont);
552 // And resolve it completely
553 real_current_font.realize(layoutfont);
558 LyXCursor tmpcursor = cursor; // store the current cursor
560 // ok we have a selection. This is always between sel_start_cursor
561 // and sel_end cursor
563 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
565 cursor = selection.start;
566 while (cursor.par() != selection.end.par() ||
567 cursor.pos() < selection.end.pos())
569 if (cursor.pos() < cursor.par()->size()) {
570 // an open footnote should behave like a closed one
571 setCharFont(cursor.par(), cursor.pos(),
573 cursor.pos(cursor.pos() + 1);
576 cursor.par(boost::next(cursor.par()));
581 redoParagraph(selection.start.par());
583 // we have to reset the selection, because the
584 // geometry could have changed, but we keep
585 // it for user convenience
586 setCursor(selection.start.par(), selection.start.pos());
587 selection.cursor = cursor;
588 setCursor(selection.end.par(), selection.end.pos());
590 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
591 tmpcursor.boundary());
595 void LyXText::redoHeightOfParagraph()
597 RowList::iterator tmprow = cursorRow();
599 setHeightOfRow(tmprow);
601 while (tmprow != rows().begin()
602 && boost::prior(tmprow)->par() == tmprow->par()) {
604 setHeightOfRow(tmprow);
607 setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
611 RowList::iterator LyXText::firstRow(ParagraphList::iterator pit)
613 RowList::iterator rit;
614 for (rit = rows().begin(); rit != rows().end(); ++rit)
615 if (rit->par() == pit)
621 // rebreaks all paragraphs between the specified pars
622 // This function is needed after SetLayout and SetFont etc.
623 void LyXText::redoParagraphs(ParagraphList::iterator start,
624 ParagraphList::iterator end)
626 for ( ; start != end; ++start)
627 redoParagraph(start);
631 void LyXText::redoParagraph(ParagraphList::iterator pit)
633 RowList::iterator rit = firstRow(pit);
635 // remove paragraph from rowlist
636 while (rit != rows().end() && rit->par() == pit) {
637 RowList::iterator rit2 = rit++;
641 // reinsert the paragraph
642 // insert a new row, starting at position 0
644 rit = rowlist_.insert(rit, newrow);
646 // and now append the whole paragraph before the new row
647 pos_type const last = rit->par()->size();
651 pos_type z = rowBreakPoint(*rit);
653 RowList::iterator tmprow = rit;
657 Row newrow(rit->par(), z);
658 rit = rowlist_.insert(boost::next(rit), newrow);
663 // Set the dimensions of the row
664 // fixed fill setting now by calling inset->update() in
665 // SingleWidth when needed!
666 tmprow->fill(fill(tmprow, workWidth()));
667 setHeightOfRow(tmprow);
671 setHeightOfRow(rows().begin());
675 void LyXText::fullRebreak()
677 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
678 setCursorIntern(cursor.par(), cursor.pos());
679 selection.cursor = cursor;
683 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
685 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
686 //Assert(mi.base.textwidth);
693 anchor_row_ = rows().end();
694 anchor_row_offset_ = 0;
696 ParagraphList::iterator pit = ownerParagraphs().begin();
697 ParagraphList::iterator end = ownerParagraphs().end();
699 for (; pit != end; ++pit) {
700 InsetList::iterator ii = pit->insetlist.begin();
701 InsetList::iterator iend = pit->insetlist.end();
702 for (; ii != iend; ++ii) {
705 #warning FIXME: pos != 0
706 m.base.font = getFont(pit, 0);
707 ii->inset->metrics(m, dim);
711 // insert a new row, starting at position 0
713 RowList::iterator rit = rowlist_.insert(rowlist_.end(), newrow);
715 // and now append the whole paragraph before the new row
716 appendParagraph(rit);
723 //lyxerr << "height 0: " << height << endl;
724 //for (RowList::iterator rit = rows().begin(); rit != rows().end(); ++rit) {
725 // height += rit->height();
727 //lyxerr << "height 1: " << height << endl;
730 dim.asc = rows().begin()->ascent_of_text();
731 dim.des = height - dim.asc;
732 dim.wid = std::max(mi.base.textwidth, int(width));
736 void LyXText::partialRebreak()
738 if (rows().empty()) {
742 breakAgain(rows().begin());
746 // important for the screen
749 // the cursor set functions have a special mechanism. When they
750 // realize, that you left an empty paragraph, they will delete it.
751 // They also delete the corresponding row
753 // need the selection cursor:
754 void LyXText::setSelection()
756 TextCursor::setSelection();
761 void LyXText::clearSelection()
763 TextCursor::clearSelection();
765 // reset this in the bv_owner!
766 if (bv_owner && bv_owner->text)
767 bv_owner->text->xsel_cache.set(false);
771 void LyXText::cursorHome()
773 setCursor(cursor.par(), cursorRow()->pos());
777 void LyXText::cursorEnd()
779 if (cursor.par()->empty())
782 RowList::iterator rit = cursorRow();
783 RowList::iterator next_rit = boost::next(rit);
784 ParagraphList::iterator pit = rit->par();
785 pos_type last_pos = lastPos(*this, rit);
787 if (next_rit == rows().end() || next_rit->par() != pit) {
791 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
796 setCursor(pit, last_pos);
800 void LyXText::cursorTop()
802 setCursor(ownerParagraphs().begin(), 0);
806 void LyXText::cursorBottom()
808 ParagraphList::iterator lastpit =
809 boost::prior(ownerParagraphs().end());
810 setCursor(lastpit, lastpit->size());
814 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
816 // If the mask is completely neutral, tell user
817 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
818 // Could only happen with user style
819 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
823 // Try implicit word selection
824 // If there is a change in the language the implicit word selection
826 LyXCursor resetCursor = cursor;
827 bool implicitSelection = (font.language() == ignore_language
828 && font.number() == LyXFont::IGNORE)
829 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
832 setFont(font, toggleall);
834 // Implicit selections are cleared afterwards
835 //and cursor is set to the original position.
836 if (implicitSelection) {
838 cursor = resetCursor;
839 setCursor(cursor.par(), cursor.pos());
840 selection.cursor = cursor;
845 string LyXText::getStringToIndex()
847 // Try implicit word selection
848 // If there is a change in the language the implicit word selection
850 LyXCursor const reset_cursor = cursor;
851 bool const implicitSelection =
852 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
855 if (!selection.set())
856 bv()->owner()->message(_("Nothing to index!"));
857 else if (selection.start.par() != selection.end.par())
858 bv()->owner()->message(_("Cannot index more than one paragraph!"));
860 idxstring = selectionAsString(bv()->buffer(), false);
862 // Reset cursors to their original position.
863 cursor = reset_cursor;
864 setCursor(cursor.par(), cursor.pos());
865 selection.cursor = cursor;
867 // Clear the implicit selection.
868 if (implicitSelection)
875 // the DTP switches for paragraphs. LyX will store them in the first
876 // physicla paragraph. When a paragraph is broken, the top settings rest,
877 // the bottom settings are given to the new one. So I can make shure,
878 // they do not duplicate themself and you cannnot make dirty things with
881 void LyXText::setParagraph(bool line_top, bool line_bottom,
882 bool pagebreak_top, bool pagebreak_bottom,
883 VSpace const & space_top,
884 VSpace const & space_bottom,
885 Spacing const & spacing,
887 string const & labelwidthstring,
890 LyXCursor tmpcursor = cursor;
891 if (!selection.set()) {
892 selection.start = cursor;
893 selection.end = cursor;
896 // make sure that the depth behind the selection are restored, too
897 ParagraphList::iterator endpit = boost::next(selection.end.par());
898 ParagraphList::iterator undoendpit = endpit;
899 ParagraphList::iterator pars_end = ownerParagraphs().end();
901 if (endpit != pars_end && endpit->getDepth()) {
902 while (endpit != pars_end && endpit->getDepth()) {
906 } else if (endpit != pars_end) {
907 // because of parindents etc.
911 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
912 boost::prior(undoendpit));
915 ParagraphList::iterator tmppit = selection.end.par();
917 while (tmppit != boost::prior(selection.start.par())) {
918 setCursor(tmppit, 0);
920 ParagraphList::iterator pit = cursor.par();
921 ParagraphParameters & params = pit->params();
923 params.lineTop(line_top);
924 params.lineBottom(line_bottom);
925 params.pagebreakTop(pagebreak_top);
926 params.pagebreakBottom(pagebreak_bottom);
927 params.spaceTop(space_top);
928 params.spaceBottom(space_bottom);
929 params.spacing(spacing);
930 // does the layout allow the new alignment?
931 LyXLayout_ptr const & layout = pit->layout();
933 if (align == LYX_ALIGN_LAYOUT)
934 align = layout->align;
935 if (align & layout->alignpossible) {
936 if (align == layout->align)
937 params.align(LYX_ALIGN_LAYOUT);
941 pit->setLabelWidthString(labelwidthstring);
942 params.noindent(noindent);
943 tmppit = boost::prior(pit);
946 redoParagraphs(selection.start.par(), endpit);
949 setCursor(selection.start.par(), selection.start.pos());
950 selection.cursor = cursor;
951 setCursor(selection.end.par(), selection.end.pos());
953 setCursor(tmpcursor.par(), tmpcursor.pos());
959 // set the counter of a paragraph. This includes the labels
960 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
962 LyXTextClass const & textclass = buf->params.getLyXTextClass();
963 LyXLayout_ptr const & layout = pit->layout();
965 if (pit != ownerParagraphs().begin()) {
967 pit->params().appendix(boost::prior(pit)->params().appendix());
968 if (!pit->params().appendix() &&
969 pit->params().startOfAppendix()) {
970 pit->params().appendix(true);
971 textclass.counters().reset();
973 pit->enumdepth = boost::prior(pit)->enumdepth;
974 pit->itemdepth = boost::prior(pit)->itemdepth;
976 pit->params().appendix(pit->params().startOfAppendix());
981 /* Maybe we have to increment the enumeration depth.
982 * BUT, enumeration in a footnote is considered in isolation from its
983 * surrounding paragraph so don't increment if this is the
984 * first line of the footnote
985 * AND, bibliographies can't have their depth changed ie. they
986 * are always of depth 0
988 if (pit != ownerParagraphs().begin()
989 && boost::prior(pit)->getDepth() < pit->getDepth()
990 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
991 && pit->enumdepth < 3
992 && layout->labeltype != LABEL_BIBLIO) {
996 // Maybe we have to decrement the enumeration depth, see note above
997 if (pit != ownerParagraphs().begin()
998 && boost::prior(pit)->getDepth() > pit->getDepth()
999 && layout->labeltype != LABEL_BIBLIO) {
1000 pit->enumdepth = depthHook(pit, ownerParagraphs(),
1001 pit->getDepth())->enumdepth;
1004 if (!pit->params().labelString().empty()) {
1005 pit->params().labelString(string());
1008 if (layout->margintype == MARGIN_MANUAL) {
1009 if (pit->params().labelWidthString().empty()) {
1010 pit->setLabelWidthString(layout->labelstring());
1013 pit->setLabelWidthString(string());
1016 // is it a layout that has an automatic label?
1017 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1018 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1022 if (i >= 0 && i <= buf->params.secnumdepth) {
1026 textclass.counters().step(layout->latexname());
1028 // Is there a label? Useful for Chapter layout
1029 if (!pit->params().appendix()) {
1030 s << buf->B_(layout->labelstring());
1032 s << buf->B_(layout->labelstring_appendix());
1035 // Use of an integer is here less than elegant. For now.
1036 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1037 if (!pit->params().appendix()) {
1038 numbertype = "sectioning";
1040 numbertype = "appendix";
1041 if (pit->isRightToLeftPar(buf->params))
1042 langtype = "hebrew";
1048 << textclass.counters()
1049 .numberLabel(layout->latexname(),
1050 numbertype, langtype, head);
1052 pit->params().labelString(STRCONV(s.str()));
1054 // reset enum counters
1055 textclass.counters().reset("enum");
1056 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1057 textclass.counters().reset("enum");
1058 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1060 // Yes I know this is a really, really! bad solution
1062 string enumcounter("enum");
1064 switch (pit->enumdepth) {
1073 enumcounter += "iv";
1076 // not a valid enumdepth...
1080 textclass.counters().step(enumcounter);
1082 s << textclass.counters()
1083 .numberLabel(enumcounter, "enumeration");
1084 pit->params().labelString(STRCONV(s.str()));
1086 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1087 textclass.counters().step("bibitem");
1088 int number = textclass.counters().value("bibitem");
1089 if (pit->bibitem()) {
1090 pit->bibitem()->setCounter(number);
1091 pit->params().labelString(layout->labelstring());
1093 // In biblio should't be following counters but...
1095 string s = buf->B_(layout->labelstring());
1097 // the caption hack:
1098 if (layout->labeltype == LABEL_SENSITIVE) {
1099 ParagraphList::iterator end = ownerParagraphs().end();
1100 ParagraphList::iterator tmppit = pit;
1103 while (tmppit != end && tmppit->inInset()
1104 // the single '=' is intended below
1105 && (in = tmppit->inInset()->owner()))
1107 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1108 in->lyxCode() == InsetOld::WRAP_CODE) {
1112 tmppit = ownerParagraphs().begin();
1113 for ( ; tmppit != end; ++tmppit)
1114 if (&*tmppit == in->parOwner())
1122 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1123 type = static_cast<InsetFloat*>(in)->params().type;
1124 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1125 type = static_cast<InsetWrap*>(in)->params().type;
1129 Floating const & fl = textclass.floats().getType(type);
1131 textclass.counters().step(fl.type());
1133 // Doesn't work... yet.
1134 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1136 // par->SetLayout(0);
1137 // s = layout->labelstring;
1138 s = _("Senseless: ");
1141 pit->params().labelString(s);
1143 // reset the enumeration counter. They are always reset
1144 // when there is any other layout between
1145 // Just fall-through between the cases so that all
1146 // enum counters deeper than enumdepth is also reset.
1147 switch (pit->enumdepth) {
1149 textclass.counters().reset("enumi");
1151 textclass.counters().reset("enumii");
1153 textclass.counters().reset("enumiii");
1155 textclass.counters().reset("enumiv");
1161 // Updates all counters. Paragraphs with changed label string will be rebroken
1162 void LyXText::updateCounters()
1164 RowList::iterator rowit = rows().begin();
1165 ParagraphList::iterator pit = rowit->par();
1167 // CHECK if this is really needed. (Lgb)
1168 bv()->buffer()->params.getLyXTextClass().counters().reset();
1170 ParagraphList::iterator beg = ownerParagraphs().begin();
1171 ParagraphList::iterator end = ownerParagraphs().end();
1172 for (; pit != end; ++pit) {
1173 while (rowit->par() != pit)
1176 string const oldLabel = pit->params().labelString();
1178 size_t maxdepth = 0;
1180 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1182 if (pit->params().depth() > maxdepth)
1183 pit->params().depth(maxdepth);
1185 // setCounter can potentially change the labelString.
1186 setCounter(bv()->buffer(), pit);
1188 string const & newLabel = pit->params().labelString();
1190 if (oldLabel.empty() && !newLabel.empty()) {
1191 removeParagraph(rowit);
1192 appendParagraph(rowit);
1198 void LyXText::insertInset(InsetOld * inset)
1200 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1202 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1204 cursor.par()->insertInset(cursor.pos(), inset);
1205 // Just to rebreak and refresh correctly.
1206 // The character will not be inserted a second time
1207 insertChar(Paragraph::META_INSET);
1208 // If we enter a highly editable inset the cursor should be to before
1209 // the inset. This couldn't happen before as Undo was not handled inside
1210 // inset now after the Undo LyX tries to call inset->Edit(...) again
1211 // and cannot do this as the cursor is behind the inset and GetInset
1212 // does not return the inset!
1213 if (isHighlyEditableInset(inset)) {
1220 void LyXText::cutSelection(bool doclear, bool realcut)
1222 // Stuff what we got on the clipboard. Even if there is no selection.
1224 // There is a problem with having the stuffing here in that the
1225 // larger the selection the slower LyX will get. This can be
1226 // solved by running the line below only when the selection has
1227 // finished. The solution used currently just works, to make it
1228 // faster we need to be more clever and probably also have more
1229 // calls to stuffClipboard. (Lgb)
1230 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1232 // This doesn't make sense, if there is no selection
1233 if (!selection.set())
1236 // OK, we have a selection. This is always between selection.start
1237 // and selection.end
1239 // make sure that the depth behind the selection are restored, too
1240 ParagraphList::iterator endpit = boost::next(selection.end.par());
1241 ParagraphList::iterator undoendpit = endpit;
1242 ParagraphList::iterator pars_end = ownerParagraphs().end();
1244 if (endpit != pars_end && endpit->getDepth()) {
1245 while (endpit != pars_end && endpit->getDepth()) {
1247 undoendpit = endpit;
1249 } else if (endpit != pars_end) {
1250 // because of parindents etc.
1254 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1255 boost::prior(undoendpit));
1258 endpit = selection.end.par();
1259 int endpos = selection.end.pos();
1261 boost::tie(endpit, endpos) = realcut ?
1262 CutAndPaste::cutSelection(bv()->buffer()->params,
1264 selection.start.par(), endpit,
1265 selection.start.pos(), endpos,
1266 bv()->buffer()->params.textclass,
1268 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1270 selection.start.par(), endpit,
1271 selection.start.pos(), endpos,
1273 // sometimes necessary
1275 selection.start.par()->stripLeadingSpaces();
1277 redoParagraphs(selection.start.par(), boost::next(endpit));
1278 // cutSelection can invalidate the cursor so we need to set
1280 // we prefer the end for when tracking changes
1284 // need a valid cursor. (Lgb)
1287 setCursor(cursor.par(), cursor.pos());
1288 selection.cursor = cursor;
1293 void LyXText::copySelection()
1295 // stuff the selection onto the X clipboard, from an explicit copy request
1296 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1298 // this doesnt make sense, if there is no selection
1299 if (!selection.set())
1302 // ok we have a selection. This is always between selection.start
1303 // and sel_end cursor
1305 // copy behind a space if there is one
1306 while (selection.start.par()->size() > selection.start.pos()
1307 && selection.start.par()->isLineSeparator(selection.start.pos())
1308 && (selection.start.par() != selection.end.par()
1309 || selection.start.pos() < selection.end.pos()))
1310 selection.start.pos(selection.start.pos() + 1);
1312 CutAndPaste::copySelection(selection.start.par(),
1313 selection.end.par(),
1314 selection.start.pos(), selection.end.pos(),
1315 bv()->buffer()->params.textclass);
1319 void LyXText::pasteSelection(size_t sel_index)
1321 // this does not make sense, if there is nothing to paste
1322 if (!CutAndPaste::checkPastePossible())
1325 recordUndo(bv(), Undo::INSERT, cursor.par());
1327 ParagraphList::iterator endpit;
1332 boost::tie(ppp, endpit) =
1333 CutAndPaste::pasteSelection(*bv()->buffer(),
1335 cursor.par(), cursor.pos(),
1336 bv()->buffer()->params.textclass,
1338 bufferErrors(*bv()->buffer(), el);
1339 bv()->showErrorList(_("Paste"));
1341 redoParagraphs(cursor.par(), endpit);
1343 setCursor(cursor.par(), cursor.pos());
1346 selection.cursor = cursor;
1347 setCursor(ppp.first, ppp.second);
1353 void LyXText::setSelectionRange(lyx::pos_type length)
1358 selection.cursor = cursor;
1365 // simple replacing. The font of the first selected character is used
1366 void LyXText::replaceSelectionWithString(string const & str)
1368 recordUndo(bv(), Undo::ATOMIC);
1371 if (!selection.set()) { // create a dummy selection
1372 selection.end = cursor;
1373 selection.start = cursor;
1376 // Get font setting before we cut
1377 pos_type pos = selection.end.pos();
1378 LyXFont const font = selection.start.par()
1379 ->getFontSettings(bv()->buffer()->params,
1380 selection.start.pos());
1382 // Insert the new string
1383 string::const_iterator cit = str.begin();
1384 string::const_iterator end = str.end();
1385 for (; cit != end; ++cit) {
1386 selection.end.par()->insertChar(pos, (*cit), font);
1390 // Cut the selection
1391 cutSelection(true, false);
1397 // needed to insert the selection
1398 void LyXText::insertStringAsLines(string const & str)
1400 ParagraphList::iterator pit = cursor.par();
1401 pos_type pos = cursor.pos();
1402 ParagraphList::iterator endpit = boost::next(cursor.par());
1404 recordUndo(bv(), Undo::ATOMIC);
1406 // only to be sure, should not be neccessary
1409 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1411 redoParagraphs(cursor.par(), endpit);
1412 setCursor(cursor.par(), cursor.pos());
1413 selection.cursor = cursor;
1414 setCursor(pit, pos);
1419 // turns double-CR to single CR, others where converted into one
1420 // blank. Then InsertStringAsLines is called
1421 void LyXText::insertStringAsParagraphs(string const & str)
1423 string linestr(str);
1424 bool newline_inserted = false;
1425 string::size_type const siz = linestr.length();
1427 for (string::size_type i = 0; i < siz; ++i) {
1428 if (linestr[i] == '\n') {
1429 if (newline_inserted) {
1430 // we know that \r will be ignored by
1431 // InsertStringA. Of course, it is a dirty
1432 // trick, but it works...
1433 linestr[i - 1] = '\r';
1437 newline_inserted = true;
1439 } else if (IsPrintable(linestr[i])) {
1440 newline_inserted = false;
1443 insertStringAsLines(linestr);
1447 void LyXText::checkParagraph(ParagraphList::iterator pit, pos_type pos)
1449 breakAgain(getRow(pit, pos));
1450 setCursorIntern(cursor.par(), cursor.pos(), false, cursor.boundary());
1454 // returns false if inset wasn't found
1455 bool LyXText::updateInset(InsetOld * inset)
1457 // first check the current paragraph
1458 int pos = cursor.par()->getPositionOfInset(inset);
1460 checkParagraph(cursor.par(), pos);
1464 // check every paragraph
1465 ParagraphList::iterator par = ownerParagraphs().begin();
1466 ParagraphList::iterator end = ownerParagraphs().end();
1467 for (; par != end; ++par) {
1468 pos = par->getPositionOfInset(inset);
1470 checkParagraph(par, pos);
1479 bool LyXText::setCursor(ParagraphList::iterator pit,
1481 bool setfont, bool boundary)
1483 LyXCursor old_cursor = cursor;
1484 setCursorIntern(pit, pos, setfont, boundary);
1485 return deleteEmptyParagraphMechanism(old_cursor);
1489 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1490 pos_type pos, bool boundary)
1492 Assert(pit != ownerParagraphs().end());
1496 cur.boundary(boundary);
1500 // get the cursor y position in text
1502 RowList::iterator row = getRow(pit, pos, y);
1503 RowList::iterator beg = rows().begin();
1505 RowList::iterator old_row = row;
1506 // if we are before the first char of this row and are still in the
1507 // same paragraph and there is a previous row then put the cursor on
1508 // the end of the previous row
1509 cur.iy(y + row->baseline());
1512 boost::prior(row)->par() == row->par() &&
1513 pos < pit->size() &&
1514 pit->getChar(pos) == Paragraph::META_INSET) {
1515 InsetOld * ins = pit->getInset(pos);
1516 if (ins && (ins->needFullRow() || ins->display())) {
1522 // y is now the beginning of the cursor row
1523 y += row->baseline();
1524 // y is now the cursor baseline
1527 pos_type last = lastPrintablePos(*this, old_row);
1529 // None of these should happen, but we're scaredy-cats
1530 if (pos > pit->size()) {
1531 lyxerr << "dont like 1 please report" << endl;
1534 } else if (pos > last + 1) {
1535 lyxerr << "dont like 2 please report" << endl;
1536 // This shouldn't happen.
1539 } else if (pos < row->pos()) {
1540 lyxerr << "dont like 3 please report" << endl;
1545 // now get the cursors x position
1546 float x = getCursorX(row, pos, last, boundary);
1549 if (old_row != row) {
1550 x = getCursorX(old_row, pos, last, boundary);
1554 /* We take out this for the time being because 1) the redraw code is not
1555 prepared to this yet and 2) because some good policy has yet to be decided
1556 while editting: for instance how to act on rows being created/deleted
1560 //if the cursor is in a visible row, anchor to it
1562 if (topy < y && y < topy + bv()->workHeight())
1568 float LyXText::getCursorX(RowList::iterator rit,
1569 pos_type pos, pos_type last, bool boundary) const
1571 pos_type cursor_vpos = 0;
1573 double fill_separator;
1575 double fill_label_hfill;
1576 // This call HAS to be here because of the BidiTables!!!
1577 prepareToPrint(rit, x, fill_separator, fill_hfill,
1580 ParagraphList::iterator rit_par = rit->par();
1581 pos_type const rit_pos = rit->pos();
1584 cursor_vpos = rit_pos;
1585 else if (pos > last && !boundary)
1586 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1587 ? rit_pos : last + 1;
1588 else if (pos > rit_pos && (pos > last || boundary))
1589 /// Place cursor after char at (logical) position pos - 1
1590 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1591 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1593 /// Place cursor before char at (logical) position pos
1594 cursor_vpos = (bidi_level(pos) % 2 == 0)
1595 ? log2vis(pos) : log2vis(pos) + 1;
1597 pos_type body_pos = rit_par->beginningOfBody();
1599 (body_pos - 1 > last || !rit_par->isLineSeparator(body_pos - 1)))
1602 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1603 pos_type pos = vis2log(vpos);
1604 if (body_pos > 0 && pos == body_pos - 1) {
1605 x += fill_label_hfill +
1606 font_metrics::width(
1607 rit_par->layout()->labelsep, getLabelFont(rit_par));
1608 if (rit_par->isLineSeparator(body_pos - 1))
1609 x -= singleWidth(rit_par, body_pos - 1);
1612 if (hfillExpansion(*this, rit, pos)) {
1613 x += singleWidth(rit_par, pos);
1614 if (pos >= body_pos)
1617 x += fill_label_hfill;
1618 } else if (rit_par->isSeparator(pos)) {
1619 x += singleWidth(rit_par, pos);
1620 if (pos >= body_pos)
1621 x += fill_separator;
1623 x += singleWidth(rit_par, pos);
1629 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1630 pos_type pos, bool setfont, bool boundary)
1632 UpdatableInset * it = pit->inInset();
1634 if (it != inset_owner) {
1635 lyxerr[Debug::INSETS] << "InsetText is " << it
1637 << "inset_owner is "
1638 << inset_owner << endl;
1639 #ifdef WITH_WARNINGS
1640 #warning I believe this code is wrong. (Lgb)
1641 #warning Jürgen, have a look at this. (Lgb)
1642 #warning Hmmm, I guess you are right but we
1643 #warning should verify when this is needed
1645 // Jürgen, would you like to have a look?
1646 // I guess we need to move the outer cursor
1647 // and open and lock the inset (bla bla bla)
1648 // stuff I don't know... so can you have a look?
1650 // I moved the lyxerr stuff in here so we can see if
1651 // this is actually really needed and where!
1653 // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1658 setCursor(cursor, pit, pos, boundary);
1664 void LyXText::setCurrentFont()
1666 pos_type pos = cursor.pos();
1667 ParagraphList::iterator pit = cursor.par();
1669 if (cursor.boundary() && pos > 0)
1673 if (pos == pit->size())
1675 else // potentional bug... BUG (Lgb)
1676 if (pit->isSeparator(pos)) {
1677 if (pos > cursorRow()->pos() &&
1678 bidi_level(pos) % 2 ==
1679 bidi_level(pos - 1) % 2)
1681 else if (pos + 1 < pit->size())
1686 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1687 real_current_font = getFont(pit, pos);
1689 if (cursor.pos() == pit->size() &&
1690 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1691 !cursor.boundary()) {
1692 Language const * lang =
1693 pit->getParLanguage(bv()->buffer()->params);
1694 current_font.setLanguage(lang);
1695 current_font.setNumber(LyXFont::OFF);
1696 real_current_font.setLanguage(lang);
1697 real_current_font.setNumber(LyXFont::OFF);
1702 // returns the column near the specified x-coordinate of the row
1703 // x is set to the real beginning of this column
1705 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1708 double fill_separator;
1710 double fill_label_hfill;
1712 prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1714 pos_type vc = rit->pos();
1715 pos_type last = lastPrintablePos(*this, rit);
1718 ParagraphList::iterator rit_par = rit->par();
1719 LyXLayout_ptr const & layout = rit->par()->layout();
1721 bool left_side = false;
1723 pos_type body_pos = rit_par->beginningOfBody();
1724 double last_tmpx = tmpx;
1727 (body_pos - 1 > last ||
1728 !rit_par->isLineSeparator(body_pos - 1)))
1731 // check for empty row
1732 if (!rit_par->size()) {
1737 while (vc <= last && tmpx <= x) {
1740 if (body_pos > 0 && c == body_pos - 1) {
1741 tmpx += fill_label_hfill +
1742 font_metrics::width(layout->labelsep, getLabelFont(rit_par));
1743 if (rit_par->isLineSeparator(body_pos - 1))
1744 tmpx -= singleWidth(rit_par, body_pos - 1);
1747 if (hfillExpansion(*this, rit, c)) {
1748 tmpx += singleWidth(rit_par, c);
1752 tmpx += fill_label_hfill;
1753 } else if (rit_par->isSeparator(c)) {
1754 tmpx += singleWidth(rit_par, c);
1756 tmpx += fill_separator;
1758 tmpx += singleWidth(rit_par, c);
1763 if ((tmpx + last_tmpx) / 2 > x) {
1768 if (vc > last + 1) // This shouldn't happen.
1772 // This (rtl_support test) is not needed, but gives
1773 // some speedup if rtl_support=false
1774 RowList::iterator next_rit = boost::next(rit);
1776 bool const lastrow = lyxrc.rtl_support &&
1777 (next_rit == rowlist_.end() ||
1778 next_rit->par() != rit_par);
1780 // If lastrow is false, we don't need to compute
1781 // the value of rtl.
1782 bool const rtl = (lastrow)
1783 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1786 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1787 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1789 else if (vc == rit->pos()) {
1791 if (bidi_level(c) % 2 == 1)
1794 c = vis2log(vc - 1);
1795 bool const rtl = (bidi_level(c) % 2 == 1);
1796 if (left_side == rtl) {
1798 boundary = isBoundary(bv()->buffer(), *rit_par, c);
1802 if (rit->pos() <= last && c > last
1803 && rit_par->isNewline(last)) {
1804 if (bidi_level(last) % 2 == 0)
1805 tmpx -= singleWidth(rit_par, last);
1807 tmpx += singleWidth(rit_par, last);
1817 void LyXText::setCursorFromCoordinates(int x, int y)
1819 //LyXCursor old_cursor = cursor;
1820 setCursorFromCoordinates(cursor, x, y);
1822 #warning DEPM disabled, otherwise crash when entering new table
1823 //deleteEmptyParagraphMechanism(old_cursor);
1830 * return true if the cursor given is at the end of a row,
1831 * and the next row is filled by an inset that spans an entire
1834 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1836 RowList::iterator row = lt.getRow(cur);
1837 if (boost::next(row) == lt.rows().end())
1840 Row const & next = *boost::next(row);
1842 if (next.pos() != cur.pos() || next.par() != cur.par())
1845 if (cur.pos() == cur.par()->size()
1846 || !cur.par()->isInset(cur.pos()))
1849 InsetOld const * inset = cur.par()->getInset(cur.pos());
1850 if (inset->needFullRow() || inset->display())
1858 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1860 // Get the row first.
1862 RowList::iterator row = getRowNearY(y);
1864 pos_type const column = getColumnNearX(row, x, bound);
1865 cur.par(row->par());
1866 cur.pos(row->pos() + column);
1868 cur.y(y + row->baseline());
1870 if (beforeFullRowInset(*this, cur)) {
1871 pos_type const last = lastPrintablePos(*this, row);
1872 RowList::iterator next_row = boost::next(row);
1874 float x = getCursorX(next_row, cur.pos(), last, bound);
1876 cur.iy(y + row->height() + next_row->baseline());
1881 cur.boundary(bound);
1885 void LyXText::cursorLeft(bool internal)
1887 if (cursor.pos() > 0) {
1888 bool boundary = cursor.boundary();
1889 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1890 if (!internal && !boundary &&
1891 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1892 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1893 } else if (cursor.par() != ownerParagraphs().begin()) {
1894 // steps into the paragraph above
1895 ParagraphList::iterator pit = boost::prior(cursor.par());
1896 setCursor(pit, pit->size());
1901 void LyXText::cursorRight(bool internal)
1903 bool const at_end = (cursor.pos() == cursor.par()->size());
1904 bool const at_newline = !at_end &&
1905 cursor.par()->isNewline(cursor.pos());
1907 if (!internal && cursor.boundary() && !at_newline)
1908 setCursor(cursor.par(), cursor.pos(), true, false);
1910 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1912 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1913 setCursor(cursor.par(), cursor.pos(), true, true);
1914 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1915 setCursor(boost::next(cursor.par()), 0);
1919 void LyXText::cursorUp(bool selecting)
1922 int x = cursor.x_fix();
1923 int y = cursor.y() - cursorRow()->baseline() - 1;
1924 setCursorFromCoordinates(x, y);
1927 int y1 = cursor.iy() - topy;
1930 InsetOld * inset_hit = checkInsetHit(x, y1);
1931 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1932 inset_hit->localDispatch(
1933 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1937 setCursorFromCoordinates(bv(), cursor.x_fix(),
1938 cursor.y() - cursorRow()->baseline() - 1);
1943 void LyXText::cursorDown(bool selecting)
1946 int x = cursor.x_fix();
1947 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1948 setCursorFromCoordinates(x, y);
1949 if (!selecting && cursorRow() == cursorIRow()) {
1951 int y1 = cursor.iy() - topy;
1954 InsetOld * inset_hit = checkInsetHit(x, y1);
1955 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1956 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1957 inset_hit->localDispatch(cmd);
1961 setCursorFromCoordinates(bv(), cursor.x_fix(),
1962 cursor.y() - cursorRow()->baseline()
1963 + cursorRow()->height() + 1);
1968 void LyXText::cursorUpParagraph()
1970 if (cursor.pos() > 0)
1971 setCursor(cursor.par(), 0);
1972 else if (cursor.par() != ownerParagraphs().begin())
1973 setCursor(boost::prior(cursor.par()), 0);
1977 void LyXText::cursorDownParagraph()
1979 ParagraphList::iterator par = cursor.par();
1980 ParagraphList::iterator next_par = boost::next(par);
1982 if (next_par != ownerParagraphs().end())
1983 setCursor(next_par, 0);
1985 setCursor(par, par->size());
1989 // fix the cursor `cur' after a characters has been deleted at `where'
1990 // position. Called by deleteEmptyParagraphMechanism
1991 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
1992 LyXCursor const & where)
1994 // if cursor is not in the paragraph where the delete occured,
1996 if (cur.par() != where.par())
1999 // if cursor position is after the place where the delete occured,
2001 if (cur.pos() > where.pos())
2002 cur.pos(cur.pos()-1);
2004 // check also if we don't want to set the cursor on a spot behind the
2005 // pagragraph because we erased the last character.
2006 if (cur.pos() > cur.par()->size())
2007 cur.pos(cur.par()->size());
2009 // recompute row et al. for this cursor
2010 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2014 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2016 // Would be wrong to delete anything if we have a selection.
2017 if (selection.set())
2020 // We allow all kinds of "mumbo-jumbo" when freespacing.
2021 if (old_cursor.par()->layout()->free_spacing
2022 || old_cursor.par()->isFreeSpacing()) {
2026 /* Ok I'll put some comments here about what is missing.
2027 I have fixed BackSpace (and thus Delete) to not delete
2028 double-spaces automagically. I have also changed Cut,
2029 Copy and Paste to hopefully do some sensible things.
2030 There are still some small problems that can lead to
2031 double spaces stored in the document file or space at
2032 the beginning of paragraphs. This happens if you have
2033 the cursor betwenn to spaces and then save. Or if you
2034 cut and paste and the selection have a space at the
2035 beginning and then save right after the paste. I am
2036 sure none of these are very hard to fix, but I will
2037 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2038 that I can get some feedback. (Lgb)
2041 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2042 // delete the LineSeparator.
2045 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2046 // delete the LineSeparator.
2049 // If the pos around the old_cursor were spaces, delete one of them.
2050 if (old_cursor.par() != cursor.par()
2051 || old_cursor.pos() != cursor.pos()) {
2052 // Only if the cursor has really moved
2054 if (old_cursor.pos() > 0
2055 && old_cursor.pos() < old_cursor.par()->size()
2056 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2057 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2058 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
2059 redoParagraph(old_cursor.par());
2063 #ifdef WITH_WARNINGS
2064 #warning This will not work anymore when we have multiple views of the same buffer
2065 // In this case, we will have to correct also the cursors held by
2066 // other bufferviews. It will probably be easier to do that in a more
2067 // automated way in LyXCursor code. (JMarc 26/09/2001)
2069 // correct all cursors held by the LyXText
2070 fixCursorAfterDelete(cursor, old_cursor);
2071 fixCursorAfterDelete(selection.cursor, old_cursor);
2072 fixCursorAfterDelete(selection.start, old_cursor);
2073 fixCursorAfterDelete(selection.end, old_cursor);
2078 // don't delete anything if this is the ONLY paragraph!
2079 if (ownerParagraphs().size() == 1)
2082 // Do not delete empty paragraphs with keepempty set.
2083 if (old_cursor.par()->allowEmpty())
2086 // only do our magic if we changed paragraph
2087 if (old_cursor.par() == cursor.par())
2090 // record if we have deleted a paragraph
2091 // we can't possibly have deleted a paragraph before this point
2092 bool deleted = false;
2094 if (old_cursor.par()->empty() ||
2095 (old_cursor.par()->size() == 1 &&
2096 old_cursor.par()->isLineSeparator(0))) {
2097 // ok, we will delete anything
2098 LyXCursor tmpcursor;
2102 bool selection_position_was_oldcursor_position = (
2103 selection.cursor.par() == old_cursor.par()
2104 && selection.cursor.pos() == old_cursor.pos());
2106 if (getRow(old_cursor) != rows().begin()) {
2107 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
2109 cursor = old_cursor; // that undo can restore the right cursor position
2110 #warning FIXME. --end() iterator is usable here
2111 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2112 while (endpit != ownerParagraphs().end() &&
2113 endpit->getDepth()) {
2117 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2118 boost::prior(endpit));
2122 removeRow(getRow(old_cursor));
2124 ownerParagraphs().erase(old_cursor.par());
2126 /* Breakagain the next par. Needed because of
2127 * the parindent that can occur or dissappear.
2128 * The next row can change its height, if
2129 * there is another layout before */
2130 RowList::iterator tmprit = boost::next(prevrow);
2131 if (tmprit != rows().end()) {
2135 setHeightOfRow(prevrow);
2137 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2140 cursor = old_cursor; // that undo can restore the right cursor position
2141 #warning FIXME. --end() iterator is usable here
2142 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2143 while (endpit != ownerParagraphs().end() &&
2144 endpit->getDepth()) {
2148 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2152 removeRow(getRow(old_cursor));
2154 ownerParagraphs().erase(old_cursor.par());
2156 /* Breakagain the next par. Needed because of
2157 the parindent that can occur or dissappear.
2158 The next row can change its height, if
2159 there is another layout before */
2160 if (nextrow != rows().end()) {
2161 breakAgain(nextrow);
2167 setCursorIntern(cursor.par(), cursor.pos());
2169 if (selection_position_was_oldcursor_position) {
2170 // correct selection
2171 selection.cursor = cursor;
2175 if (old_cursor.par()->stripLeadingSpaces()) {
2176 redoParagraph(old_cursor.par());
2178 setCursorIntern(cursor.par(), cursor.pos());
2179 selection.cursor = cursor;
2186 ParagraphList & LyXText::ownerParagraphs() const
2189 return inset_owner->paragraphs;
2191 return bv_owner->buffer()->paragraphs;
2195 bool LyXText::isInInset() const
2197 // Sub-level has non-null bv owner and
2198 // non-null inset owner.
2199 return inset_owner != 0 && bv_owner != 0;
2203 int defaultRowHeight()
2205 LyXFont const font(LyXFont::ALL_SANE);
2206 return int(font_metrics::maxAscent(font)
2207 + font_metrics::maxDescent(font) * 1.5);