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
563 for (int i = 0; rit != end; ++rit, ++i) {
564 if (rit == anchor_row_)
566 height -= rit->height();
570 // rebreak the paragraph
571 // insert a new row, starting at position 0
574 pit->rows.push_back(Row(z));
577 z = rowBreakPoint(pit, pit->rows.back());
579 RowList::iterator tmprow = boost::prior(pit->rows.end());
581 if (z >= pit->size())
585 pit->rows.push_back(Row(z));
588 tmprow->fill(fill(pit, tmprow, workWidth()));
589 setHeightOfRow(pit, tmprow);
592 if (anchor_cnt == -1) {
593 if (anchor_cnt >= pit->rows.size())
594 anchor_cnt = pit->rows.size();
595 anchor_row_ = pit->rows.begin();
596 advance(anchor_row_, anchor_cnt);
599 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
603 void LyXText::fullRebreak()
605 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
607 selection.cursor = cursor;
611 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
613 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
614 //Assert(mi.base.textwidth);
620 anchor_row_ = endRow();
621 anchor_row_offset_ = 0;
623 ParagraphList::iterator pit = ownerParagraphs().begin();
624 ParagraphList::iterator end = ownerParagraphs().end();
626 for (; pit != end; ++pit) {
629 InsetList::iterator ii = pit->insetlist.begin();
630 InsetList::iterator iend = pit->insetlist.end();
631 for (; ii != iend; ++ii) {
634 #warning FIXME: pos != 0
635 m.base.font = getFont(pit, 0);
636 ii->inset->metrics(m, dim);
643 dim.asc = firstRow()->ascent_of_text();
644 dim.des = height - dim.asc;
645 dim.wid = std::max(mi.base.textwidth, int(width));
649 // important for the screen
652 // the cursor set functions have a special mechanism. When they
653 // realize, that you left an empty paragraph, they will delete it.
654 // They also delete the corresponding row
656 // need the selection cursor:
657 void LyXText::setSelection()
659 TextCursor::setSelection();
664 void LyXText::clearSelection()
666 TextCursor::clearSelection();
668 // reset this in the bv_owner!
669 if (bv_owner && bv_owner->text)
670 bv_owner->text->xsel_cache.set(false);
674 void LyXText::cursorHome()
676 setCursor(cursor.par(), cursorRow()->pos());
680 void LyXText::cursorEnd()
682 if (cursor.par()->empty())
685 RowList::iterator rit = cursorRow();
686 RowList::iterator next_rit = boost::next(rit);
687 RowList::iterator end = boost::next(rit);
688 ParagraphList::iterator pit = cursor.par();
689 pos_type last_pos = lastPos(*pit, rit);
691 if (next_rit == end) {
695 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
700 setCursor(pit, last_pos);
704 void LyXText::cursorTop()
706 setCursor(ownerParagraphs().begin(), 0);
710 void LyXText::cursorBottom()
712 ParagraphList::iterator lastpit =
713 boost::prior(ownerParagraphs().end());
714 setCursor(lastpit, lastpit->size());
718 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
720 // If the mask is completely neutral, tell user
721 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
722 // Could only happen with user style
723 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
727 // Try implicit word selection
728 // If there is a change in the language the implicit word selection
730 LyXCursor resetCursor = cursor;
731 bool implicitSelection = (font.language() == ignore_language
732 && font.number() == LyXFont::IGNORE)
733 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
736 setFont(font, toggleall);
738 // Implicit selections are cleared afterwards
739 //and cursor is set to the original position.
740 if (implicitSelection) {
742 cursor = resetCursor;
743 setCursor(cursor.par(), cursor.pos());
744 selection.cursor = cursor;
749 string LyXText::getStringToIndex()
751 // Try implicit word selection
752 // If there is a change in the language the implicit word selection
754 LyXCursor const reset_cursor = cursor;
755 bool const implicitSelection =
756 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
759 if (!selection.set())
760 bv()->owner()->message(_("Nothing to index!"));
761 else if (selection.start.par() != selection.end.par())
762 bv()->owner()->message(_("Cannot index more than one paragraph!"));
764 idxstring = selectionAsString(bv()->buffer(), false);
766 // Reset cursors to their original position.
767 cursor = reset_cursor;
768 setCursor(cursor.par(), cursor.pos());
769 selection.cursor = cursor;
771 // Clear the implicit selection.
772 if (implicitSelection)
779 // the DTP switches for paragraphs. LyX will store them in the first
780 // physical paragraph. When a paragraph is broken, the top settings rest,
781 // the bottom settings are given to the new one. So I can make sure,
782 // they do not duplicate themself and you cannnot make dirty things with
785 void LyXText::setParagraph(bool line_top, bool line_bottom,
786 bool pagebreak_top, bool pagebreak_bottom,
787 VSpace const & space_top,
788 VSpace const & space_bottom,
789 Spacing const & spacing,
791 string const & labelwidthstring,
794 LyXCursor tmpcursor = cursor;
795 if (!selection.set()) {
796 selection.start = cursor;
797 selection.end = cursor;
800 // make sure that the depth behind the selection are restored, too
801 ParagraphList::iterator endpit = boost::next(selection.end.par());
802 ParagraphList::iterator undoendpit = endpit;
803 ParagraphList::iterator pars_end = ownerParagraphs().end();
805 if (endpit != pars_end && endpit->getDepth()) {
806 while (endpit != pars_end && endpit->getDepth()) {
810 } else if (endpit != pars_end) {
811 // because of parindents etc.
815 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
816 boost::prior(undoendpit));
819 ParagraphList::iterator tmppit = selection.end.par();
821 while (tmppit != boost::prior(selection.start.par())) {
822 setCursor(tmppit, 0);
824 ParagraphList::iterator pit = cursor.par();
825 ParagraphParameters & params = pit->params();
827 params.lineTop(line_top);
828 params.lineBottom(line_bottom);
829 params.pagebreakTop(pagebreak_top);
830 params.pagebreakBottom(pagebreak_bottom);
831 params.spaceTop(space_top);
832 params.spaceBottom(space_bottom);
833 params.spacing(spacing);
834 // does the layout allow the new alignment?
835 LyXLayout_ptr const & layout = pit->layout();
837 if (align == LYX_ALIGN_LAYOUT)
838 align = layout->align;
839 if (align & layout->alignpossible) {
840 if (align == layout->align)
841 params.align(LYX_ALIGN_LAYOUT);
845 pit->setLabelWidthString(labelwidthstring);
846 params.noindent(noindent);
847 tmppit = boost::prior(pit);
850 redoParagraphs(selection.start.par(), endpit);
853 setCursor(selection.start.par(), selection.start.pos());
854 selection.cursor = cursor;
855 setCursor(selection.end.par(), selection.end.pos());
857 setCursor(tmpcursor.par(), tmpcursor.pos());
863 // set the counter of a paragraph. This includes the labels
864 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
866 LyXTextClass const & textclass = buf->params.getLyXTextClass();
867 LyXLayout_ptr const & layout = pit->layout();
869 if (pit != ownerParagraphs().begin()) {
871 pit->params().appendix(boost::prior(pit)->params().appendix());
872 if (!pit->params().appendix() &&
873 pit->params().startOfAppendix()) {
874 pit->params().appendix(true);
875 textclass.counters().reset();
877 pit->enumdepth = boost::prior(pit)->enumdepth;
878 pit->itemdepth = boost::prior(pit)->itemdepth;
880 pit->params().appendix(pit->params().startOfAppendix());
885 // Maybe we have to increment the enumeration depth.
886 // BUT, enumeration in a footnote is considered in isolation from its
887 // surrounding paragraph so don't increment if this is the
888 // first line of the footnote
889 // AND, bibliographies can't have their depth changed ie. they
890 // are always of depth 0
891 if (pit != ownerParagraphs().begin()
892 && boost::prior(pit)->getDepth() < pit->getDepth()
893 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
894 && pit->enumdepth < 3
895 && layout->labeltype != LABEL_BIBLIO) {
899 // Maybe we have to decrement the enumeration depth, see note above
900 if (pit != ownerParagraphs().begin()
901 && boost::prior(pit)->getDepth() > pit->getDepth()
902 && layout->labeltype != LABEL_BIBLIO) {
903 pit->enumdepth = depthHook(pit, ownerParagraphs(),
904 pit->getDepth())->enumdepth;
907 if (!pit->params().labelString().empty()) {
908 pit->params().labelString(string());
911 if (layout->margintype == MARGIN_MANUAL) {
912 if (pit->params().labelWidthString().empty())
913 pit->setLabelWidthString(layout->labelstring());
915 pit->setLabelWidthString(string());
918 // is it a layout that has an automatic label?
919 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
920 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
924 if (i >= 0 && i <= buf->params.secnumdepth) {
928 textclass.counters().step(layout->latexname());
930 // Is there a label? Useful for Chapter layout
931 if (!pit->params().appendix()) {
932 s << buf->B_(layout->labelstring());
934 s << buf->B_(layout->labelstring_appendix());
937 // Use of an integer is here less than elegant. For now.
938 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
939 if (!pit->params().appendix()) {
940 numbertype = "sectioning";
942 numbertype = "appendix";
943 if (pit->isRightToLeftPar(buf->params))
950 << textclass.counters()
951 .numberLabel(layout->latexname(),
952 numbertype, langtype, head);
954 pit->params().labelString(STRCONV(s.str()));
956 // reset enum counters
957 textclass.counters().reset("enum");
958 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
959 textclass.counters().reset("enum");
960 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
962 // Yes I know this is a really, really! bad solution
964 string enumcounter("enum");
966 switch (pit->enumdepth) {
978 // not a valid enumdepth...
982 textclass.counters().step(enumcounter);
984 s << textclass.counters()
985 .numberLabel(enumcounter, "enumeration");
986 pit->params().labelString(STRCONV(s.str()));
988 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
989 textclass.counters().step("bibitem");
990 int number = textclass.counters().value("bibitem");
991 if (pit->bibitem()) {
992 pit->bibitem()->setCounter(number);
993 pit->params().labelString(layout->labelstring());
995 // In biblio should't be following counters but...
997 string s = buf->B_(layout->labelstring());
1000 if (layout->labeltype == LABEL_SENSITIVE) {
1001 ParagraphList::iterator end = ownerParagraphs().end();
1002 ParagraphList::iterator tmppit = pit;
1005 while (tmppit != end && tmppit->inInset()
1006 // the single '=' is intended below
1007 && (in = tmppit->inInset()->owner()))
1009 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1010 in->lyxCode() == InsetOld::WRAP_CODE) {
1014 tmppit = ownerParagraphs().begin();
1015 for ( ; tmppit != end; ++tmppit)
1016 if (&*tmppit == in->parOwner())
1024 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1025 type = static_cast<InsetFloat*>(in)->params().type;
1026 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1027 type = static_cast<InsetWrap*>(in)->params().type;
1031 Floating const & fl = textclass.floats().getType(type);
1033 textclass.counters().step(fl.type());
1035 // Doesn't work... yet.
1036 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1038 // par->SetLayout(0);
1039 // s = layout->labelstring;
1040 s = _("Senseless: ");
1043 pit->params().labelString(s);
1045 // reset the enumeration counter. They are always reset
1046 // when there is any other layout between
1047 // Just fall-through between the cases so that all
1048 // enum counters deeper than enumdepth is also reset.
1049 switch (pit->enumdepth) {
1051 textclass.counters().reset("enumi");
1053 textclass.counters().reset("enumii");
1055 textclass.counters().reset("enumiii");
1057 textclass.counters().reset("enumiv");
1063 // Updates all counters. Paragraphs with changed label string will be rebroken
1064 void LyXText::updateCounters()
1067 bv()->buffer()->params.getLyXTextClass().counters().reset();
1069 ParagraphList::iterator beg = ownerParagraphs().begin();
1070 ParagraphList::iterator end = ownerParagraphs().end();
1071 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1072 string const oldLabel = pit->params().labelString();
1074 size_t maxdepth = 0;
1076 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1078 if (pit->params().depth() > maxdepth)
1079 pit->params().depth(maxdepth);
1081 // setCounter can potentially change the labelString.
1082 setCounter(bv()->buffer(), pit);
1084 string const & newLabel = pit->params().labelString();
1086 if (oldLabel != newLabel)
1092 void LyXText::insertInset(InsetOld * inset)
1094 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1096 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1098 cursor.par()->insertInset(cursor.pos(), inset);
1099 // Just to rebreak and refresh correctly.
1100 // The character will not be inserted a second time
1101 insertChar(Paragraph::META_INSET);
1102 // If we enter a highly editable inset the cursor should be before
1103 // the inset. After an Undo LyX tries to call inset->edit(...)
1104 // and fails if the cursor is behind the inset and getInset
1105 // does not return the inset!
1106 if (isHighlyEditableInset(inset))
1112 void LyXText::cutSelection(bool doclear, bool realcut)
1114 // Stuff what we got on the clipboard. Even if there is no selection.
1116 // There is a problem with having the stuffing here in that the
1117 // larger the selection the slower LyX will get. This can be
1118 // solved by running the line below only when the selection has
1119 // finished. The solution used currently just works, to make it
1120 // faster we need to be more clever and probably also have more
1121 // calls to stuffClipboard. (Lgb)
1122 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1124 // This doesn't make sense, if there is no selection
1125 if (!selection.set())
1128 // OK, we have a selection. This is always between selection.start
1129 // and selection.end
1131 // make sure that the depth behind the selection are restored, too
1132 ParagraphList::iterator endpit = boost::next(selection.end.par());
1133 ParagraphList::iterator undoendpit = endpit;
1134 ParagraphList::iterator pars_end = ownerParagraphs().end();
1136 if (endpit != pars_end && endpit->getDepth()) {
1137 while (endpit != pars_end && endpit->getDepth()) {
1139 undoendpit = endpit;
1141 } else if (endpit != pars_end) {
1142 // because of parindents etc.
1146 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1147 boost::prior(undoendpit));
1149 endpit = selection.end.par();
1150 int endpos = selection.end.pos();
1152 boost::tie(endpit, endpos) = realcut ?
1153 CutAndPaste::cutSelection(bv()->buffer()->params,
1155 selection.start.par(), endpit,
1156 selection.start.pos(), endpos,
1157 bv()->buffer()->params.textclass,
1159 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1161 selection.start.par(), endpit,
1162 selection.start.pos(), endpos,
1164 // sometimes necessary
1166 selection.start.par()->stripLeadingSpaces();
1168 redoParagraphs(selection.start.par(), boost::next(endpit));
1169 // cutSelection can invalidate the cursor so we need to set
1171 // we prefer the end for when tracking changes
1175 // need a valid cursor. (Lgb)
1178 setCursor(cursor.par(), cursor.pos());
1179 selection.cursor = cursor;
1184 void LyXText::copySelection()
1186 // stuff the selection onto the X clipboard, from an explicit copy request
1187 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1189 // this doesnt make sense, if there is no selection
1190 if (!selection.set())
1193 // ok we have a selection. This is always between selection.start
1194 // and sel_end cursor
1196 // copy behind a space if there is one
1197 while (selection.start.par()->size() > selection.start.pos()
1198 && selection.start.par()->isLineSeparator(selection.start.pos())
1199 && (selection.start.par() != selection.end.par()
1200 || selection.start.pos() < selection.end.pos()))
1201 selection.start.pos(selection.start.pos() + 1);
1203 CutAndPaste::copySelection(selection.start.par(),
1204 selection.end.par(),
1205 selection.start.pos(), selection.end.pos(),
1206 bv()->buffer()->params.textclass);
1210 void LyXText::pasteSelection(size_t sel_index)
1212 // this does not make sense, if there is nothing to paste
1213 if (!CutAndPaste::checkPastePossible())
1216 recordUndo(bv(), Undo::INSERT, cursor.par());
1218 ParagraphList::iterator endpit;
1223 boost::tie(ppp, endpit) =
1224 CutAndPaste::pasteSelection(*bv()->buffer(),
1226 cursor.par(), cursor.pos(),
1227 bv()->buffer()->params.textclass,
1229 bufferErrors(*bv()->buffer(), el);
1230 bv()->showErrorList(_("Paste"));
1232 redoParagraphs(cursor.par(), endpit);
1234 setCursor(cursor.par(), cursor.pos());
1237 selection.cursor = cursor;
1238 setCursor(ppp.first, ppp.second);
1244 void LyXText::setSelectionRange(lyx::pos_type length)
1249 selection.cursor = cursor;
1256 // simple replacing. The font of the first selected character is used
1257 void LyXText::replaceSelectionWithString(string const & str)
1259 recordUndo(bv(), Undo::ATOMIC);
1262 if (!selection.set()) { // create a dummy selection
1263 selection.end = cursor;
1264 selection.start = cursor;
1267 // Get font setting before we cut
1268 pos_type pos = selection.end.pos();
1269 LyXFont const font = selection.start.par()
1270 ->getFontSettings(bv()->buffer()->params,
1271 selection.start.pos());
1273 // Insert the new string
1274 string::const_iterator cit = str.begin();
1275 string::const_iterator end = str.end();
1276 for (; cit != end; ++cit) {
1277 selection.end.par()->insertChar(pos, (*cit), font);
1281 // Cut the selection
1282 cutSelection(true, false);
1288 // needed to insert the selection
1289 void LyXText::insertStringAsLines(string const & str)
1291 ParagraphList::iterator pit = cursor.par();
1292 pos_type pos = cursor.pos();
1293 ParagraphList::iterator endpit = boost::next(cursor.par());
1295 recordUndo(bv(), Undo::ATOMIC);
1297 // only to be sure, should not be neccessary
1300 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1302 redoParagraphs(cursor.par(), endpit);
1303 setCursor(cursor.par(), cursor.pos());
1304 selection.cursor = cursor;
1305 setCursor(pit, pos);
1310 // turns double-CR to single CR, others where converted into one
1311 // blank. Then InsertStringAsLines is called
1312 void LyXText::insertStringAsParagraphs(string const & str)
1314 string linestr(str);
1315 bool newline_inserted = false;
1316 string::size_type const siz = linestr.length();
1318 for (string::size_type i = 0; i < siz; ++i) {
1319 if (linestr[i] == '\n') {
1320 if (newline_inserted) {
1321 // we know that \r will be ignored by
1322 // InsertStringA. Of course, it is a dirty
1323 // trick, but it works...
1324 linestr[i - 1] = '\r';
1328 newline_inserted = true;
1330 } else if (IsPrintable(linestr[i])) {
1331 newline_inserted = false;
1334 insertStringAsLines(linestr);
1338 bool LyXText::setCursor(ParagraphList::iterator pit,
1340 bool setfont, bool boundary)
1342 LyXCursor old_cursor = cursor;
1343 setCursorIntern(pit, pos, setfont, boundary);
1344 return deleteEmptyParagraphMechanism(old_cursor);
1348 void LyXText::redoCursor()
1350 #warning maybe the same for selections?
1351 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1355 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1356 pos_type pos, bool boundary)
1358 Assert(pit != ownerParagraphs().end());
1362 cur.boundary(boundary);
1366 // get the cursor y position in text
1368 RowList::iterator row = getRow(pit, pos, y);
1369 RowList::iterator old_row = row;
1370 // if we are before the first char of this row and are still in the
1371 // same paragraph and there is a previous row then put the cursor on
1372 // the end of the previous row
1373 cur.iy(y + row->baseline());
1374 if (row != pit->rows.begin()
1376 && pos < pit->size()
1377 && pit->getChar(pos) == Paragraph::META_INSET) {
1378 InsetOld * ins = pit->getInset(pos);
1379 if (ins && (ins->needFullRow() || ins->display())) {
1385 // y is now the beginning of the cursor row
1386 y += row->baseline();
1387 // y is now the cursor baseline
1390 pos_type last = lastPrintablePos(*pit, old_row);
1392 // None of these should happen, but we're scaredy-cats
1393 if (pos > pit->size()) {
1394 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1397 } else if (pos > last + 1) {
1398 lyxerr << "dont like 2 please report" << endl;
1399 // This shouldn't happen.
1402 } else if (pos < row->pos()) {
1403 lyxerr << "dont like 3 please report" << endl;
1408 // now get the cursors x position
1409 float x = getCursorX(pit, row, pos, last, boundary);
1412 if (old_row != row) {
1413 x = getCursorX(pit, old_row, pos, last, boundary);
1417 /* We take out this for the time being because 1) the redraw code is not
1418 prepared to this yet and 2) because some good policy has yet to be decided
1419 while editting: for instance how to act on rows being created/deleted
1423 //if the cursor is in a visible row, anchor to it
1425 if (topy < y && y < topy + bv()->workHeight())
1431 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1432 pos_type pos, pos_type last, bool boundary) const
1434 pos_type cursor_vpos = 0;
1436 double fill_separator;
1438 double fill_label_hfill;
1439 // This call HAS to be here because of the BidiTables!!!
1440 prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
1443 pos_type const rit_pos = rit->pos();
1446 cursor_vpos = rit_pos;
1447 else if (pos > last && !boundary)
1448 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1449 ? rit_pos : last + 1;
1450 else if (pos > rit_pos && (pos > last || boundary))
1451 // Place cursor after char at (logical) position pos - 1
1452 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1453 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1455 // Place cursor before char at (logical) position pos
1456 cursor_vpos = (bidi_level(pos) % 2 == 0)
1457 ? log2vis(pos) : log2vis(pos) + 1;
1459 pos_type body_pos = pit->beginningOfBody();
1461 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1464 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1465 pos_type pos = vis2log(vpos);
1466 if (body_pos > 0 && pos == body_pos - 1) {
1467 x += fill_label_hfill +
1468 font_metrics::width(
1469 pit->layout()->labelsep, getLabelFont(pit));
1470 if (pit->isLineSeparator(body_pos - 1))
1471 x -= singleWidth(pit, body_pos - 1);
1474 if (hfillExpansion(*pit, rit, pos)) {
1475 x += singleWidth(pit, pos);
1476 if (pos >= body_pos)
1479 x += fill_label_hfill;
1480 } else if (pit->isSeparator(pos)) {
1481 x += singleWidth(pit, pos);
1482 if (pos >= body_pos)
1483 x += fill_separator;
1485 x += singleWidth(pit, pos);
1491 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1492 pos_type pos, bool setfont, bool boundary)
1494 setCursor(cursor, pit, pos, boundary);
1500 void LyXText::setCurrentFont()
1502 pos_type pos = cursor.pos();
1503 ParagraphList::iterator pit = cursor.par();
1505 if (cursor.boundary() && pos > 0)
1509 if (pos == pit->size())
1511 else // potentional bug... BUG (Lgb)
1512 if (pit->isSeparator(pos)) {
1513 if (pos > cursorRow()->pos() &&
1514 bidi_level(pos) % 2 ==
1515 bidi_level(pos - 1) % 2)
1517 else if (pos + 1 < pit->size())
1522 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1523 real_current_font = getFont(pit, pos);
1525 if (cursor.pos() == pit->size() &&
1526 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1527 !cursor.boundary()) {
1528 Language const * lang =
1529 pit->getParLanguage(bv()->buffer()->params);
1530 current_font.setLanguage(lang);
1531 current_font.setNumber(LyXFont::OFF);
1532 real_current_font.setLanguage(lang);
1533 real_current_font.setNumber(LyXFont::OFF);
1538 // returns the column near the specified x-coordinate of the row
1539 // x is set to the real beginning of this column
1540 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1541 RowList::iterator rit, int & x, bool & boundary) const
1544 double fill_separator;
1546 double fill_label_hfill;
1548 prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1550 pos_type vc = rit->pos();
1551 pos_type last = lastPrintablePos(*pit, rit);
1553 LyXLayout_ptr const & layout = pit->layout();
1555 bool left_side = false;
1557 pos_type body_pos = pit->beginningOfBody();
1558 double last_tmpx = tmpx;
1561 (body_pos - 1 > last ||
1562 !pit->isLineSeparator(body_pos - 1)))
1565 // check for empty row
1571 while (vc <= last && tmpx <= x) {
1574 if (body_pos > 0 && c == body_pos - 1) {
1575 tmpx += fill_label_hfill +
1576 font_metrics::width(layout->labelsep, getLabelFont(pit));
1577 if (pit->isLineSeparator(body_pos - 1))
1578 tmpx -= singleWidth(pit, body_pos - 1);
1581 if (hfillExpansion(*pit, rit, c)) {
1582 tmpx += singleWidth(pit, c);
1586 tmpx += fill_label_hfill;
1587 } else if (pit->isSeparator(c)) {
1588 tmpx += singleWidth(pit, c);
1590 tmpx += fill_separator;
1592 tmpx += singleWidth(pit, c);
1597 if ((tmpx + last_tmpx) / 2 > x) {
1602 if (vc > last + 1) // This shouldn't happen.
1606 // This (rtl_support test) is not needed, but gives
1607 // some speedup if rtl_support == false
1608 bool const lastrow = lyxrc.rtl_support
1609 && boost::next(rit) == pit->rows.end();
1611 // If lastrow is false, we don't need to compute
1612 // the value of rtl.
1613 bool const rtl = (lastrow)
1614 ? pit->isRightToLeftPar(bv()->buffer()->params)
1617 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1618 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1620 else if (vc == rit->pos()) {
1622 if (bidi_level(c) % 2 == 1)
1625 c = vis2log(vc - 1);
1626 bool const rtl = (bidi_level(c) % 2 == 1);
1627 if (left_side == rtl) {
1629 boundary = isBoundary(bv()->buffer(), *pit, c);
1633 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1634 if (bidi_level(last) % 2 == 0)
1635 tmpx -= singleWidth(pit, last);
1637 tmpx += singleWidth(pit, last);
1647 void LyXText::setCursorFromCoordinates(int x, int y)
1649 //LyXCursor old_cursor = cursor;
1650 setCursorFromCoordinates(cursor, x, y);
1652 #warning DEPM disabled, otherwise crash when entering new table
1653 //deleteEmptyParagraphMechanism(old_cursor);
1660 * return true if the cursor given is at the end of a row,
1661 * and the next row is filled by an inset that spans an entire
1664 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1666 RowList::iterator row = lt.getRow(cur);
1667 RowList::iterator next = boost::next(row);
1669 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1672 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1675 InsetOld const * inset = cur.par()->getInset(cur.pos());
1676 if (inset->needFullRow() || inset->display())
1684 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1686 // Get the row first.
1687 ParagraphList::iterator pit;
1688 RowList::iterator row = getRowNearY(y, pit);
1690 pos_type const column = getColumnNearX(pit, row, x, bound);
1692 cur.pos(row->pos() + column);
1694 cur.y(y + row->baseline());
1696 // if (beforeFullRowInset(*this, cur)) {
1697 // pos_type const last = lastPrintablePos(*this, pit, row);
1698 // RowList::iterator next_row = nextRow(row);
1699 // cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
1700 // cur.iy(y + row->height() + next_row->baseline());
1705 cur.boundary(bound);
1709 void LyXText::cursorLeft(bool internal)
1711 if (cursor.pos() > 0) {
1712 bool boundary = cursor.boundary();
1713 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1714 if (!internal && !boundary &&
1715 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1716 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1717 } else if (cursor.par() != ownerParagraphs().begin()) {
1718 // steps into the paragraph above
1719 ParagraphList::iterator pit = boost::prior(cursor.par());
1720 setCursor(pit, pit->size());
1725 void LyXText::cursorRight(bool internal)
1727 bool const at_end = (cursor.pos() == cursor.par()->size());
1728 bool const at_newline = !at_end &&
1729 cursor.par()->isNewline(cursor.pos());
1731 if (!internal && cursor.boundary() && !at_newline)
1732 setCursor(cursor.par(), cursor.pos(), true, false);
1734 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1736 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1737 setCursor(cursor.par(), cursor.pos(), true, true);
1738 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1739 setCursor(boost::next(cursor.par()), 0);
1743 void LyXText::cursorUp(bool selecting)
1746 int x = cursor.x_fix();
1747 int y = cursor.y() - cursorRow()->baseline() - 1;
1748 setCursorFromCoordinates(x, y);
1751 int y1 = cursor.iy() - topy;
1754 InsetOld * inset_hit = checkInsetHit(x, y1);
1755 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1756 inset_hit->localDispatch(
1757 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1761 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1762 cursorRow()->baseline() << endl;
1763 setCursorFromCoordinates(cursor.x_fix(),
1764 cursor.y() - cursorRow()->baseline() - 1);
1769 void LyXText::cursorDown(bool selecting)
1772 int x = cursor.x_fix();
1773 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1774 setCursorFromCoordinates(x, y);
1775 if (!selecting && cursorRow() == cursorIRow()) {
1777 int y1 = cursor.iy() - topy;
1780 InsetOld * inset_hit = checkInsetHit(x, y1);
1781 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1782 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1783 inset_hit->localDispatch(cmd);
1787 setCursorFromCoordinates(cursor.x_fix(),
1788 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1793 void LyXText::cursorUpParagraph()
1795 if (cursor.pos() > 0)
1796 setCursor(cursor.par(), 0);
1797 else if (cursor.par() != ownerParagraphs().begin())
1798 setCursor(boost::prior(cursor.par()), 0);
1802 void LyXText::cursorDownParagraph()
1804 ParagraphList::iterator par = cursor.par();
1805 ParagraphList::iterator next_par = boost::next(par);
1807 if (next_par != ownerParagraphs().end())
1808 setCursor(next_par, 0);
1810 setCursor(par, par->size());
1814 // fix the cursor `cur' after a characters has been deleted at `where'
1815 // position. Called by deleteEmptyParagraphMechanism
1816 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1818 // if cursor is not in the paragraph where the delete occured,
1820 if (cur.par() != where.par())
1823 // if cursor position is after the place where the delete occured,
1825 if (cur.pos() > where.pos())
1826 cur.pos(cur.pos()-1);
1828 // check also if we don't want to set the cursor on a spot behind the
1829 // pagragraph because we erased the last character.
1830 if (cur.pos() > cur.par()->size())
1831 cur.pos(cur.par()->size());
1833 // recompute row et al. for this cursor
1834 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1838 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1840 // Would be wrong to delete anything if we have a selection.
1841 if (selection.set())
1844 // We allow all kinds of "mumbo-jumbo" when freespacing.
1845 if (old_cursor.par()->isFreeSpacing())
1848 /* Ok I'll put some comments here about what is missing.
1849 I have fixed BackSpace (and thus Delete) to not delete
1850 double-spaces automagically. I have also changed Cut,
1851 Copy and Paste to hopefully do some sensible things.
1852 There are still some small problems that can lead to
1853 double spaces stored in the document file or space at
1854 the beginning of paragraphs. This happens if you have
1855 the cursor betwenn to spaces and then save. Or if you
1856 cut and paste and the selection have a space at the
1857 beginning and then save right after the paste. I am
1858 sure none of these are very hard to fix, but I will
1859 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1860 that I can get some feedback. (Lgb)
1863 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1864 // delete the LineSeparator.
1867 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1868 // delete the LineSeparator.
1871 // If the pos around the old_cursor were spaces, delete one of them.
1872 if (old_cursor.par() != cursor.par()
1873 || old_cursor.pos() != cursor.pos()) {
1875 // Only if the cursor has really moved
1876 if (old_cursor.pos() > 0
1877 && old_cursor.pos() < old_cursor.par()->size()
1878 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1879 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1880 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1881 redoParagraph(old_cursor.par());
1885 #ifdef WITH_WARNINGS
1886 #warning This will not work anymore when we have multiple views of the same buffer
1887 // In this case, we will have to correct also the cursors held by
1888 // other bufferviews. It will probably be easier to do that in a more
1889 // automated way in LyXCursor code. (JMarc 26/09/2001)
1891 // correct all cursors held by the LyXText
1892 fixCursorAfterDelete(cursor, old_cursor);
1893 fixCursorAfterDelete(selection.cursor, old_cursor);
1894 fixCursorAfterDelete(selection.start, old_cursor);
1895 fixCursorAfterDelete(selection.end, old_cursor);
1900 // don't delete anything if this is the ONLY paragraph!
1901 if (ownerParagraphs().size() == 1)
1904 // Do not delete empty paragraphs with keepempty set.
1905 if (old_cursor.par()->allowEmpty())
1908 // only do our magic if we changed paragraph
1909 if (old_cursor.par() == cursor.par())
1912 // record if we have deleted a paragraph
1913 // we can't possibly have deleted a paragraph before this point
1914 bool deleted = false;
1916 if (old_cursor.par()->empty() ||
1917 (old_cursor.par()->size() == 1 &&
1918 old_cursor.par()->isLineSeparator(0))) {
1919 // ok, we will delete anything
1920 LyXCursor tmpcursor;
1924 bool selection_position_was_oldcursor_position = (
1925 selection.cursor.par() == old_cursor.par()
1926 && selection.cursor.pos() == old_cursor.pos());
1929 cursor = old_cursor; // that undo can restore the right cursor position
1931 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1932 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1935 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1939 ownerParagraphs().erase(old_cursor.par());
1943 setCursorIntern(cursor.par(), cursor.pos());
1945 if (selection_position_was_oldcursor_position) {
1946 // correct selection
1947 selection.cursor = cursor;
1951 if (old_cursor.par()->stripLeadingSpaces()) {
1952 redoParagraph(old_cursor.par());
1954 setCursorIntern(cursor.par(), cursor.pos());
1955 selection.cursor = cursor;
1962 ParagraphList & LyXText::ownerParagraphs() const
1965 return inset_owner->paragraphs;
1967 return bv_owner->buffer()->paragraphs;
1971 bool LyXText::isInInset() const
1973 // Sub-level has non-null bv owner and non-null inset owner.
1974 return inset_owner != 0 && bv_owner != 0;
1978 int defaultRowHeight()
1980 LyXFont const font(LyXFont::ALL_SANE);
1981 return int(font_metrics::maxAscent(font)
1982 + font_metrics::maxDescent(font) * 1.5);