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();
72 LyXText::LyXText(BufferView * bv, InsetText * inset)
73 : height(0), width(0), anchor_row_offset_(0),
74 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
76 anchor_row_ = rows().end();
81 void LyXText::init(BufferView * bview)
89 anchor_row_ = rows().end();
90 anchor_row_offset_ = 0;
92 ParagraphList::iterator pit = ownerParagraphs().begin();
93 ParagraphList::iterator end = ownerParagraphs().end();
95 current_font = getFont(pit, 0);
97 for (; pit != end; ++pit)
98 insertParagraph(pit, rowlist_.end());
100 setCursorIntern(rowlist_.begin()->par(), 0);
101 selection.cursor = cursor;
107 // Gets the fully instantiated font at a given position in a paragraph
108 // Basically the same routine as Paragraph::getFont() in paragraph.C.
109 // The difference is that this one is used for displaying, and thus we
110 // are allowed to make cosmetic improvements. For instance make footnotes
112 // If position is -1, we get the layout font of the paragraph.
113 // If position is -2, we get the font of the manual label of the paragraph.
114 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
118 LyXLayout_ptr const & layout = pit->layout();
120 BufferParams const & params = bv()->buffer()->params;
122 // We specialize the 95% common case:
123 if (!pit->getDepth()) {
124 if (layout->labeltype == LABEL_MANUAL
125 && pos < pit->beginningOfBody()) {
127 LyXFont f = pit->getFontSettings(params, pos);
129 pit->inInset()->getDrawFont(f);
130 return f.realize(layout->reslabelfont);
132 LyXFont f = pit->getFontSettings(params, pos);
134 pit->inInset()->getDrawFont(f);
135 return f.realize(layout->resfont);
139 // The uncommon case need not be optimized as much
143 if (pos < pit->beginningOfBody()) {
145 layoutfont = layout->labelfont;
148 layoutfont = layout->font;
151 LyXFont tmpfont = pit->getFontSettings(params, pos);
152 tmpfont.realize(layoutfont);
155 pit->inInset()->getDrawFont(tmpfont);
157 // Realize with the fonts of lesser depth.
158 tmpfont.realize(outerFont(pit, ownerParagraphs()));
159 tmpfont.realize(defaultfont_);
165 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
167 LyXLayout_ptr const & layout = pit->layout();
169 if (!pit->getDepth())
170 return layout->resfont;
172 LyXFont font = layout->font;
173 // Realize with the fonts of lesser depth.
174 font.realize(outerFont(pit, ownerParagraphs()));
175 font.realize(defaultfont_);
181 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
183 LyXLayout_ptr const & layout = pit->layout();
185 if (!pit->getDepth())
186 return layout->reslabelfont;
188 LyXFont font = layout->labelfont;
189 // Realize with the fonts of lesser depth.
190 font.realize(outerFont(pit, ownerParagraphs()));
191 font.realize(defaultfont_);
197 void LyXText::setCharFont(ParagraphList::iterator pit,
198 pos_type pos, LyXFont const & fnt,
201 BufferParams const & params = bv()->buffer()->params;
202 LyXFont font = getFont(pit, pos);
203 font.update(fnt, params.language, toggleall);
204 // Let the insets convert their font
205 if (pit->isInset(pos)) {
206 InsetOld * inset = pit->getInset(pos);
207 if (isEditableInset(inset)) {
208 static_cast<UpdatableInset *>(inset)
209 ->setFont(bv(), fnt, toggleall, true);
213 // Plug through to version below:
214 setCharFont(pit, pos, font);
218 void LyXText::setCharFont(
219 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
222 LyXLayout_ptr const & layout = pit->layout();
224 // Get concrete layout font to reduce against
227 if (pos < pit->beginningOfBody())
228 layoutfont = layout->labelfont;
230 layoutfont = layout->font;
232 // Realize against environment font information
233 if (pit->getDepth()) {
234 ParagraphList::iterator tp = pit;
235 while (!layoutfont.resolved() &&
236 tp != ownerParagraphs().end() &&
238 tp = outerHook(tp, ownerParagraphs());
239 if (tp != ownerParagraphs().end())
240 layoutfont.realize(tp->layout()->font);
244 layoutfont.realize(defaultfont_);
246 // Now, reduce font against full layout font
247 font.reduce(layoutfont);
249 pit->setFont(pos, font);
253 // removes the row and reset the touched counters
254 void LyXText::removeRow(RowList::iterator rit)
256 if (anchor_row_ == rit) {
257 if (rit != rows().begin()) {
258 anchor_row_ = boost::prior(rit);
259 anchor_row_offset_ += anchor_row_->height();
261 anchor_row_ = boost::next(rit);
262 anchor_row_offset_ -= rit->height();
266 // the text becomes smaller
267 height -= rit->height();
273 // remove all following rows of the paragraph of the specified row.
274 void LyXText::removeParagraph(RowList::iterator rit)
276 ParagraphList::iterator tmppit = rit->par();
279 while (rit != rows().end() && rit->par() == tmppit) {
280 RowList::iterator tmprit = boost::next(rit);
287 void LyXText::insertParagraph(ParagraphList::iterator pit,
288 RowList::iterator rowit)
290 // insert a new row, starting at position 0
292 RowList::iterator rit = rowlist_.insert(rowit, newrow);
294 // and now append the whole paragraph before the new row
295 appendParagraph(rit);
299 InsetOld * LyXText::getInset() const
301 ParagraphList::iterator pit = cursor.par();
302 pos_type const pos = cursor.pos();
304 if (pos < pit->size() && pit->isInset(pos)) {
305 return pit->getInset(pos);
311 void LyXText::toggleInset()
313 InsetOld * inset = getInset();
314 // is there an editable inset at cursor position?
315 if (!isEditableInset(inset)) {
316 // No, try to see if we are inside a collapsable inset
317 if (inset_owner && inset_owner->owner()
318 && inset_owner->owner()->isOpen()) {
319 bv()->unlockInset(inset_owner->owner());
320 inset_owner->owner()->close(bv());
321 bv()->getLyXText()->cursorRight(bv());
325 //bv()->owner()->message(inset->editMessage());
327 // do we want to keep this?? (JMarc)
328 if (!isHighlyEditableInset(inset))
329 recordUndo(bv(), Undo::ATOMIC);
331 if (inset->isOpen()) {
337 bv()->updateInset(inset);
341 /* used in setlayout */
342 // Asger is not sure we want to do this...
343 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
346 LyXLayout_ptr const & layout = par.layout();
347 pos_type const psize = par.size();
350 for (pos_type pos = 0; pos < psize; ++pos) {
351 if (pos < par.beginningOfBody())
352 layoutfont = layout->labelfont;
354 layoutfont = layout->font;
356 LyXFont tmpfont = par.getFontSettings(params, pos);
357 tmpfont.reduce(layoutfont);
358 par.setFont(pos, tmpfont);
363 ParagraphList::iterator
364 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
365 LyXCursor & send_cur,
366 string const & layout)
368 ParagraphList::iterator endpit = boost::next(send_cur.par());
369 ParagraphList::iterator undoendpit = endpit;
370 ParagraphList::iterator pars_end = ownerParagraphs().end();
372 if (endpit != pars_end && endpit->getDepth()) {
373 while (endpit != pars_end && endpit->getDepth()) {
377 } else if (endpit != pars_end) {
378 // because of parindents etc.
382 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
384 // ok we have a selection. This is always between sstart_cur
385 // and sel_end cursor
387 ParagraphList::iterator pit = sstart_cur.par();
388 ParagraphList::iterator epit = boost::next(send_cur.par());
390 LyXLayout_ptr const & lyxlayout =
391 bv()->buffer()->params.getLyXTextClass()[layout];
394 pit->applyLayout(lyxlayout);
395 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
396 ParagraphList::iterator fppit = pit;
397 fppit->params().spaceTop(lyxlayout->fill_top ?
398 VSpace(VSpace::VFILL)
399 : VSpace(VSpace::NONE));
400 fppit->params().spaceBottom(lyxlayout->fill_bottom ?
401 VSpace(VSpace::VFILL)
402 : VSpace(VSpace::NONE));
403 if (lyxlayout->margintype == MARGIN_MANUAL)
404 pit->setLabelWidthString(lyxlayout->labelstring());
407 } while (pit != epit);
413 // set layout over selection and make a total rebreak of those paragraphs
414 void LyXText::setLayout(string const & layout)
416 LyXCursor tmpcursor = cursor; // store the current cursor
418 // if there is no selection just set the layout
419 // of the current paragraph
420 if (!selection.set()) {
421 selection.start = cursor; // dummy selection
422 selection.end = cursor;
425 // special handling of new environment insets
426 BufferParams const & params = bv()->buffer()->params;
427 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
428 if (lyxlayout->is_environment) {
429 // move everything in a new environment inset
430 lyxerr << "setting layout " << layout << endl;
431 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
432 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
433 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
434 InsetOld * inset = new InsetEnvironment(params, layout);
435 if (bv()->insertInset(inset)) {
437 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
444 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
445 selection.end, layout);
446 redoParagraphs(selection.start.par(), endpit);
448 // we have to reset the selection, because the
449 // geometry could have changed
450 setCursor(selection.start.par(), selection.start.pos(), false);
451 selection.cursor = cursor;
452 setCursor(selection.end.par(), selection.end.pos(), false);
456 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
460 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
462 ParagraphList::iterator pit(cursor.par());
463 ParagraphList::iterator end(cursor.par());
464 ParagraphList::iterator start = pit;
466 if (selection.set()) {
467 pit = selection.start.par();
468 end = selection.end.par();
472 ParagraphList::iterator pastend = boost::next(end);
475 recordUndo(bv(), Undo::ATOMIC, start, end);
477 bool changed = false;
479 int prev_after_depth = 0;
480 #warning parlist ... could be nicer ?
481 if (start != ownerParagraphs().begin()) {
482 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
486 int const depth = pit->params().depth();
487 if (type == bv_funcs::INC_DEPTH) {
488 if (depth < prev_after_depth
489 && pit->layout()->labeltype != LABEL_BIBLIO) {
492 pit->params().depth(depth + 1);
499 pit->params().depth(depth - 1);
502 prev_after_depth = pit->getMaxDepthAfter();
515 redoParagraphs(start, pastend);
517 // We need to actually move the text->cursor. I don't
518 // understand why ...
519 LyXCursor tmpcursor = cursor;
521 // we have to reset the visual selection because the
522 // geometry could have changed
523 if (selection.set()) {
524 setCursor(selection.start.par(), selection.start.pos());
525 selection.cursor = cursor;
526 setCursor(selection.end.par(), selection.end.pos());
529 // this handles the counter labels, and also fixes up
530 // depth values for follow-on (child) paragraphs
534 setCursor(tmpcursor.par(), tmpcursor.pos());
540 // set font over selection and make a total rebreak of those paragraphs
541 void LyXText::setFont(LyXFont const & font, bool toggleall)
543 // if there is no selection just set the current_font
544 if (!selection.set()) {
545 // Determine basis font
547 if (cursor.pos() < cursor.par()->beginningOfBody()) {
548 layoutfont = getLabelFont(cursor.par());
550 layoutfont = getLayoutFont(cursor.par());
552 // Update current font
553 real_current_font.update(font,
554 bv()->buffer()->params.language,
557 // Reduce to implicit settings
558 current_font = real_current_font;
559 current_font.reduce(layoutfont);
560 // And resolve it completely
561 real_current_font.realize(layoutfont);
566 LyXCursor tmpcursor = cursor; // store the current cursor
568 // ok we have a selection. This is always between sel_start_cursor
569 // and sel_end cursor
571 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
573 cursor = selection.start;
574 while (cursor.par() != selection.end.par() ||
575 cursor.pos() < selection.end.pos())
577 if (cursor.pos() < cursor.par()->size()) {
578 // an open footnote should behave like a closed one
579 setCharFont(cursor.par(), cursor.pos(),
581 cursor.pos(cursor.pos() + 1);
584 cursor.par(boost::next(cursor.par()));
589 redoParagraph(selection.start.par());
591 // we have to reset the selection, because the
592 // geometry could have changed, but we keep
593 // it for user convenience
594 setCursor(selection.start.par(), selection.start.pos());
595 selection.cursor = cursor;
596 setCursor(selection.end.par(), selection.end.pos());
598 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
599 tmpcursor.boundary());
603 void LyXText::redoHeightOfParagraph()
605 RowList::iterator tmprow = cursorRow();
607 setHeightOfRow(tmprow);
609 while (tmprow != rows().begin()
610 && boost::prior(tmprow)->par() == tmprow->par()) {
612 setHeightOfRow(tmprow);
617 setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
621 RowList::iterator LyXText::firstRow(ParagraphList::iterator pit)
623 RowList::iterator rit;
624 for (rit = rows().begin(); rit != rows().end(); ++rit)
625 if (rit->par() == pit)
631 // rebreaks all paragraphs between the specified pars
632 // This function is needed after SetLayout and SetFont etc.
633 void LyXText::redoParagraphs(ParagraphList::iterator start,
634 ParagraphList::iterator end)
636 for ( ; start != end; ++start)
637 redoParagraph(start);
641 void LyXText::redoParagraph(ParagraphList::iterator pit)
643 RowList::iterator rit = firstRow(pit);
645 if (rit == rows().end()) {
646 lyxerr << "LyXText::redoParagraphs: should not happen\n";
651 while (rit != rows().end() && rit->par() == pit) {
652 RowList::iterator rit2 = rit++;
656 // Reinsert the paragraph.
657 insertParagraph(pit, rit);
658 setHeightOfRow(rows().begin());
663 void LyXText::fullRebreak()
665 lyxerr << "fullRebreak\n";
666 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
667 setCursorIntern(cursor.par(), cursor.pos());
668 selection.cursor = cursor;
672 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
674 lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << "\n";
675 //Assert(mi.base.textwidth);
681 anchor_row_ = rows().end();
682 anchor_row_offset_ = 0;
684 ParagraphList::iterator pit = ownerParagraphs().begin();
685 ParagraphList::iterator end = ownerParagraphs().end();
687 for (; pit != end; ++pit) {
688 InsetList::iterator ii = pit->insetlist.begin();
689 InsetList::iterator iend = pit->insetlist.end();
690 for (; ii != iend; ++ii) {
693 ii->inset->metrics(m, dim);
696 // insert a new row, starting at position 0
698 RowList::iterator rit = rowlist_.insert(rowlist_.end(), newrow);
700 // and now append the whole paragraph before the new row
701 appendParagraph(rit);
705 //lyxerr << "height 0: " << height << "\n";
706 //for (RowList::iterator rit = rows().begin(); rit != rows().end(); ++rit) {
707 // height += rit->height();
709 //lyxerr << "height 1: " << height << "\n";
712 dim.asc = rows().begin()->ascent_of_text();
713 dim.des = height - dim.asc;
714 dim.wid = std::max(mi.base.textwidth, int(width));
718 void LyXText::partialRebreak()
720 if (rows().empty()) {
724 breakAgain(rows().begin());
728 // important for the screen
731 // the cursor set functions have a special mechanism. When they
732 // realize, that you left an empty paragraph, they will delete it.
733 // They also delete the corresponding row
735 // need the selection cursor:
736 void LyXText::setSelection()
738 bool const lsel = TextCursor::setSelection();
740 if (inset_owner && (selection.set() || lsel))
741 inset_owner->setUpdateStatus(InsetText::SELECTION);
746 void LyXText::clearSelection()
748 TextCursor::clearSelection();
750 // reset this in the bv_owner!
751 if (bv_owner && bv_owner->text)
752 bv_owner->text->xsel_cache.set(false);
756 void LyXText::cursorHome()
758 setCursor(cursor.par(), cursorRow()->pos());
762 void LyXText::cursorEnd()
764 if (cursor.par()->empty())
767 RowList::iterator rit = cursorRow();
768 RowList::iterator next_rit = boost::next(rit);
769 ParagraphList::iterator pit = rit->par();
770 pos_type last_pos = lastPos(*this, rit);
772 if (next_rit == rows().end() || next_rit->par() != pit) {
776 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
781 setCursor(pit, last_pos);
785 void LyXText::cursorTop()
787 setCursor(ownerParagraphs().begin(), 0);
791 void LyXText::cursorBottom()
793 ParagraphList::iterator lastpit =
794 boost::prior(ownerParagraphs().end());
795 setCursor(lastpit, lastpit->size());
799 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
801 // If the mask is completely neutral, tell user
802 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
803 // Could only happen with user style
804 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
808 // Try implicit word selection
809 // If there is a change in the language the implicit word selection
811 LyXCursor resetCursor = cursor;
812 bool implicitSelection = (font.language() == ignore_language
813 && font.number() == LyXFont::IGNORE)
814 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
817 setFont(font, toggleall);
819 // Implicit selections are cleared afterwards
820 //and cursor is set to the original position.
821 if (implicitSelection) {
823 cursor = resetCursor;
824 setCursor(cursor.par(), cursor.pos());
825 selection.cursor = cursor;
828 inset_owner->setUpdateStatus(InsetText::CURSOR_PAR);
832 string LyXText::getStringToIndex()
834 // Try implicit word selection
835 // If there is a change in the language the implicit word selection
837 LyXCursor const reset_cursor = cursor;
838 bool const implicitSelection =
839 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
842 if (!selection.set())
843 bv()->owner()->message(_("Nothing to index!"));
844 else if (selection.start.par() != selection.end.par())
845 bv()->owner()->message(_("Cannot index more than one paragraph!"));
847 idxstring = selectionAsString(bv()->buffer(), false);
849 // Reset cursors to their original position.
850 cursor = reset_cursor;
851 setCursor(cursor.par(), cursor.pos());
852 selection.cursor = cursor;
854 // Clear the implicit selection.
855 if (implicitSelection)
862 // the DTP switches for paragraphs. LyX will store them in the first
863 // physicla paragraph. When a paragraph is broken, the top settings rest,
864 // the bottom settings are given to the new one. So I can make shure,
865 // they do not duplicate themself and you cannnot make dirty things with
868 void LyXText::setParagraph(bool line_top, bool line_bottom,
869 bool pagebreak_top, bool pagebreak_bottom,
870 VSpace const & space_top,
871 VSpace const & space_bottom,
872 Spacing const & spacing,
874 string const & labelwidthstring,
877 LyXCursor tmpcursor = cursor;
878 if (!selection.set()) {
879 selection.start = cursor;
880 selection.end = cursor;
883 // make sure that the depth behind the selection are restored, too
884 ParagraphList::iterator endpit = boost::next(selection.end.par());
885 ParagraphList::iterator undoendpit = endpit;
886 ParagraphList::iterator pars_end = ownerParagraphs().end();
888 if (endpit != pars_end && endpit->getDepth()) {
889 while (endpit != pars_end && endpit->getDepth()) {
893 } else if (endpit != pars_end) {
894 // because of parindents etc.
898 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
899 boost::prior(undoendpit));
902 ParagraphList::iterator tmppit = selection.end.par();
904 while (tmppit != boost::prior(selection.start.par())) {
905 setCursor(tmppit, 0);
907 ParagraphList::iterator pit = cursor.par();
908 ParagraphParameters & params = pit->params();
910 params.lineTop(line_top);
911 params.lineBottom(line_bottom);
912 params.pagebreakTop(pagebreak_top);
913 params.pagebreakBottom(pagebreak_bottom);
914 params.spaceTop(space_top);
915 params.spaceBottom(space_bottom);
916 params.spacing(spacing);
917 // does the layout allow the new alignment?
918 LyXLayout_ptr const & layout = pit->layout();
920 if (align == LYX_ALIGN_LAYOUT)
921 align = layout->align;
922 if (align & layout->alignpossible) {
923 if (align == layout->align)
924 params.align(LYX_ALIGN_LAYOUT);
928 pit->setLabelWidthString(labelwidthstring);
929 params.noindent(noindent);
930 tmppit = boost::prior(pit);
934 redoParagraphs(selection.start.par(), endpit);
937 setCursor(selection.start.par(), selection.start.pos());
938 selection.cursor = cursor;
939 setCursor(selection.end.par(), selection.end.pos());
941 setCursor(tmpcursor.par(), tmpcursor.pos());
943 bv()->updateInset(inset_owner);
947 // set the counter of a paragraph. This includes the labels
948 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
950 LyXTextClass const & textclass = buf->params.getLyXTextClass();
951 LyXLayout_ptr const & layout = pit->layout();
953 if (pit != ownerParagraphs().begin()) {
955 pit->params().appendix(boost::prior(pit)->params().appendix());
956 if (!pit->params().appendix() &&
957 pit->params().startOfAppendix()) {
958 pit->params().appendix(true);
959 textclass.counters().reset();
961 pit->enumdepth = boost::prior(pit)->enumdepth;
962 pit->itemdepth = boost::prior(pit)->itemdepth;
964 pit->params().appendix(pit->params().startOfAppendix());
969 /* Maybe we have to increment the enumeration depth.
970 * BUT, enumeration in a footnote is considered in isolation from its
971 * surrounding paragraph so don't increment if this is the
972 * first line of the footnote
973 * AND, bibliographies can't have their depth changed ie. they
974 * are always of depth 0
976 if (pit != ownerParagraphs().begin()
977 && boost::prior(pit)->getDepth() < pit->getDepth()
978 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
979 && pit->enumdepth < 3
980 && layout->labeltype != LABEL_BIBLIO) {
984 // Maybe we have to decrement the enumeration depth, see note above
985 if (pit != ownerParagraphs().begin()
986 && boost::prior(pit)->getDepth() > pit->getDepth()
987 && layout->labeltype != LABEL_BIBLIO) {
988 pit->enumdepth = depthHook(pit, ownerParagraphs(),
989 pit->getDepth())->enumdepth;
992 if (!pit->params().labelString().empty()) {
993 pit->params().labelString(string());
996 if (layout->margintype == MARGIN_MANUAL) {
997 if (pit->params().labelWidthString().empty()) {
998 pit->setLabelWidthString(layout->labelstring());
1001 pit->setLabelWidthString(string());
1004 // is it a layout that has an automatic label?
1005 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1006 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1010 if (i >= 0 && i <= buf->params.secnumdepth) {
1014 textclass.counters().step(layout->latexname());
1016 // Is there a label? Useful for Chapter layout
1017 if (!pit->params().appendix()) {
1018 s << buf->B_(layout->labelstring());
1020 s << buf->B_(layout->labelstring_appendix());
1023 // Use of an integer is here less than elegant. For now.
1024 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1025 if (!pit->params().appendix()) {
1026 numbertype = "sectioning";
1028 numbertype = "appendix";
1029 if (pit->isRightToLeftPar(buf->params))
1030 langtype = "hebrew";
1036 << textclass.counters()
1037 .numberLabel(layout->latexname(),
1038 numbertype, langtype, head);
1040 pit->params().labelString(STRCONV(s.str()));
1042 // reset enum counters
1043 textclass.counters().reset("enum");
1044 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1045 textclass.counters().reset("enum");
1046 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1048 // Yes I know this is a really, really! bad solution
1050 string enumcounter("enum");
1052 switch (pit->enumdepth) {
1061 enumcounter += "iv";
1064 // not a valid enumdepth...
1068 textclass.counters().step(enumcounter);
1070 s << textclass.counters()
1071 .numberLabel(enumcounter, "enumeration");
1072 pit->params().labelString(STRCONV(s.str()));
1074 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1075 textclass.counters().step("bibitem");
1076 int number = textclass.counters().value("bibitem");
1077 if (pit->bibitem()) {
1078 pit->bibitem()->setCounter(number);
1079 pit->params().labelString(layout->labelstring());
1081 // In biblio should't be following counters but...
1083 string s = buf->B_(layout->labelstring());
1085 // the caption hack:
1086 if (layout->labeltype == LABEL_SENSITIVE) {
1087 ParagraphList::iterator end = ownerParagraphs().end();
1088 ParagraphList::iterator tmppit = pit;
1091 while (tmppit != end && tmppit->inInset()
1092 // the single '=' is intended below
1093 && (in = tmppit->inInset()->owner()))
1095 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1096 in->lyxCode() == InsetOld::WRAP_CODE) {
1100 tmppit = ownerParagraphs().begin();
1101 for ( ; tmppit != end; ++tmppit)
1102 if (&*tmppit == in->parOwner())
1110 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1111 type = static_cast<InsetFloat*>(in)->params().type;
1112 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1113 type = static_cast<InsetWrap*>(in)->params().type;
1117 Floating const & fl = textclass.floats().getType(type);
1119 textclass.counters().step(fl.type());
1121 // Doesn't work... yet.
1122 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1124 // par->SetLayout(0);
1125 // s = layout->labelstring;
1126 s = _("Senseless: ");
1129 pit->params().labelString(s);
1131 // reset the enumeration counter. They are always reset
1132 // when there is any other layout between
1133 // Just fall-through between the cases so that all
1134 // enum counters deeper than enumdepth is also reset.
1135 switch (pit->enumdepth) {
1137 textclass.counters().reset("enumi");
1139 textclass.counters().reset("enumii");
1141 textclass.counters().reset("enumiii");
1143 textclass.counters().reset("enumiv");
1149 // Updates all counters. Paragraphs with changed label string will be rebroken
1150 void LyXText::updateCounters()
1152 RowList::iterator rowit = rows().begin();
1153 ParagraphList::iterator pit = rowit->par();
1155 // CHECK if this is really needed. (Lgb)
1156 bv()->buffer()->params.getLyXTextClass().counters().reset();
1158 ParagraphList::iterator beg = ownerParagraphs().begin();
1159 ParagraphList::iterator end = ownerParagraphs().end();
1160 for (; pit != end; ++pit) {
1161 while (rowit->par() != pit)
1164 string const oldLabel = pit->params().labelString();
1166 size_t maxdepth = 0;
1168 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1170 if (pit->params().depth() > maxdepth)
1171 pit->params().depth(maxdepth);
1173 // setCounter can potentially change the labelString.
1174 setCounter(bv()->buffer(), pit);
1176 string const & newLabel = pit->params().labelString();
1178 if (oldLabel.empty() && !newLabel.empty()) {
1179 removeParagraph(rowit);
1180 appendParagraph(rowit);
1186 void LyXText::insertInset(InsetOld * inset)
1188 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1190 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1192 cursor.par()->insertInset(cursor.pos(), inset);
1193 // Just to rebreak and refresh correctly.
1194 // The character will not be inserted a second time
1195 insertChar(Paragraph::META_INSET);
1196 // If we enter a highly editable inset the cursor should be to before
1197 // the inset. This couldn't happen before as Undo was not handled inside
1198 // inset now after the Undo LyX tries to call inset->Edit(...) again
1199 // and cannot do this as the cursor is behind the inset and GetInset
1200 // does not return the inset!
1201 if (isHighlyEditableInset(inset)) {
1208 void LyXText::cutSelection(bool doclear, bool realcut)
1210 // Stuff what we got on the clipboard. Even if there is no selection.
1212 // There is a problem with having the stuffing here in that the
1213 // larger the selection the slower LyX will get. This can be
1214 // solved by running the line below only when the selection has
1215 // finished. The solution used currently just works, to make it
1216 // faster we need to be more clever and probably also have more
1217 // calls to stuffClipboard. (Lgb)
1218 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1220 // This doesn't 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 selection.end
1227 // make sure that the depth behind the selection are restored, too
1228 ParagraphList::iterator endpit = boost::next(selection.end.par());
1229 ParagraphList::iterator undoendpit = endpit;
1230 ParagraphList::iterator pars_end = ownerParagraphs().end();
1232 if (endpit != pars_end && endpit->getDepth()) {
1233 while (endpit != pars_end && endpit->getDepth()) {
1235 undoendpit = endpit;
1237 } else if (endpit != pars_end) {
1238 // because of parindents etc.
1242 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1243 boost::prior(undoendpit));
1246 endpit = selection.end.par();
1247 int endpos = selection.end.pos();
1249 boost::tie(endpit, endpos) = realcut ?
1250 CutAndPaste::cutSelection(bv()->buffer()->params,
1252 selection.start.par(), endpit,
1253 selection.start.pos(), endpos,
1254 bv()->buffer()->params.textclass,
1256 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1258 selection.start.par(), endpit,
1259 selection.start.pos(), endpos,
1261 // sometimes necessary
1263 selection.start.par()->stripLeadingSpaces();
1265 redoParagraphs(selection.start.par(), boost::next(endpit));
1266 #warning FIXME latent bug
1267 // endpit will be invalidated on redoParagraphs once ParagraphList
1268 // becomes a std::list? There are maybe other places on which this
1269 // can happend? (Ab)
1270 // cutSelection can invalidate the cursor so we need to set
1272 // we prefer the end for when tracking changes
1276 // need a valid cursor. (Lgb)
1279 setCursor(cursor.par(), cursor.pos());
1280 selection.cursor = cursor;
1285 void LyXText::copySelection()
1287 // stuff the selection onto the X clipboard, from an explicit copy request
1288 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1290 // this doesnt make sense, if there is no selection
1291 if (!selection.set())
1294 // ok we have a selection. This is always between selection.start
1295 // and sel_end cursor
1297 // copy behind a space if there is one
1298 while (selection.start.par()->size() > selection.start.pos()
1299 && selection.start.par()->isLineSeparator(selection.start.pos())
1300 && (selection.start.par() != selection.end.par()
1301 || selection.start.pos() < selection.end.pos()))
1302 selection.start.pos(selection.start.pos() + 1);
1304 CutAndPaste::copySelection(selection.start.par(),
1305 selection.end.par(),
1306 selection.start.pos(), selection.end.pos(),
1307 bv()->buffer()->params.textclass);
1311 void LyXText::pasteSelection(size_t sel_index)
1313 // this does not make sense, if there is nothing to paste
1314 if (!CutAndPaste::checkPastePossible())
1317 recordUndo(bv(), Undo::INSERT, cursor.par());
1319 ParagraphList::iterator endpit;
1324 boost::tie(ppp, endpit) =
1325 CutAndPaste::pasteSelection(*bv()->buffer(),
1327 cursor.par(), cursor.pos(),
1328 bv()->buffer()->params.textclass,
1330 bufferErrors(*bv()->buffer(), el);
1331 bv()->showErrorList(_("Paste"));
1333 redoParagraphs(cursor.par(), endpit);
1335 setCursor(cursor.par(), cursor.pos());
1338 selection.cursor = cursor;
1339 setCursor(ppp.first, ppp.second);
1345 void LyXText::setSelectionRange(lyx::pos_type length)
1350 selection.cursor = cursor;
1357 // simple replacing. The font of the first selected character is used
1358 void LyXText::replaceSelectionWithString(string const & str)
1360 recordUndo(bv(), Undo::ATOMIC);
1363 if (!selection.set()) { // create a dummy selection
1364 selection.end = cursor;
1365 selection.start = cursor;
1368 // Get font setting before we cut
1369 pos_type pos = selection.end.pos();
1370 LyXFont const font = selection.start.par()
1371 ->getFontSettings(bv()->buffer()->params,
1372 selection.start.pos());
1374 // Insert the new string
1375 string::const_iterator cit = str.begin();
1376 string::const_iterator end = str.end();
1377 for (; cit != end; ++cit) {
1378 selection.end.par()->insertChar(pos, (*cit), font);
1382 // Cut the selection
1383 cutSelection(true, false);
1389 // needed to insert the selection
1390 void LyXText::insertStringAsLines(string const & str)
1392 ParagraphList::iterator pit = cursor.par();
1393 pos_type pos = cursor.pos();
1394 ParagraphList::iterator endpit = boost::next(cursor.par());
1396 recordUndo(bv(), Undo::ATOMIC);
1398 // only to be sure, should not be neccessary
1401 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1403 redoParagraphs(cursor.par(), endpit);
1404 setCursor(cursor.par(), cursor.pos());
1405 selection.cursor = cursor;
1406 setCursor(pit, pos);
1411 // turns double-CR to single CR, others where converted into one
1412 // blank. Then InsertStringAsLines is called
1413 void LyXText::insertStringAsParagraphs(string const & str)
1415 string linestr(str);
1416 bool newline_inserted = false;
1417 string::size_type const siz = linestr.length();
1419 for (string::size_type i = 0; i < siz; ++i) {
1420 if (linestr[i] == '\n') {
1421 if (newline_inserted) {
1422 // we know that \r will be ignored by
1423 // InsertStringA. Of course, it is a dirty
1424 // trick, but it works...
1425 linestr[i - 1] = '\r';
1429 newline_inserted = true;
1431 } else if (IsPrintable(linestr[i])) {
1432 newline_inserted = false;
1435 insertStringAsLines(linestr);
1439 void LyXText::checkParagraph(ParagraphList::iterator pit, pos_type pos)
1441 breakAgain(getRow(pit, pos));
1443 setCursorIntern(cursor.par(), cursor.pos(), false, cursor.boundary());
1447 // returns false if inset wasn't found
1448 bool LyXText::updateInset(InsetOld * inset)
1450 // first check the current paragraph
1451 int pos = cursor.par()->getPositionOfInset(inset);
1453 checkParagraph(cursor.par(), pos);
1457 // check every paragraph
1458 ParagraphList::iterator par = ownerParagraphs().begin();
1459 ParagraphList::iterator end = ownerParagraphs().end();
1460 for (; par != end; ++par) {
1461 pos = par->getPositionOfInset(inset);
1463 checkParagraph(par, pos);
1472 bool LyXText::setCursor(ParagraphList::iterator pit,
1474 bool setfont, bool boundary)
1476 LyXCursor old_cursor = cursor;
1477 setCursorIntern(pit, pos, setfont, boundary);
1478 return deleteEmptyParagraphMechanism(old_cursor);
1482 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1483 pos_type pos, bool boundary)
1485 Assert(pit != ownerParagraphs().end());
1489 cur.boundary(boundary);
1493 // get the cursor y position in text
1495 RowList::iterator row = getRow(pit, pos, y);
1496 RowList::iterator beg = rows().begin();
1498 RowList::iterator old_row = row;
1499 // if we are before the first char of this row and are still in the
1500 // same paragraph and there is a previous row then put the cursor on
1501 // the end of the previous row
1502 cur.iy(y + row->baseline());
1505 boost::prior(row)->par() == row->par() &&
1506 pos < pit->size() &&
1507 pit->getChar(pos) == Paragraph::META_INSET) {
1508 InsetOld * ins = pit->getInset(pos);
1509 if (ins && (ins->needFullRow() || ins->display())) {
1515 // y is now the beginning of the cursor row
1516 y += row->baseline();
1517 // y is now the cursor baseline
1520 pos_type last = lastPrintablePos(*this, old_row);
1522 // None of these should happen, but we're scaredy-cats
1523 if (pos > pit->size()) {
1524 lyxerr << "dont like 1 please report" << endl;
1527 } else if (pos > last + 1) {
1528 lyxerr << "dont like 2 please report" << endl;
1529 // This shouldn't happen.
1532 } else if (pos < row->pos()) {
1533 lyxerr << "dont like 3 please report" << endl;
1538 // now get the cursors x position
1539 float x = getCursorX(row, pos, last, boundary);
1542 if (old_row != row) {
1543 x = getCursorX(old_row, pos, last, boundary);
1547 /* We take out this for the time being because 1) the redraw code is not
1548 prepared to this yet and 2) because some good policy has yet to be decided
1549 while editting: for instance how to act on rows being created/deleted
1553 //if the cursor is in a visible row, anchor to it
1555 if (topy < y && y < topy + bv()->workHeight())
1561 float LyXText::getCursorX(RowList::iterator rit,
1562 pos_type pos, pos_type last, bool boundary) const
1564 pos_type cursor_vpos = 0;
1566 double fill_separator;
1568 double fill_label_hfill;
1569 // This call HAS to be here because of the BidiTables!!!
1570 prepareToPrint(rit, x, fill_separator, fill_hfill,
1573 ParagraphList::iterator rit_par = rit->par();
1574 pos_type const rit_pos = rit->pos();
1577 cursor_vpos = rit_pos;
1578 else if (pos > last && !boundary)
1579 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1580 ? rit_pos : last + 1;
1581 else if (pos > rit_pos && (pos > last || boundary))
1582 /// Place cursor after char at (logical) position pos - 1
1583 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1584 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1586 /// Place cursor before char at (logical) position pos
1587 cursor_vpos = (bidi_level(pos) % 2 == 0)
1588 ? log2vis(pos) : log2vis(pos) + 1;
1590 pos_type body_pos = rit_par->beginningOfBody();
1592 (body_pos - 1 > last || !rit_par->isLineSeparator(body_pos - 1)))
1595 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1596 pos_type pos = vis2log(vpos);
1597 if (body_pos > 0 && pos == body_pos - 1) {
1598 x += fill_label_hfill +
1599 font_metrics::width(
1600 rit_par->layout()->labelsep, getLabelFont(rit_par));
1601 if (rit_par->isLineSeparator(body_pos - 1))
1602 x -= singleWidth(rit_par, body_pos - 1);
1605 if (hfillExpansion(*this, rit, pos)) {
1606 x += singleWidth(rit_par, pos);
1607 if (pos >= body_pos)
1610 x += fill_label_hfill;
1611 } else if (rit_par->isSeparator(pos)) {
1612 x += singleWidth(rit_par, pos);
1613 if (pos >= body_pos)
1614 x += fill_separator;
1616 x += singleWidth(rit_par, pos);
1622 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1623 pos_type pos, bool setfont, bool boundary)
1625 UpdatableInset * it = pit->inInset();
1627 if (it != inset_owner) {
1628 lyxerr[Debug::INSETS] << "InsetText is " << it
1630 << "inset_owner is "
1631 << inset_owner << endl;
1632 #ifdef WITH_WARNINGS
1633 #warning I believe this code is wrong. (Lgb)
1634 #warning Jürgen, have a look at this. (Lgb)
1635 #warning Hmmm, I guess you are right but we
1636 #warning should verify when this is needed
1638 // Jürgen, would you like to have a look?
1639 // I guess we need to move the outer cursor
1640 // and open and lock the inset (bla bla bla)
1641 // stuff I don't know... so can you have a look?
1643 // I moved the lyxerr stuff in here so we can see if
1644 // this is actually really needed and where!
1646 // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1651 setCursor(cursor, pit, pos, boundary);
1657 void LyXText::setCurrentFont()
1659 pos_type pos = cursor.pos();
1660 ParagraphList::iterator pit = cursor.par();
1662 if (cursor.boundary() && pos > 0)
1666 if (pos == pit->size())
1668 else // potentional bug... BUG (Lgb)
1669 if (pit->isSeparator(pos)) {
1670 if (pos > cursorRow()->pos() &&
1671 bidi_level(pos) % 2 ==
1672 bidi_level(pos - 1) % 2)
1674 else if (pos + 1 < pit->size())
1679 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1680 real_current_font = getFont(pit, pos);
1682 if (cursor.pos() == pit->size() &&
1683 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1684 !cursor.boundary()) {
1685 Language const * lang =
1686 pit->getParLanguage(bv()->buffer()->params);
1687 current_font.setLanguage(lang);
1688 current_font.setNumber(LyXFont::OFF);
1689 real_current_font.setLanguage(lang);
1690 real_current_font.setNumber(LyXFont::OFF);
1695 // returns the column near the specified x-coordinate of the row
1696 // x is set to the real beginning of this column
1698 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1701 double fill_separator;
1703 double fill_label_hfill;
1705 prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1707 pos_type vc = rit->pos();
1708 pos_type last = lastPrintablePos(*this, rit);
1711 ParagraphList::iterator rit_par = rit->par();
1712 LyXLayout_ptr const & layout = rit->par()->layout();
1714 bool left_side = false;
1716 pos_type body_pos = rit_par->beginningOfBody();
1717 double last_tmpx = tmpx;
1720 (body_pos - 1 > last ||
1721 !rit_par->isLineSeparator(body_pos - 1)))
1724 // check for empty row
1725 if (!rit_par->size()) {
1730 while (vc <= last && tmpx <= x) {
1733 if (body_pos > 0 && c == body_pos - 1) {
1734 tmpx += fill_label_hfill +
1735 font_metrics::width(layout->labelsep, getLabelFont(rit_par));
1736 if (rit_par->isLineSeparator(body_pos - 1))
1737 tmpx -= singleWidth(rit_par, body_pos - 1);
1740 if (hfillExpansion(*this, rit, c)) {
1741 tmpx += singleWidth(rit_par, c);
1745 tmpx += fill_label_hfill;
1746 } else if (rit_par->isSeparator(c)) {
1747 tmpx += singleWidth(rit_par, c);
1749 tmpx += fill_separator;
1751 tmpx += singleWidth(rit_par, c);
1756 if ((tmpx + last_tmpx) / 2 > x) {
1761 if (vc > last + 1) // This shouldn't happen.
1765 // This (rtl_support test) is not needed, but gives
1766 // some speedup if rtl_support=false
1767 RowList::iterator next_rit = boost::next(rit);
1769 bool const lastrow = lyxrc.rtl_support &&
1770 (next_rit == rowlist_.end() ||
1771 next_rit->par() != rit_par);
1773 // If lastrow is false, we don't need to compute
1774 // the value of rtl.
1775 bool const rtl = (lastrow)
1776 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1779 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1780 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1782 else if (vc == rit->pos()) {
1784 if (bidi_level(c) % 2 == 1)
1787 c = vis2log(vc - 1);
1788 bool const rtl = (bidi_level(c) % 2 == 1);
1789 if (left_side == rtl) {
1791 boundary = isBoundary(bv()->buffer(), *rit_par, c);
1795 if (rit->pos() <= last && c > last
1796 && rit_par->isNewline(last)) {
1797 if (bidi_level(last) % 2 == 0)
1798 tmpx -= singleWidth(rit_par, last);
1800 tmpx += singleWidth(rit_par, last);
1810 void LyXText::setCursorFromCoordinates(int x, int y)
1812 //LyXCursor old_cursor = cursor;
1813 setCursorFromCoordinates(cursor, x, y);
1815 #warning DEPM disabled, otherwise crash when entering new table
1816 //deleteEmptyParagraphMechanism(old_cursor);
1823 * return true if the cursor given is at the end of a row,
1824 * and the next row is filled by an inset that spans an entire
1827 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1829 RowList::iterator row = lt.getRow(cur);
1830 if (boost::next(row) == lt.rows().end())
1833 Row const & next = *boost::next(row);
1835 if (next.pos() != cur.pos() || next.par() != cur.par())
1838 if (cur.pos() == cur.par()->size()
1839 || !cur.par()->isInset(cur.pos()))
1842 InsetOld const * inset = cur.par()->getInset(cur.pos());
1843 if (inset->needFullRow() || inset->display())
1851 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1853 // Get the row first.
1855 RowList::iterator row = getRowNearY(y);
1857 pos_type const column = getColumnNearX(row, x, bound);
1858 cur.par(row->par());
1859 cur.pos(row->pos() + column);
1861 cur.y(y + row->baseline());
1863 if (beforeFullRowInset(*this, cur)) {
1864 pos_type const last = lastPrintablePos(*this, row);
1865 RowList::iterator next_row = boost::next(row);
1867 float x = getCursorX(next_row, cur.pos(), last, bound);
1869 cur.iy(y + row->height() + next_row->baseline());
1874 cur.boundary(bound);
1878 void LyXText::cursorLeft(bool internal)
1880 if (cursor.pos() > 0) {
1881 bool boundary = cursor.boundary();
1882 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1883 if (!internal && !boundary &&
1884 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1885 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1886 } else if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph.
1887 ParagraphList::iterator pit = boost::prior(cursor.par());
1888 setCursor(pit, pit->size());
1893 void LyXText::cursorRight(bool internal)
1895 bool const at_end = (cursor.pos() == cursor.par()->size());
1896 bool const at_newline = !at_end &&
1897 cursor.par()->isNewline(cursor.pos());
1899 if (!internal && cursor.boundary() && !at_newline)
1900 setCursor(cursor.par(), cursor.pos(), true, false);
1902 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1904 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1905 setCursor(cursor.par(), cursor.pos(), true, true);
1906 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1907 setCursor(boost::next(cursor.par()), 0);
1911 void LyXText::cursorUp(bool selecting)
1914 int x = cursor.x_fix();
1915 int y = cursor.y() - cursorRow()->baseline() - 1;
1916 setCursorFromCoordinates(x, y);
1919 int y1 = cursor.iy() - topy;
1922 InsetOld * inset_hit = checkInsetHit(x, y1);
1923 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1924 inset_hit->localDispatch(
1925 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1929 setCursorFromCoordinates(bv(), cursor.x_fix(),
1930 cursor.y() - cursorRow()->baseline() - 1);
1935 void LyXText::cursorDown(bool selecting)
1938 int x = cursor.x_fix();
1939 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1940 setCursorFromCoordinates(x, y);
1941 if (!selecting && cursorRow() == cursorIRow()) {
1943 int y1 = cursor.iy() - topy;
1946 InsetOld * inset_hit = checkInsetHit(x, y1);
1947 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1948 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1949 inset_hit->localDispatch(cmd);
1953 setCursorFromCoordinates(bv(), cursor.x_fix(),
1954 cursor.y() - cursorRow()->baseline()
1955 + cursorRow()->height() + 1);
1960 void LyXText::cursorUpParagraph()
1962 if (cursor.pos() > 0) {
1963 setCursor(cursor.par(), 0);
1965 else if (cursor.par() != ownerParagraphs().begin()) {
1966 setCursor(boost::prior(cursor.par()), 0);
1971 void LyXText::cursorDownParagraph()
1973 ParagraphList::iterator par = cursor.par();
1974 ParagraphList::iterator next_par = boost::next(par);
1976 if (next_par != ownerParagraphs().end()) {
1977 setCursor(next_par, 0);
1979 setCursor(par, par->size());
1983 // fix the cursor `cur' after a characters has been deleted at `where'
1984 // position. Called by deleteEmptyParagraphMechanism
1985 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
1986 LyXCursor const & where)
1988 // if cursor is not in the paragraph where the delete occured,
1990 if (cur.par() != where.par())
1993 // if cursor position is after the place where the delete occured,
1995 if (cur.pos() > where.pos())
1996 cur.pos(cur.pos()-1);
1998 // check also if we don't want to set the cursor on a spot behind the
1999 // pagragraph because we erased the last character.
2000 if (cur.pos() > cur.par()->size())
2001 cur.pos(cur.par()->size());
2003 // recompute row et al. for this cursor
2004 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2008 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2010 // Would be wrong to delete anything if we have a selection.
2011 if (selection.set())
2014 // We allow all kinds of "mumbo-jumbo" when freespacing.
2015 if (old_cursor.par()->layout()->free_spacing
2016 || old_cursor.par()->isFreeSpacing()) {
2020 /* Ok I'll put some comments here about what is missing.
2021 I have fixed BackSpace (and thus Delete) to not delete
2022 double-spaces automagically. I have also changed Cut,
2023 Copy and Paste to hopefully do some sensible things.
2024 There are still some small problems that can lead to
2025 double spaces stored in the document file or space at
2026 the beginning of paragraphs. This happens if you have
2027 the cursor betwenn to spaces and then save. Or if you
2028 cut and paste and the selection have a space at the
2029 beginning and then save right after the paste. I am
2030 sure none of these are very hard to fix, but I will
2031 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2032 that I can get some feedback. (Lgb)
2035 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2036 // delete the LineSeparator.
2039 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2040 // delete the LineSeparator.
2043 // If the pos around the old_cursor were spaces, delete one of them.
2044 if (old_cursor.par() != cursor.par()
2045 || old_cursor.pos() != cursor.pos()) {
2046 // Only if the cursor has really moved
2048 if (old_cursor.pos() > 0
2049 && old_cursor.pos() < old_cursor.par()->size()
2050 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2051 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2052 old_cursor.par()->erase(old_cursor.pos() - 1);
2053 redoParagraph(old_cursor.par());
2055 #ifdef WITH_WARNINGS
2056 #warning This will not work anymore when we have multiple views of the same buffer
2057 // In this case, we will have to correct also the cursors held by
2058 // other bufferviews. It will probably be easier to do that in a more
2059 // automated way in LyXCursor code. (JMarc 26/09/2001)
2061 // correct all cursors held by the LyXText
2062 fixCursorAfterDelete(cursor, old_cursor);
2063 fixCursorAfterDelete(selection.cursor, old_cursor);
2064 fixCursorAfterDelete(selection.start, old_cursor);
2065 fixCursorAfterDelete(selection.end, old_cursor);
2066 fixCursorAfterDelete(last_sel_cursor, old_cursor);
2071 // don't delete anything if this is the ONLY paragraph!
2072 if (ownerParagraphs().size() == 1)
2075 // Do not delete empty paragraphs with keepempty set.
2076 if (old_cursor.par()->allowEmpty())
2079 // only do our magic if we changed paragraph
2080 if (old_cursor.par() == cursor.par())
2083 // record if we have deleted a paragraph
2084 // we can't possibly have deleted a paragraph before this point
2085 bool deleted = false;
2087 if (old_cursor.par()->empty() ||
2088 (old_cursor.par()->size() == 1 &&
2089 old_cursor.par()->isLineSeparator(0))) {
2090 // ok, we will delete anything
2091 LyXCursor tmpcursor;
2095 bool selection_position_was_oldcursor_position = (
2096 selection.cursor.par() == old_cursor.par()
2097 && selection.cursor.pos() == old_cursor.pos());
2099 if (getRow(old_cursor) != rows().begin()) {
2100 RowList::iterator prevrow = boost::prior(getRow(old_cursor));
2103 cursor = old_cursor; // that undo can restore the right cursor position
2104 #warning FIXME. --end() iterator is usable here
2105 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2106 while (endpit != ownerParagraphs().end() &&
2107 endpit->getDepth()) {
2111 recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2112 boost::prior(endpit));
2116 removeRow(getRow(old_cursor));
2118 ownerParagraphs().erase(old_cursor.par());
2120 /* Breakagain the next par. Needed because of
2121 * the parindent that can occur or dissappear.
2122 * The next row can change its height, if
2123 * there is another layout before */
2124 RowList::iterator tmprit = boost::next(prevrow);
2125 if (tmprit != rows().end()) {
2129 setHeightOfRow(prevrow);
2131 RowList::iterator nextrow = boost::next(getRow(old_cursor));
2135 cursor = old_cursor; // that undo can restore the right cursor position
2136 #warning FIXME. --end() iterator is usable here
2137 ParagraphList::iterator endpit = boost::next(old_cursor.par());
2138 while (endpit != ownerParagraphs().end() &&
2139 endpit->getDepth()) {
2143 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2147 removeRow(getRow(old_cursor));
2149 ownerParagraphs().erase(old_cursor.par());
2151 /* Breakagain the next par. Needed because of
2152 the parindent that can occur or dissappear.
2153 The next row can change its height, if
2154 there is another layout before */
2155 if (nextrow != rows().end()) {
2156 breakAgain(nextrow);
2162 setCursorIntern(cursor.par(), cursor.pos());
2164 if (selection_position_was_oldcursor_position) {
2165 // correct selection
2166 selection.cursor = cursor;
2170 if (old_cursor.par()->stripLeadingSpaces()) {
2171 redoParagraph(old_cursor.par());
2173 setCursorIntern(cursor.par(), cursor.pos());
2174 selection.cursor = cursor;
2181 ParagraphList & LyXText::ownerParagraphs() const
2184 return inset_owner->paragraphs;
2186 return bv_owner->buffer()->paragraphs;
2190 bool LyXText::needRefresh() const
2192 return need_refresh_;
2196 void LyXText::clearPaint()
2198 need_refresh_ = false;
2202 void LyXText::postPaint()
2204 need_refresh_ = true;
2206 // We are an inset's lyxtext. Tell the top-level lyxtext
2207 // it needs to update the row we're in.
2209 bv()->text->postPaint();
2213 bool LyXText::isInInset() const
2215 // Sub-level has non-null bv owner and
2216 // non-null inset owner.
2217 return inset_owner != 0 && bv_owner != 0;
2221 int defaultRowHeight()
2223 LyXFont const font(LyXFont::ALL_SANE);
2224 return int(font_metrics::maxAscent(font)
2225 + font_metrics::maxDescent(font) * 1.5);