1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
31 #include "FloatList.h"
33 #include "ParagraphParameters.h"
35 #include "lyxrow_funcs.h"
36 #include "metricsinfo.h"
37 #include "paragraph_funcs.h"
39 #include "insets/insetbibitem.h"
40 #include "insets/insetenv.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
48 #include <boost/tuple/tuple.hpp>
52 using namespace lyx::support;
62 LyXText::LyXText(BufferView * bv)
63 : height(0), width(0), anchor_row_offset_(0),
64 inset_owner(0), the_locking_inset(0), bv_owner(bv)
66 anchor_row_ = endRow();
70 LyXText::LyXText(BufferView * bv, InsetText * inset)
71 : height(0), width(0), anchor_row_offset_(0),
72 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
74 anchor_row_ = endRow();
78 void LyXText::init(BufferView * bview)
82 ParagraphList::iterator const beg = ownerParagraphs().begin();
83 ParagraphList::iterator const end = ownerParagraphs().end();
84 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
90 anchor_row_ = endRow();
91 anchor_row_offset_ = 0;
93 current_font = getFont(beg, 0);
95 redoParagraphs(beg, end);
96 setCursorIntern(beg, 0);
97 selection.cursor = cursor;
103 // Gets the fully instantiated font at a given position in a paragraph
104 // Basically the same routine as Paragraph::getFont() in paragraph.C.
105 // The difference is that this one is used for displaying, and thus we
106 // are allowed to make cosmetic improvements. For instance make footnotes
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 InsetOld * LyXText::getInset() const
249 ParagraphList::iterator pit = cursor.par();
250 pos_type const pos = cursor.pos();
252 if (pos < pit->size() && pit->isInset(pos)) {
253 return pit->getInset(pos);
259 void LyXText::toggleInset()
261 InsetOld * inset = getInset();
262 // is there an editable inset at cursor position?
263 if (!isEditableInset(inset)) {
264 // No, try to see if we are inside a collapsable inset
265 if (inset_owner && inset_owner->owner()
266 && inset_owner->owner()->isOpen()) {
267 bv()->unlockInset(inset_owner->owner());
268 inset_owner->owner()->close(bv());
269 bv()->getLyXText()->cursorRight(bv());
273 //bv()->owner()->message(inset->editMessage());
275 // do we want to keep this?? (JMarc)
276 if (!isHighlyEditableInset(inset))
277 recordUndo(bv(), Undo::ATOMIC);
288 /* used in setlayout */
289 // Asger is not sure we want to do this...
290 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
293 LyXLayout_ptr const & layout = par.layout();
294 pos_type const psize = par.size();
297 for (pos_type pos = 0; pos < psize; ++pos) {
298 if (pos < par.beginningOfBody())
299 layoutfont = layout->labelfont;
301 layoutfont = layout->font;
303 LyXFont tmpfont = par.getFontSettings(params, pos);
304 tmpfont.reduce(layoutfont);
305 par.setFont(pos, tmpfont);
310 ParagraphList::iterator
311 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
312 LyXCursor & send_cur,
313 string const & layout)
315 ParagraphList::iterator endpit = boost::next(send_cur.par());
316 ParagraphList::iterator undoendpit = endpit;
317 ParagraphList::iterator pars_end = ownerParagraphs().end();
319 if (endpit != pars_end && endpit->getDepth()) {
320 while (endpit != pars_end && endpit->getDepth()) {
324 } else if (endpit != pars_end) {
325 // because of parindents etc.
329 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
331 // ok we have a selection. This is always between sstart_cur
332 // and sel_end cursor
334 ParagraphList::iterator pit = sstart_cur.par();
335 ParagraphList::iterator epit = boost::next(send_cur.par());
337 LyXLayout_ptr const & lyxlayout =
338 bv()->buffer()->params.getLyXTextClass()[layout];
341 pit->applyLayout(lyxlayout);
342 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
343 pit->params().spaceTop(lyxlayout->fill_top ?
344 VSpace(VSpace::VFILL)
345 : VSpace(VSpace::NONE));
346 pit->params().spaceBottom(lyxlayout->fill_bottom ?
347 VSpace(VSpace::VFILL)
348 : VSpace(VSpace::NONE));
349 if (lyxlayout->margintype == MARGIN_MANUAL)
350 pit->setLabelWidthString(lyxlayout->labelstring());
353 } while (pit != epit);
359 // set layout over selection and make a total rebreak of those paragraphs
360 void LyXText::setLayout(string const & layout)
362 LyXCursor tmpcursor = cursor; // store the current cursor
364 // if there is no selection just set the layout
365 // of the current paragraph
366 if (!selection.set()) {
367 selection.start = cursor; // dummy selection
368 selection.end = cursor;
371 // special handling of new environment insets
372 BufferParams const & params = bv()->buffer()->params;
373 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
374 if (lyxlayout->is_environment) {
375 // move everything in a new environment inset
376 lyxerr << "setting layout " << layout << endl;
377 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
378 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
379 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
380 InsetOld * inset = new InsetEnvironment(params, layout);
381 if (bv()->insertInset(inset)) {
383 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
390 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
391 selection.end, layout);
392 redoParagraphs(selection.start.par(), endpit);
394 // we have to reset the selection, because the
395 // geometry could have changed
396 setCursor(selection.start.par(), selection.start.pos(), false);
397 selection.cursor = cursor;
398 setCursor(selection.end.par(), selection.end.pos(), false);
402 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
406 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
408 ParagraphList::iterator pit = cursor.par();
409 ParagraphList::iterator end = cursor.par();
410 ParagraphList::iterator start = pit;
412 if (selection.set()) {
413 pit = selection.start.par();
414 end = selection.end.par();
418 ParagraphList::iterator pastend = boost::next(end);
421 recordUndo(bv(), Undo::ATOMIC, start, end);
423 bool changed = false;
425 int prev_after_depth = 0;
426 #warning parlist ... could be nicer ?
427 if (start != ownerParagraphs().begin()) {
428 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
432 int const depth = pit->params().depth();
433 if (type == bv_funcs::INC_DEPTH) {
434 if (depth < prev_after_depth
435 && pit->layout()->labeltype != LABEL_BIBLIO) {
438 pit->params().depth(depth + 1);
443 pit->params().depth(depth - 1);
446 prev_after_depth = pit->getMaxDepthAfter();
458 redoParagraphs(start, pastend);
460 // We need to actually move the text->cursor. I don't
461 // understand why ...
462 LyXCursor tmpcursor = cursor;
464 // we have to reset the visual selection because the
465 // geometry could have changed
466 if (selection.set()) {
467 setCursor(selection.start.par(), selection.start.pos());
468 selection.cursor = cursor;
469 setCursor(selection.end.par(), selection.end.pos());
472 // this handles the counter labels, and also fixes up
473 // depth values for follow-on (child) paragraphs
477 setCursor(tmpcursor.par(), tmpcursor.pos());
483 // set font over selection and make a total rebreak of those paragraphs
484 void LyXText::setFont(LyXFont const & font, bool toggleall)
486 // if there is no selection just set the current_font
487 if (!selection.set()) {
488 // Determine basis font
490 if (cursor.pos() < cursor.par()->beginningOfBody()) {
491 layoutfont = getLabelFont(cursor.par());
493 layoutfont = getLayoutFont(cursor.par());
495 // Update current font
496 real_current_font.update(font,
497 bv()->buffer()->params.language,
500 // Reduce to implicit settings
501 current_font = real_current_font;
502 current_font.reduce(layoutfont);
503 // And resolve it completely
504 real_current_font.realize(layoutfont);
509 LyXCursor tmpcursor = cursor; // store the current cursor
511 // ok we have a selection. This is always between sel_start_cursor
512 // and sel_end cursor
514 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
516 cursor = selection.start;
517 while (cursor.par() != selection.end.par() ||
518 cursor.pos() < selection.end.pos())
520 if (cursor.pos() < cursor.par()->size()) {
521 // an open footnote should behave like a closed one
522 setCharFont(cursor.par(), cursor.pos(),
524 cursor.pos(cursor.pos() + 1);
527 cursor.par(boost::next(cursor.par()));
532 redoParagraph(selection.start.par());
534 // we have to reset the selection, because the
535 // geometry could have changed, but we keep
536 // it for user convenience
537 setCursor(selection.start.par(), selection.start.pos());
538 selection.cursor = cursor;
539 setCursor(selection.end.par(), selection.end.pos());
541 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
542 tmpcursor.boundary());
546 // rebreaks all paragraphs between the specified pars
547 // This function is needed after SetLayout and SetFont etc.
548 void LyXText::redoParagraphs(ParagraphList::iterator start,
549 ParagraphList::iterator end)
551 for ( ; start != end; ++start)
552 redoParagraph(start);
556 void LyXText::redoParagraph(ParagraphList::iterator pit)
558 RowList::iterator rit = pit->rows.begin();
559 RowList::iterator end = pit->rows.end();
561 // remove rows of paragraph
562 for ( ; rit != end; ++rit) {
563 if (rit == anchor_row_)
564 anchor_row_ = endRow();
565 height -= rit->height();
569 // rebreak the paragraph
570 // insert a new row, starting at position 0
573 pit->rows.push_back(Row(z));
576 z = rowBreakPoint(pit, pit->rows.back());
578 RowList::iterator tmprow = boost::prior(pit->rows.end());
580 if (z >= pit->size())
584 pit->rows.push_back(Row(z));
587 tmprow->fill(fill(pit, tmprow, workWidth()));
588 setHeightOfRow(pit, tmprow);
591 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
595 void LyXText::fullRebreak()
597 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
599 selection.cursor = cursor;
603 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
605 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
606 //Assert(mi.base.textwidth);
612 anchor_row_ = endRow();
613 anchor_row_offset_ = 0;
615 ParagraphList::iterator pit = ownerParagraphs().begin();
616 ParagraphList::iterator end = ownerParagraphs().end();
618 for (; pit != end; ++pit) {
621 InsetList::iterator ii = pit->insetlist.begin();
622 InsetList::iterator iend = pit->insetlist.end();
623 for (; ii != iend; ++ii) {
626 #warning FIXME: pos != 0
627 m.base.font = getFont(pit, 0);
628 ii->inset->metrics(m, dim);
635 dim.asc = firstRow()->ascent_of_text();
636 dim.des = height - dim.asc;
637 dim.wid = std::max(mi.base.textwidth, int(width));
641 // important for the screen
644 // the cursor set functions have a special mechanism. When they
645 // realize, that you left an empty paragraph, they will delete it.
646 // They also delete the corresponding row
648 // need the selection cursor:
649 void LyXText::setSelection()
651 TextCursor::setSelection();
656 void LyXText::clearSelection()
658 TextCursor::clearSelection();
660 // reset this in the bv_owner!
661 if (bv_owner && bv_owner->text)
662 bv_owner->text->xsel_cache.set(false);
666 void LyXText::cursorHome()
668 setCursor(cursor.par(), cursorRow()->pos());
672 void LyXText::cursorEnd()
674 if (cursor.par()->empty())
677 RowList::iterator rit = cursorRow();
678 RowList::iterator next_rit = boost::next(rit);
679 RowList::iterator end = boost::next(rit);
680 ParagraphList::iterator pit = cursor.par();
681 pos_type last_pos = lastPos(*pit, rit);
683 if (next_rit == end) {
687 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
692 setCursor(pit, last_pos);
696 void LyXText::cursorTop()
698 setCursor(ownerParagraphs().begin(), 0);
702 void LyXText::cursorBottom()
704 ParagraphList::iterator lastpit =
705 boost::prior(ownerParagraphs().end());
706 setCursor(lastpit, lastpit->size());
710 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
712 // If the mask is completely neutral, tell user
713 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
714 // Could only happen with user style
715 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
719 // Try implicit word selection
720 // If there is a change in the language the implicit word selection
722 LyXCursor resetCursor = cursor;
723 bool implicitSelection = (font.language() == ignore_language
724 && font.number() == LyXFont::IGNORE)
725 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
728 setFont(font, toggleall);
730 // Implicit selections are cleared afterwards
731 //and cursor is set to the original position.
732 if (implicitSelection) {
734 cursor = resetCursor;
735 setCursor(cursor.par(), cursor.pos());
736 selection.cursor = cursor;
741 string LyXText::getStringToIndex()
743 // Try implicit word selection
744 // If there is a change in the language the implicit word selection
746 LyXCursor const reset_cursor = cursor;
747 bool const implicitSelection =
748 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
751 if (!selection.set())
752 bv()->owner()->message(_("Nothing to index!"));
753 else if (selection.start.par() != selection.end.par())
754 bv()->owner()->message(_("Cannot index more than one paragraph!"));
756 idxstring = selectionAsString(bv()->buffer(), false);
758 // Reset cursors to their original position.
759 cursor = reset_cursor;
760 setCursor(cursor.par(), cursor.pos());
761 selection.cursor = cursor;
763 // Clear the implicit selection.
764 if (implicitSelection)
771 // the DTP switches for paragraphs. LyX will store them in the first
772 // physical paragraph. When a paragraph is broken, the top settings rest,
773 // the bottom settings are given to the new one. So I can make sure,
774 // they do not duplicate themself and you cannnot make dirty things with
777 void LyXText::setParagraph(bool line_top, bool line_bottom,
778 bool pagebreak_top, bool pagebreak_bottom,
779 VSpace const & space_top,
780 VSpace const & space_bottom,
781 Spacing const & spacing,
783 string const & labelwidthstring,
786 LyXCursor tmpcursor = cursor;
787 if (!selection.set()) {
788 selection.start = cursor;
789 selection.end = cursor;
792 // make sure that the depth behind the selection are restored, too
793 ParagraphList::iterator endpit = boost::next(selection.end.par());
794 ParagraphList::iterator undoendpit = endpit;
795 ParagraphList::iterator pars_end = ownerParagraphs().end();
797 if (endpit != pars_end && endpit->getDepth()) {
798 while (endpit != pars_end && endpit->getDepth()) {
802 } else if (endpit != pars_end) {
803 // because of parindents etc.
807 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
808 boost::prior(undoendpit));
811 ParagraphList::iterator tmppit = selection.end.par();
813 while (tmppit != boost::prior(selection.start.par())) {
814 setCursor(tmppit, 0);
816 ParagraphList::iterator pit = cursor.par();
817 ParagraphParameters & params = pit->params();
819 params.lineTop(line_top);
820 params.lineBottom(line_bottom);
821 params.pagebreakTop(pagebreak_top);
822 params.pagebreakBottom(pagebreak_bottom);
823 params.spaceTop(space_top);
824 params.spaceBottom(space_bottom);
825 params.spacing(spacing);
826 // does the layout allow the new alignment?
827 LyXLayout_ptr const & layout = pit->layout();
829 if (align == LYX_ALIGN_LAYOUT)
830 align = layout->align;
831 if (align & layout->alignpossible) {
832 if (align == layout->align)
833 params.align(LYX_ALIGN_LAYOUT);
837 pit->setLabelWidthString(labelwidthstring);
838 params.noindent(noindent);
839 tmppit = boost::prior(pit);
842 redoParagraphs(selection.start.par(), endpit);
845 setCursor(selection.start.par(), selection.start.pos());
846 selection.cursor = cursor;
847 setCursor(selection.end.par(), selection.end.pos());
849 setCursor(tmpcursor.par(), tmpcursor.pos());
855 // set the counter of a paragraph. This includes the labels
856 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
858 LyXTextClass const & textclass = buf->params.getLyXTextClass();
859 LyXLayout_ptr const & layout = pit->layout();
861 if (pit != ownerParagraphs().begin()) {
863 pit->params().appendix(boost::prior(pit)->params().appendix());
864 if (!pit->params().appendix() &&
865 pit->params().startOfAppendix()) {
866 pit->params().appendix(true);
867 textclass.counters().reset();
869 pit->enumdepth = boost::prior(pit)->enumdepth;
870 pit->itemdepth = boost::prior(pit)->itemdepth;
872 pit->params().appendix(pit->params().startOfAppendix());
877 // Maybe we have to increment the enumeration depth.
878 // BUT, enumeration in a footnote is considered in isolation from its
879 // surrounding paragraph so don't increment if this is the
880 // first line of the footnote
881 // AND, bibliographies can't have their depth changed ie. they
882 // are always of depth 0
883 if (pit != ownerParagraphs().begin()
884 && boost::prior(pit)->getDepth() < pit->getDepth()
885 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
886 && pit->enumdepth < 3
887 && layout->labeltype != LABEL_BIBLIO) {
891 // Maybe we have to decrement the enumeration depth, see note above
892 if (pit != ownerParagraphs().begin()
893 && boost::prior(pit)->getDepth() > pit->getDepth()
894 && layout->labeltype != LABEL_BIBLIO) {
895 pit->enumdepth = depthHook(pit, ownerParagraphs(),
896 pit->getDepth())->enumdepth;
899 if (!pit->params().labelString().empty()) {
900 pit->params().labelString(string());
903 if (layout->margintype == MARGIN_MANUAL) {
904 if (pit->params().labelWidthString().empty())
905 pit->setLabelWidthString(layout->labelstring());
907 pit->setLabelWidthString(string());
910 // is it a layout that has an automatic label?
911 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
912 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
916 if (i >= 0 && i <= buf->params.secnumdepth) {
920 textclass.counters().step(layout->latexname());
922 // Is there a label? Useful for Chapter layout
923 if (!pit->params().appendix()) {
924 s << buf->B_(layout->labelstring());
926 s << buf->B_(layout->labelstring_appendix());
929 // Use of an integer is here less than elegant. For now.
930 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
931 if (!pit->params().appendix()) {
932 numbertype = "sectioning";
934 numbertype = "appendix";
935 if (pit->isRightToLeftPar(buf->params))
942 << textclass.counters()
943 .numberLabel(layout->latexname(),
944 numbertype, langtype, head);
946 pit->params().labelString(STRCONV(s.str()));
948 // reset enum counters
949 textclass.counters().reset("enum");
950 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
951 textclass.counters().reset("enum");
952 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
954 // Yes I know this is a really, really! bad solution
956 string enumcounter("enum");
958 switch (pit->enumdepth) {
970 // not a valid enumdepth...
974 textclass.counters().step(enumcounter);
976 s << textclass.counters()
977 .numberLabel(enumcounter, "enumeration");
978 pit->params().labelString(STRCONV(s.str()));
980 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
981 textclass.counters().step("bibitem");
982 int number = textclass.counters().value("bibitem");
983 if (pit->bibitem()) {
984 pit->bibitem()->setCounter(number);
985 pit->params().labelString(layout->labelstring());
987 // In biblio should't be following counters but...
989 string s = buf->B_(layout->labelstring());
992 if (layout->labeltype == LABEL_SENSITIVE) {
993 ParagraphList::iterator end = ownerParagraphs().end();
994 ParagraphList::iterator tmppit = pit;
997 while (tmppit != end && tmppit->inInset()
998 // the single '=' is intended below
999 && (in = tmppit->inInset()->owner()))
1001 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1002 in->lyxCode() == InsetOld::WRAP_CODE) {
1006 tmppit = ownerParagraphs().begin();
1007 for ( ; tmppit != end; ++tmppit)
1008 if (&*tmppit == in->parOwner())
1016 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1017 type = static_cast<InsetFloat*>(in)->params().type;
1018 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1019 type = static_cast<InsetWrap*>(in)->params().type;
1023 Floating const & fl = textclass.floats().getType(type);
1025 textclass.counters().step(fl.type());
1027 // Doesn't work... yet.
1028 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1030 // par->SetLayout(0);
1031 // s = layout->labelstring;
1032 s = _("Senseless: ");
1035 pit->params().labelString(s);
1037 // reset the enumeration counter. They are always reset
1038 // when there is any other layout between
1039 // Just fall-through between the cases so that all
1040 // enum counters deeper than enumdepth is also reset.
1041 switch (pit->enumdepth) {
1043 textclass.counters().reset("enumi");
1045 textclass.counters().reset("enumii");
1047 textclass.counters().reset("enumiii");
1049 textclass.counters().reset("enumiv");
1055 // Updates all counters. Paragraphs with changed label string will be rebroken
1056 void LyXText::updateCounters()
1059 bv()->buffer()->params.getLyXTextClass().counters().reset();
1061 ParagraphList::iterator beg = ownerParagraphs().begin();
1062 ParagraphList::iterator end = ownerParagraphs().end();
1063 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1064 string const oldLabel = pit->params().labelString();
1066 size_t maxdepth = 0;
1068 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1070 if (pit->params().depth() > maxdepth)
1071 pit->params().depth(maxdepth);
1073 // setCounter can potentially change the labelString.
1074 setCounter(bv()->buffer(), pit);
1076 string const & newLabel = pit->params().labelString();
1078 if (oldLabel != newLabel)
1084 void LyXText::insertInset(InsetOld * inset)
1086 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1088 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1090 cursor.par()->insertInset(cursor.pos(), inset);
1091 // Just to rebreak and refresh correctly.
1092 // The character will not be inserted a second time
1093 insertChar(Paragraph::META_INSET);
1094 // If we enter a highly editable inset the cursor should be before
1095 // the inset. After an Undo LyX tries to call inset->edit(...)
1096 // and fails if the cursor is behind the inset and getInset
1097 // does not return the inset!
1098 if (isHighlyEditableInset(inset))
1104 void LyXText::cutSelection(bool doclear, bool realcut)
1106 // Stuff what we got on the clipboard. Even if there is no selection.
1108 // There is a problem with having the stuffing here in that the
1109 // larger the selection the slower LyX will get. This can be
1110 // solved by running the line below only when the selection has
1111 // finished. The solution used currently just works, to make it
1112 // faster we need to be more clever and probably also have more
1113 // calls to stuffClipboard. (Lgb)
1114 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1116 // This doesn't make sense, if there is no selection
1117 if (!selection.set())
1120 // OK, we have a selection. This is always between selection.start
1121 // and selection.end
1123 // make sure that the depth behind the selection are restored, too
1124 ParagraphList::iterator endpit = boost::next(selection.end.par());
1125 ParagraphList::iterator undoendpit = endpit;
1126 ParagraphList::iterator pars_end = ownerParagraphs().end();
1128 if (endpit != pars_end && endpit->getDepth()) {
1129 while (endpit != pars_end && endpit->getDepth()) {
1131 undoendpit = endpit;
1133 } else if (endpit != pars_end) {
1134 // because of parindents etc.
1138 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1139 boost::prior(undoendpit));
1141 endpit = selection.end.par();
1142 int endpos = selection.end.pos();
1144 boost::tie(endpit, endpos) = realcut ?
1145 CutAndPaste::cutSelection(bv()->buffer()->params,
1147 selection.start.par(), endpit,
1148 selection.start.pos(), endpos,
1149 bv()->buffer()->params.textclass,
1151 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1153 selection.start.par(), endpit,
1154 selection.start.pos(), endpos,
1156 // sometimes necessary
1158 selection.start.par()->stripLeadingSpaces();
1160 redoParagraphs(selection.start.par(), boost::next(endpit));
1161 // cutSelection can invalidate the cursor so we need to set
1163 // we prefer the end for when tracking changes
1167 // need a valid cursor. (Lgb)
1170 setCursor(cursor.par(), cursor.pos());
1171 selection.cursor = cursor;
1176 void LyXText::copySelection()
1178 // stuff the selection onto the X clipboard, from an explicit copy request
1179 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1181 // this doesnt make sense, if there is no selection
1182 if (!selection.set())
1185 // ok we have a selection. This is always between selection.start
1186 // and sel_end cursor
1188 // copy behind a space if there is one
1189 while (selection.start.par()->size() > selection.start.pos()
1190 && selection.start.par()->isLineSeparator(selection.start.pos())
1191 && (selection.start.par() != selection.end.par()
1192 || selection.start.pos() < selection.end.pos()))
1193 selection.start.pos(selection.start.pos() + 1);
1195 CutAndPaste::copySelection(selection.start.par(),
1196 selection.end.par(),
1197 selection.start.pos(), selection.end.pos(),
1198 bv()->buffer()->params.textclass);
1202 void LyXText::pasteSelection(size_t sel_index)
1204 // this does not make sense, if there is nothing to paste
1205 if (!CutAndPaste::checkPastePossible())
1208 recordUndo(bv(), Undo::INSERT, cursor.par());
1210 ParagraphList::iterator endpit;
1215 boost::tie(ppp, endpit) =
1216 CutAndPaste::pasteSelection(*bv()->buffer(),
1218 cursor.par(), cursor.pos(),
1219 bv()->buffer()->params.textclass,
1221 bufferErrors(*bv()->buffer(), el);
1222 bv()->showErrorList(_("Paste"));
1224 redoParagraphs(cursor.par(), endpit);
1226 setCursor(cursor.par(), cursor.pos());
1229 selection.cursor = cursor;
1230 setCursor(ppp.first, ppp.second);
1236 void LyXText::setSelectionRange(lyx::pos_type length)
1241 selection.cursor = cursor;
1248 // simple replacing. The font of the first selected character is used
1249 void LyXText::replaceSelectionWithString(string const & str)
1251 recordUndo(bv(), Undo::ATOMIC);
1254 if (!selection.set()) { // create a dummy selection
1255 selection.end = cursor;
1256 selection.start = cursor;
1259 // Get font setting before we cut
1260 pos_type pos = selection.end.pos();
1261 LyXFont const font = selection.start.par()
1262 ->getFontSettings(bv()->buffer()->params,
1263 selection.start.pos());
1265 // Insert the new string
1266 string::const_iterator cit = str.begin();
1267 string::const_iterator end = str.end();
1268 for (; cit != end; ++cit) {
1269 selection.end.par()->insertChar(pos, (*cit), font);
1273 // Cut the selection
1274 cutSelection(true, false);
1280 // needed to insert the selection
1281 void LyXText::insertStringAsLines(string const & str)
1283 ParagraphList::iterator pit = cursor.par();
1284 pos_type pos = cursor.pos();
1285 ParagraphList::iterator endpit = boost::next(cursor.par());
1287 recordUndo(bv(), Undo::ATOMIC);
1289 // only to be sure, should not be neccessary
1292 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1294 redoParagraphs(cursor.par(), endpit);
1295 setCursor(cursor.par(), cursor.pos());
1296 selection.cursor = cursor;
1297 setCursor(pit, pos);
1302 // turns double-CR to single CR, others where converted into one
1303 // blank. Then InsertStringAsLines is called
1304 void LyXText::insertStringAsParagraphs(string const & str)
1306 string linestr(str);
1307 bool newline_inserted = false;
1308 string::size_type const siz = linestr.length();
1310 for (string::size_type i = 0; i < siz; ++i) {
1311 if (linestr[i] == '\n') {
1312 if (newline_inserted) {
1313 // we know that \r will be ignored by
1314 // InsertStringA. Of course, it is a dirty
1315 // trick, but it works...
1316 linestr[i - 1] = '\r';
1320 newline_inserted = true;
1322 } else if (IsPrintable(linestr[i])) {
1323 newline_inserted = false;
1326 insertStringAsLines(linestr);
1330 bool LyXText::setCursor(ParagraphList::iterator pit,
1332 bool setfont, bool boundary)
1334 LyXCursor old_cursor = cursor;
1335 setCursorIntern(pit, pos, setfont, boundary);
1336 return deleteEmptyParagraphMechanism(old_cursor);
1340 void LyXText::redoCursor()
1342 #warning maybe the same for selections?
1343 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1347 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1348 pos_type pos, bool boundary)
1350 Assert(pit != ownerParagraphs().end());
1354 cur.boundary(boundary);
1358 // get the cursor y position in text
1360 RowList::iterator row = getRow(pit, pos, y);
1361 RowList::iterator old_row = row;
1362 // if we are before the first char of this row and are still in the
1363 // same paragraph and there is a previous row then put the cursor on
1364 // the end of the previous row
1365 cur.iy(y + row->baseline());
1366 if (row != pit->rows.begin()
1368 && pos < pit->size()
1369 && pit->getChar(pos) == Paragraph::META_INSET) {
1370 InsetOld * ins = pit->getInset(pos);
1371 if (ins && (ins->needFullRow() || ins->display())) {
1377 // y is now the beginning of the cursor row
1378 y += row->baseline();
1379 // y is now the cursor baseline
1382 pos_type last = lastPrintablePos(*pit, old_row);
1384 // None of these should happen, but we're scaredy-cats
1385 if (pos > pit->size()) {
1386 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1389 } else if (pos > last + 1) {
1390 lyxerr << "dont like 2 please report" << endl;
1391 // This shouldn't happen.
1394 } else if (pos < row->pos()) {
1395 lyxerr << "dont like 3 please report" << endl;
1400 // now get the cursors x position
1401 float x = getCursorX(pit, row, pos, last, boundary);
1404 if (old_row != row) {
1405 x = getCursorX(pit, old_row, pos, last, boundary);
1409 /* We take out this for the time being because 1) the redraw code is not
1410 prepared to this yet and 2) because some good policy has yet to be decided
1411 while editting: for instance how to act on rows being created/deleted
1415 //if the cursor is in a visible row, anchor to it
1417 if (topy < y && y < topy + bv()->workHeight())
1423 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1424 pos_type pos, pos_type last, bool boundary) const
1426 pos_type cursor_vpos = 0;
1428 double fill_separator;
1430 double fill_label_hfill;
1431 // This call HAS to be here because of the BidiTables!!!
1432 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1435 pos_type const rit_pos = rit->pos();
1438 cursor_vpos = rit_pos;
1439 else if (pos > last && !boundary)
1440 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1441 ? rit_pos : last + 1;
1442 else if (pos > rit_pos && (pos > last || boundary))
1443 // Place cursor after char at (logical) position pos - 1
1444 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1445 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1447 // Place cursor before char at (logical) position pos
1448 cursor_vpos = (bidi_level(pos) % 2 == 0)
1449 ? log2vis(pos) : log2vis(pos) + 1;
1451 pos_type body_pos = pit->beginningOfBody();
1453 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1456 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1457 pos_type pos = vis2log(vpos);
1458 if (body_pos > 0 && pos == body_pos - 1) {
1459 x += fill_label_hfill +
1460 font_metrics::width(
1461 pit->layout()->labelsep, getLabelFont(pit));
1462 if (pit->isLineSeparator(body_pos - 1))
1463 x -= singleWidth(pit, body_pos - 1);
1466 if (hfillExpansion(*pit, rit, pos)) {
1467 x += singleWidth(pit, pos);
1468 if (pos >= body_pos)
1471 x += fill_label_hfill;
1472 } else if (pit->isSeparator(pos)) {
1473 x += singleWidth(pit, pos);
1474 if (pos >= body_pos)
1475 x += fill_separator;
1477 x += singleWidth(pit, pos);
1483 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1484 pos_type pos, bool setfont, bool boundary)
1486 setCursor(cursor, pit, pos, boundary);
1492 void LyXText::setCurrentFont()
1494 pos_type pos = cursor.pos();
1495 ParagraphList::iterator pit = cursor.par();
1497 if (cursor.boundary() && pos > 0)
1501 if (pos == pit->size())
1503 else // potentional bug... BUG (Lgb)
1504 if (pit->isSeparator(pos)) {
1505 if (pos > cursorRow()->pos() &&
1506 bidi_level(pos) % 2 ==
1507 bidi_level(pos - 1) % 2)
1509 else if (pos + 1 < pit->size())
1514 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1515 real_current_font = getFont(pit, pos);
1517 if (cursor.pos() == pit->size() &&
1518 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1519 !cursor.boundary()) {
1520 Language const * lang =
1521 pit->getParLanguage(bv()->buffer()->params);
1522 current_font.setLanguage(lang);
1523 current_font.setNumber(LyXFont::OFF);
1524 real_current_font.setLanguage(lang);
1525 real_current_font.setNumber(LyXFont::OFF);
1530 // returns the column near the specified x-coordinate of the row
1531 // x is set to the real beginning of this column
1532 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1533 RowList::iterator rit, int & x, bool & boundary) const
1536 double fill_separator;
1538 double fill_label_hfill;
1540 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1542 pos_type vc = rit->pos();
1543 pos_type last = lastPrintablePos(*pit, rit);
1545 LyXLayout_ptr const & layout = pit->layout();
1547 bool left_side = false;
1549 pos_type body_pos = pit->beginningOfBody();
1550 double last_tmpx = tmpx;
1553 (body_pos - 1 > last ||
1554 !pit->isLineSeparator(body_pos - 1)))
1557 // check for empty row
1563 while (vc <= last && tmpx <= x) {
1566 if (body_pos > 0 && c == body_pos - 1) {
1567 tmpx += fill_label_hfill +
1568 font_metrics::width(layout->labelsep, getLabelFont(pit));
1569 if (pit->isLineSeparator(body_pos - 1))
1570 tmpx -= singleWidth(pit, body_pos - 1);
1573 if (hfillExpansion(*pit, rit, c)) {
1574 tmpx += singleWidth(pit, c);
1578 tmpx += fill_label_hfill;
1579 } else if (pit->isSeparator(c)) {
1580 tmpx += singleWidth(pit, c);
1582 tmpx += fill_separator;
1584 tmpx += singleWidth(pit, c);
1589 if ((tmpx + last_tmpx) / 2 > x) {
1594 if (vc > last + 1) // This shouldn't happen.
1598 // This (rtl_support test) is not needed, but gives
1599 // some speedup if rtl_support == false
1600 bool const lastrow = lyxrc.rtl_support
1601 && boost::next(rit) == pit->rows.end();
1603 // If lastrow is false, we don't need to compute
1604 // the value of rtl.
1605 bool const rtl = (lastrow)
1606 ? pit->isRightToLeftPar(bv()->buffer()->params)
1609 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1610 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1612 else if (vc == rit->pos()) {
1614 if (bidi_level(c) % 2 == 1)
1617 c = vis2log(vc - 1);
1618 bool const rtl = (bidi_level(c) % 2 == 1);
1619 if (left_side == rtl) {
1621 boundary = isBoundary(bv()->buffer(), *pit, c);
1625 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1626 if (bidi_level(last) % 2 == 0)
1627 tmpx -= singleWidth(pit, last);
1629 tmpx += singleWidth(pit, last);
1639 void LyXText::setCursorFromCoordinates(int x, int y)
1641 //LyXCursor old_cursor = cursor;
1642 setCursorFromCoordinates(cursor, x, y);
1644 #warning DEPM disabled, otherwise crash when entering new table
1645 //deleteEmptyParagraphMechanism(old_cursor);
1652 * return true if the cursor given is at the end of a row,
1653 * and the next row is filled by an inset that spans an entire
1656 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1658 RowList::iterator row = lt.getRow(cur);
1659 RowList::iterator next = boost::next(row);
1661 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1664 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1667 InsetOld const * inset = cur.par()->getInset(cur.pos());
1668 if (inset->needFullRow() || inset->display())
1676 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1678 // Get the row first.
1679 ParagraphList::iterator pit;
1680 RowList::iterator row = getRowNearY(y, pit);
1682 pos_type const column = getColumnNearX(pit, row, x, bound);
1684 cur.pos(row->pos() + column);
1686 cur.y(y + row->baseline());
1688 // if (beforeFullRowInset(*this, cur)) {
1689 // pos_type const last = lastPrintablePos(*this, pit, row);
1690 // RowList::iterator next_row = nextRow(row);
1691 // cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1692 // cur.iy(y + row->height() + next_row->baseline());
1697 cur.boundary(bound);
1701 void LyXText::cursorLeft(bool internal)
1703 if (cursor.pos() > 0) {
1704 bool boundary = cursor.boundary();
1705 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1706 if (!internal && !boundary &&
1707 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1708 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1709 } else if (cursor.par() != ownerParagraphs().begin()) {
1710 // steps into the paragraph above
1711 ParagraphList::iterator pit = boost::prior(cursor.par());
1712 setCursor(pit, pit->size());
1717 void LyXText::cursorRight(bool internal)
1719 bool const at_end = (cursor.pos() == cursor.par()->size());
1720 bool const at_newline = !at_end &&
1721 cursor.par()->isNewline(cursor.pos());
1723 if (!internal && cursor.boundary() && !at_newline)
1724 setCursor(cursor.par(), cursor.pos(), true, false);
1726 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1728 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1729 setCursor(cursor.par(), cursor.pos(), true, true);
1730 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1731 setCursor(boost::next(cursor.par()), 0);
1735 void LyXText::cursorUp(bool selecting)
1738 int x = cursor.x_fix();
1739 int y = cursor.y() - cursorRow()->baseline() - 1;
1740 setCursorFromCoordinates(x, y);
1743 int y1 = cursor.iy() - topy;
1746 InsetOld * inset_hit = checkInsetHit(x, y1);
1747 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1748 inset_hit->localDispatch(
1749 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1753 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1754 cursorRow()->baseline() << endl;
1755 setCursorFromCoordinates(cursor.x_fix(),
1756 cursor.y() - cursorRow()->baseline() - 1);
1761 void LyXText::cursorDown(bool selecting)
1764 int x = cursor.x_fix();
1765 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1766 setCursorFromCoordinates(x, y);
1767 if (!selecting && cursorRow() == cursorIRow()) {
1769 int y1 = cursor.iy() - topy;
1772 InsetOld * inset_hit = checkInsetHit(x, y1);
1773 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1774 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1775 inset_hit->localDispatch(cmd);
1779 setCursorFromCoordinates(cursor.x_fix(),
1780 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1785 void LyXText::cursorUpParagraph()
1787 if (cursor.pos() > 0)
1788 setCursor(cursor.par(), 0);
1789 else if (cursor.par() != ownerParagraphs().begin())
1790 setCursor(boost::prior(cursor.par()), 0);
1794 void LyXText::cursorDownParagraph()
1796 ParagraphList::iterator par = cursor.par();
1797 ParagraphList::iterator next_par = boost::next(par);
1799 if (next_par != ownerParagraphs().end())
1800 setCursor(next_par, 0);
1802 setCursor(par, par->size());
1806 // fix the cursor `cur' after a characters has been deleted at `where'
1807 // position. Called by deleteEmptyParagraphMechanism
1808 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1810 // if cursor is not in the paragraph where the delete occured,
1812 if (cur.par() != where.par())
1815 // if cursor position is after the place where the delete occured,
1817 if (cur.pos() > where.pos())
1818 cur.pos(cur.pos()-1);
1820 // check also if we don't want to set the cursor on a spot behind the
1821 // pagragraph because we erased the last character.
1822 if (cur.pos() > cur.par()->size())
1823 cur.pos(cur.par()->size());
1825 // recompute row et al. for this cursor
1826 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1830 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1832 // Would be wrong to delete anything if we have a selection.
1833 if (selection.set())
1836 // We allow all kinds of "mumbo-jumbo" when freespacing.
1837 if (old_cursor.par()->isFreeSpacing())
1840 /* Ok I'll put some comments here about what is missing.
1841 I have fixed BackSpace (and thus Delete) to not delete
1842 double-spaces automagically. I have also changed Cut,
1843 Copy and Paste to hopefully do some sensible things.
1844 There are still some small problems that can lead to
1845 double spaces stored in the document file or space at
1846 the beginning of paragraphs. This happens if you have
1847 the cursor betwenn to spaces and then save. Or if you
1848 cut and paste and the selection have a space at the
1849 beginning and then save right after the paste. I am
1850 sure none of these are very hard to fix, but I will
1851 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1852 that I can get some feedback. (Lgb)
1855 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1856 // delete the LineSeparator.
1859 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1860 // delete the LineSeparator.
1863 // If the pos around the old_cursor were spaces, delete one of them.
1864 if (old_cursor.par() != cursor.par()
1865 || old_cursor.pos() != cursor.pos()) {
1867 // Only if the cursor has really moved
1868 if (old_cursor.pos() > 0
1869 && old_cursor.pos() < old_cursor.par()->size()
1870 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1871 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1872 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1873 redoParagraph(old_cursor.par());
1877 #ifdef WITH_WARNINGS
1878 #warning This will not work anymore when we have multiple views of the same buffer
1879 // In this case, we will have to correct also the cursors held by
1880 // other bufferviews. It will probably be easier to do that in a more
1881 // automated way in LyXCursor code. (JMarc 26/09/2001)
1883 // correct all cursors held by the LyXText
1884 fixCursorAfterDelete(cursor, old_cursor);
1885 fixCursorAfterDelete(selection.cursor, old_cursor);
1886 fixCursorAfterDelete(selection.start, old_cursor);
1887 fixCursorAfterDelete(selection.end, old_cursor);
1892 // don't delete anything if this is the ONLY paragraph!
1893 if (ownerParagraphs().size() == 1)
1896 // Do not delete empty paragraphs with keepempty set.
1897 if (old_cursor.par()->allowEmpty())
1900 // only do our magic if we changed paragraph
1901 if (old_cursor.par() == cursor.par())
1904 // record if we have deleted a paragraph
1905 // we can't possibly have deleted a paragraph before this point
1906 bool deleted = false;
1908 if (old_cursor.par()->empty() ||
1909 (old_cursor.par()->size() == 1 &&
1910 old_cursor.par()->isLineSeparator(0))) {
1911 // ok, we will delete anything
1912 LyXCursor tmpcursor;
1916 bool selection_position_was_oldcursor_position = (
1917 selection.cursor.par() == old_cursor.par()
1918 && selection.cursor.pos() == old_cursor.pos());
1921 cursor = old_cursor; // that undo can restore the right cursor position
1923 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1924 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1927 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1931 ownerParagraphs().erase(old_cursor.par());
1935 setCursorIntern(cursor.par(), cursor.pos());
1937 if (selection_position_was_oldcursor_position) {
1938 // correct selection
1939 selection.cursor = cursor;
1943 if (old_cursor.par()->stripLeadingSpaces()) {
1944 redoParagraph(old_cursor.par());
1946 setCursorIntern(cursor.par(), cursor.pos());
1947 selection.cursor = cursor;
1954 ParagraphList & LyXText::ownerParagraphs() const
1957 return inset_owner->paragraphs;
1959 return bv_owner->buffer()->paragraphs;
1963 bool LyXText::isInInset() const
1965 // Sub-level has non-null bv owner and non-null inset owner.
1966 return inset_owner != 0 && bv_owner != 0;
1970 int defaultRowHeight()
1972 LyXFont const font(LyXFont::ALL_SANE);
1973 return int(font_metrics::maxAscent(font)
1974 + font_metrics::maxDescent(font) * 1.5);