3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
30 #include "CutAndPaste.h"
32 #include "errorlist.h"
34 #include "FloatList.h"
35 #include "funcrequest.h"
40 #include "lyxrow_funcs.h"
41 #include "metricsinfo.h"
42 #include "paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
48 #include "frontends/font_metrics.h"
49 #include "frontends/LyXView.h"
51 #include "insets/insetbibitem.h"
52 #include "insets/insetenv.h"
53 #include "insets/insetfloat.h"
54 #include "insets/insetwrap.h"
56 #include "support/lstrings.h"
57 #include "support/textutils.h"
58 #include "support/tostr.h"
59 #include "support/std_sstream.h"
61 #include <boost/tuple/tuple.hpp>
64 using lyx::paroffset_type;
65 using lyx::support::bformat;
68 using std::ostringstream;
72 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
73 ParagraphList & paragraphs)
74 : height(0), width(0), anchor_y_(0),
75 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
76 in_inset_(ininset), paragraphs_(¶graphs)
81 void LyXText::init(BufferView * bview)
85 ParagraphList::iterator const beg = ownerParagraphs().begin();
86 ParagraphList::iterator const end = ownerParagraphs().end();
87 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
95 current_font = getFont(beg, 0);
97 redoParagraphs(beg, end);
98 setCursorIntern(0, 0);
99 selection.cursor = cursor;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as Paragraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
112 BOOST_ASSERT(pos >= 0);
114 LyXLayout_ptr const & layout = pit->layout();
116 BufferParams const & params = bv()->buffer()->params();
118 // We specialize the 95% common case:
119 if (!pit->getDepth()) {
120 if (layout->labeltype == LABEL_MANUAL
121 && pos < pit->beginningOfBody()) {
123 LyXFont f = pit->getFontSettings(params, pos);
125 pit->inInset()->getDrawFont(f);
126 return f.realize(layout->reslabelfont);
128 LyXFont f = pit->getFontSettings(params, pos);
130 pit->inInset()->getDrawFont(f);
131 return f.realize(layout->resfont);
135 // The uncommon case need not be optimized as much
139 if (pos < pit->beginningOfBody()) {
141 layoutfont = layout->labelfont;
144 layoutfont = layout->font;
147 LyXFont tmpfont = pit->getFontSettings(params, pos);
148 tmpfont.realize(layoutfont);
151 pit->inInset()->getDrawFont(tmpfont);
153 // Realize with the fonts of lesser depth.
154 tmpfont.realize(outerFont(pit, ownerParagraphs()));
155 tmpfont.realize(defaultfont_);
161 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
163 LyXLayout_ptr const & layout = pit->layout();
165 if (!pit->getDepth())
166 return layout->resfont;
168 LyXFont font = layout->font;
169 // Realize with the fonts of lesser depth.
170 font.realize(outerFont(pit, ownerParagraphs()));
171 font.realize(defaultfont_);
177 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
179 LyXLayout_ptr const & layout = pit->layout();
181 if (!pit->getDepth())
182 return layout->reslabelfont;
184 LyXFont font = layout->labelfont;
185 // Realize with the fonts of lesser depth.
186 font.realize(outerFont(pit, ownerParagraphs()));
187 font.realize(defaultfont_);
193 void LyXText::setCharFont(ParagraphList::iterator pit,
194 pos_type pos, LyXFont const & fnt,
197 BufferParams const & params = bv()->buffer()->params();
198 LyXFont font = getFont(pit, pos);
199 font.update(fnt, params.language, toggleall);
200 // Let the insets convert their font
201 if (pit->isInset(pos)) {
202 InsetOld * inset = pit->getInset(pos);
203 if (isEditableInset(inset)) {
204 static_cast<UpdatableInset *>(inset)
205 ->setFont(bv(), fnt, toggleall, true);
209 // Plug through to version below:
210 setCharFont(pit, pos, font);
214 void LyXText::setCharFont(
215 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
218 LyXLayout_ptr const & layout = pit->layout();
220 // Get concrete layout font to reduce against
223 if (pos < pit->beginningOfBody())
224 layoutfont = layout->labelfont;
226 layoutfont = layout->font;
228 // Realize against environment font information
229 if (pit->getDepth()) {
230 ParagraphList::iterator tp = pit;
231 while (!layoutfont.resolved() &&
232 tp != ownerParagraphs().end() &&
234 tp = outerHook(tp, ownerParagraphs());
235 if (tp != ownerParagraphs().end())
236 layoutfont.realize(tp->layout()->font);
240 layoutfont.realize(defaultfont_);
242 // Now, reduce font against full layout font
243 font.reduce(layoutfont);
245 pit->setFont(pos, font);
249 InsetOld * LyXText::getInset() const
251 ParagraphList::iterator pit = cursorPar();
252 pos_type const pos = cursor.pos();
254 if (pos < pit->size() && pit->isInset(pos)) {
255 return pit->getInset(pos);
261 void LyXText::toggleInset()
263 InsetOld * inset = getInset();
264 // is there an editable inset at cursor position?
265 if (!isEditableInset(inset)) {
266 // No, try to see if we are inside a collapsable inset
267 if (inset_owner && inset_owner->owner()
268 && inset_owner->owner()->isOpen()) {
269 bv()->unlockInset(inset_owner->owner());
270 inset_owner->owner()->close(bv());
271 bv()->getLyXText()->cursorRight(bv());
275 //bv()->owner()->message(inset->editMessage());
277 // do we want to keep this?? (JMarc)
278 if (!isHighlyEditableInset(inset))
279 recUndo(cursor.par());
286 bv()->updateInset(inset);
290 /* used in setlayout */
291 // Asger is not sure we want to do this...
292 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
295 LyXLayout_ptr const & layout = par.layout();
296 pos_type const psize = par.size();
299 for (pos_type pos = 0; pos < psize; ++pos) {
300 if (pos < par.beginningOfBody())
301 layoutfont = layout->labelfont;
303 layoutfont = layout->font;
305 LyXFont tmpfont = par.getFontSettings(params, pos);
306 tmpfont.reduce(layoutfont);
307 par.setFont(pos, tmpfont);
312 ParagraphList::iterator
313 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
314 LyXCursor & send_cur,
315 string const & layout)
317 ParagraphList::iterator endpit = boost::next(getPar(send_cur));
318 ParagraphList::iterator undoendpit = endpit;
319 ParagraphList::iterator pars_end = ownerParagraphs().end();
321 if (endpit != pars_end && endpit->getDepth()) {
322 while (endpit != pars_end && endpit->getDepth()) {
326 } else if (endpit != pars_end) {
327 // because of parindents etc.
331 recUndo(sstart_cur.par(), parOffset(undoendpit) - 1);
333 // ok we have a selection. This is always between sstart_cur
334 // and sel_end cursor
336 ParagraphList::iterator pit = getPar(sstart_cur);
337 ParagraphList::iterator epit = boost::next(getPar(send_cur));
339 BufferParams const & bufparams = bv()->buffer()->params();
340 LyXLayout_ptr const & lyxlayout =
341 bufparams.getLyXTextClass()[layout];
344 pit->applyLayout(lyxlayout);
345 makeFontEntriesLayoutSpecific(bufparams, *pit);
346 pit->params().spaceTop(lyxlayout->fill_top ?
347 VSpace(VSpace::VFILL)
348 : VSpace(VSpace::NONE));
349 pit->params().spaceBottom(lyxlayout->fill_bottom ?
350 VSpace(VSpace::VFILL)
351 : VSpace(VSpace::NONE));
352 if (lyxlayout->margintype == MARGIN_MANUAL)
353 pit->setLabelWidthString(lyxlayout->labelstring());
354 cur.par(std::distance(ownerParagraphs().begin(), pit));
356 } while (pit != epit);
362 // set layout over selection and make a total rebreak of those paragraphs
363 void LyXText::setLayout(string const & layout)
365 LyXCursor tmpcursor = cursor; // store the current cursor
367 // if there is no selection just set the layout
368 // of the current paragraph
369 if (!selection.set()) {
370 selection.start = cursor; // dummy selection
371 selection.end = cursor;
374 // special handling of new environment insets
375 BufferParams const & params = bv()->buffer()->params();
376 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
377 if (lyxlayout->is_environment) {
378 // move everything in a new environment inset
379 lyxerr << "setting layout " << layout << endl;
380 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
381 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
382 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
383 InsetOld * inset = new InsetEnvironment(params, layout);
384 if (bv()->insertInset(inset)) {
386 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
393 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
394 selection.end, layout);
395 redoParagraphs(getPar(selection.start), endpit);
397 // we have to reset the selection, because the
398 // geometry could have changed
399 setCursor(selection.start.par(), selection.start.pos(), false);
400 selection.cursor = cursor;
401 setCursor(selection.end.par(), selection.end.pos(), false);
405 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
409 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
411 ParagraphList::iterator pit = cursorPar();
412 ParagraphList::iterator end = cursorPar();
413 ParagraphList::iterator start = pit;
415 if (selection.set()) {
416 pit = getPar(selection.start);
417 end = getPar(selection.end);
421 ParagraphList::iterator pastend = boost::next(end);
424 recUndo(parOffset(start), parOffset(end));
426 bool changed = false;
428 int prev_after_depth = 0;
429 #warning parlist ... could be nicer ?
430 if (start != ownerParagraphs().begin()) {
431 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
435 int const depth = pit->params().depth();
436 if (type == bv_funcs::INC_DEPTH) {
437 if (depth < prev_after_depth
438 && pit->layout()->labeltype != LABEL_BIBLIO) {
441 pit->params().depth(depth + 1);
446 pit->params().depth(depth - 1);
449 prev_after_depth = pit->getMaxDepthAfter();
461 redoParagraphs(start, pastend);
463 // We need to actually move the text->cursor. I don't
464 // understand why ...
465 LyXCursor tmpcursor = cursor;
467 // we have to reset the visual selection because the
468 // geometry could have changed
469 if (selection.set()) {
470 setCursor(selection.start.par(), selection.start.pos());
471 selection.cursor = cursor;
472 setCursor(selection.end.par(), selection.end.pos());
475 // this handles the counter labels, and also fixes up
476 // depth values for follow-on (child) paragraphs
480 setCursor(tmpcursor.par(), tmpcursor.pos());
486 // set font over selection and make a total rebreak of those paragraphs
487 void LyXText::setFont(LyXFont const & font, bool toggleall)
489 // if there is no selection just set the current_font
490 if (!selection.set()) {
491 // Determine basis font
493 if (cursor.pos() < cursorPar()->beginningOfBody()) {
494 layoutfont = getLabelFont(cursorPar());
496 layoutfont = getLayoutFont(cursorPar());
498 // Update current font
499 real_current_font.update(font,
500 bv()->buffer()->params().language,
503 // Reduce to implicit settings
504 current_font = real_current_font;
505 current_font.reduce(layoutfont);
506 // And resolve it completely
507 real_current_font.realize(layoutfont);
512 LyXCursor tmpcursor = cursor; // store the current cursor
514 // ok we have a selection. This is always between sel_start_cursor
515 // and sel_end cursor
517 recUndo(selection.start.par(), selection.end.par());
519 cursor = selection.start;
520 while (cursor.par() != selection.end.par() ||
521 cursor.pos() < selection.end.pos())
523 if (cursor.pos() < cursorPar()->size()) {
524 // an open footnote should behave like a closed one
525 setCharFont(cursorPar(), cursor.pos(), font, toggleall);
526 cursor.pos(cursor.pos() + 1);
529 cursor.par(cursor.par() + 1);
534 redoParagraph(getPar(selection.start));
536 // we have to reset the selection, because the
537 // geometry could have changed, but we keep
538 // it for user convenience
539 setCursor(selection.start.par(), selection.start.pos());
540 selection.cursor = cursor;
541 setCursor(selection.end.par(), selection.end.pos());
543 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
544 tmpcursor.boundary());
548 int LyXText::redoParagraphInternal(ParagraphList::iterator pit)
550 RowList::iterator rit = pit->rows.begin();
551 RowList::iterator end = pit->rows.end();
553 // remove rows of paragraph, keep track of height changes
554 for (int i = 0; rit != end; ++rit, ++i)
555 height -= rit->height();
559 InsetList::iterator ii = pit->insetlist.begin();
560 InsetList::iterator iend = pit->insetlist.end();
561 for (; ii != iend; ++ii) {
563 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
564 ii->inset->metrics(mi, dim);
567 // rebreak the paragraph
568 for (pos_type z = 0; z < pit->size() + 1; ) {
570 z = rowBreakPoint(pit, row) + 1;
572 pit->rows.push_back(row);
576 // set height and fill and width of rows
577 int const ww = workWidth();
579 for (rit = pit->rows.begin(); rit != end; ++rit) {
580 int const f = fill(pit, rit, ww);
581 int const w = ww - f;
582 par_width = std::max(par_width, w);
585 prepareToPrint(pit, rit);
586 setHeightOfRow(pit, rit);
587 rit->y_offset(pit->height);
588 pit->height += rit->height();
590 height += pit->height;
592 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
597 int LyXText::redoParagraphs(ParagraphList::iterator start,
598 ParagraphList::iterator end)
601 for ( ; start != end; ++start) {
602 int par_width = redoParagraphInternal(start);
603 pars_width = std::max(par_width, pars_width);
605 updateRowPositions();
610 void LyXText::redoParagraph(ParagraphList::iterator pit)
612 redoParagraphInternal(pit);
613 updateRowPositions();
617 void LyXText::fullRebreak()
619 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
621 selection.cursor = cursor;
625 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
627 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
628 // << " workWidth: " << workWidth() << endl;
629 //BOOST_ASSERT(mi.base.textwidth);
636 width = redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
639 dim.asc = firstRow()->ascent_of_text();
640 dim.des = height - dim.asc;
641 dim.wid = std::max(mi.base.textwidth, int(width));
645 // important for the screen
648 // the cursor set functions have a special mechanism. When they
649 // realize, that you left an empty paragraph, they will delete it.
650 // They also delete the corresponding row
652 // need the selection cursor:
653 void LyXText::setSelection()
655 TextCursor::setSelection();
660 void LyXText::clearSelection()
662 TextCursor::clearSelection();
664 // reset this in the bv_owner!
665 if (bv_owner && bv_owner->text)
666 bv_owner->text->xsel_cache.set(false);
670 void LyXText::cursorHome()
672 setCursor(cursorPar(), cursorRow()->pos());
676 void LyXText::cursorEnd()
678 setCursor(cursorPar(), cursorRow()->end() - 1);
682 void LyXText::cursorTop()
684 setCursor(ownerParagraphs().begin(), 0);
688 void LyXText::cursorBottom()
690 ParagraphList::iterator lastpit =
691 boost::prior(ownerParagraphs().end());
692 setCursor(lastpit, lastpit->size());
696 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
698 // If the mask is completely neutral, tell user
699 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
700 // Could only happen with user style
701 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
705 // Try implicit word selection
706 // If there is a change in the language the implicit word selection
708 LyXCursor resetCursor = cursor;
709 bool implicitSelection = (font.language() == ignore_language
710 && font.number() == LyXFont::IGNORE)
711 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
714 setFont(font, toggleall);
716 // Implicit selections are cleared afterwards
717 //and cursor is set to the original position.
718 if (implicitSelection) {
720 cursor = resetCursor;
721 setCursor(cursorPar(), cursor.pos());
722 selection.cursor = cursor;
727 string LyXText::getStringToIndex()
729 // Try implicit word selection
730 // If there is a change in the language the implicit word selection
732 LyXCursor const reset_cursor = cursor;
733 bool const implicitSelection =
734 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
737 if (!selection.set())
738 bv()->owner()->message(_("Nothing to index!"));
739 else if (selection.start.par() != selection.end.par())
740 bv()->owner()->message(_("Cannot index more than one paragraph!"));
742 idxstring = selectionAsString(*bv()->buffer(), false);
744 // Reset cursors to their original position.
745 cursor = reset_cursor;
746 setCursor(cursorPar(), cursor.pos());
747 selection.cursor = cursor;
749 // Clear the implicit selection.
750 if (implicitSelection)
757 // the DTP switches for paragraphs. LyX will store them in the first
758 // physical paragraph. When a paragraph is broken, the top settings rest,
759 // the bottom settings are given to the new one. So I can make sure,
760 // they do not duplicate themself and you cannnot make dirty things with
763 void LyXText::setParagraph(bool line_top, bool line_bottom,
764 bool pagebreak_top, bool pagebreak_bottom,
765 VSpace const & space_top,
766 VSpace const & space_bottom,
767 Spacing const & spacing,
769 string const & labelwidthstring,
772 LyXCursor tmpcursor = cursor;
773 if (!selection.set()) {
774 selection.start = cursor;
775 selection.end = cursor;
778 // make sure that the depth behind the selection are restored, too
779 ParagraphList::iterator endpit = boost::next(getPar(selection.end));
780 ParagraphList::iterator undoendpit = endpit;
781 ParagraphList::iterator pars_end = ownerParagraphs().end();
783 if (endpit != pars_end && endpit->getDepth()) {
784 while (endpit != pars_end && endpit->getDepth()) {
788 } else if (endpit != pars_end) {
789 // because of parindents etc.
793 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
796 int tmppit = selection.end.par();
798 while (tmppit != selection.start.par() - 1) {
799 setCursor(tmppit, 0);
801 ParagraphList::iterator const pit = cursorPar();
802 ParagraphParameters & params = pit->params();
804 params.lineTop(line_top);
805 params.lineBottom(line_bottom);
806 params.pagebreakTop(pagebreak_top);
807 params.pagebreakBottom(pagebreak_bottom);
808 params.spaceTop(space_top);
809 params.spaceBottom(space_bottom);
810 params.spacing(spacing);
811 // does the layout allow the new alignment?
812 LyXLayout_ptr const & layout = pit->layout();
814 if (align == LYX_ALIGN_LAYOUT)
815 align = layout->align;
816 if (align & layout->alignpossible) {
817 if (align == layout->align)
818 params.align(LYX_ALIGN_LAYOUT);
822 pit->setLabelWidthString(labelwidthstring);
823 params.noindent(noindent);
827 redoParagraphs(getPar(selection.start), endpit);
830 setCursor(selection.start.par(), selection.start.pos());
831 selection.cursor = cursor;
832 setCursor(selection.end.par(), selection.end.pos());
834 setCursor(tmpcursor.par(), tmpcursor.pos());
836 bv()->updateInset(inset_owner);
842 string expandLabel(LyXTextClass const & textclass,
843 LyXLayout_ptr const & layout, bool appendix)
845 string fmt = appendix ?
846 layout->labelstring_appendix() : layout->labelstring();
848 // handle 'inherited level parts' in 'fmt',
849 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
850 size_t const i = fmt.find('@', 0);
851 if (i != string::npos) {
852 size_t const j = fmt.find('@', i + 1);
853 if (j != string::npos) {
854 string parent(fmt, i + 1, j - i - 1);
855 string label = expandLabel(textclass, textclass[parent], appendix);
856 fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
860 return textclass.counters().counterLabel(fmt);
864 void incrementItemDepth(ParagraphList::iterator pit,
865 ParagraphList::iterator first_pit)
867 int const cur_labeltype = pit->layout()->labeltype;
869 if (cur_labeltype != LABEL_ENUMERATE &&
870 cur_labeltype != LABEL_ITEMIZE)
873 int const cur_depth = pit->getDepth();
875 ParagraphList::iterator prev_pit = boost::prior(pit);
877 int const prev_depth = prev_pit->getDepth();
878 int const prev_labeltype = prev_pit->layout()->labeltype;
879 if (prev_depth == 0 && cur_depth > 0) {
880 if (prev_labeltype == cur_labeltype) {
881 pit->itemdepth = prev_pit->itemdepth + 1;
884 } else if (prev_depth < cur_depth) {
885 if (prev_labeltype == cur_labeltype) {
886 pit->itemdepth = prev_pit->itemdepth + 1;
889 } else if (prev_depth == cur_depth) {
890 if (prev_labeltype == cur_labeltype) {
891 pit->itemdepth = prev_pit->itemdepth;
895 if (prev_pit == first_pit)
903 void resetEnumCounterIfNeeded(ParagraphList::iterator pit,
904 ParagraphList::iterator firstpit,
910 int const cur_depth = pit->getDepth();
911 ParagraphList::iterator prev_pit = boost::prior(pit);
913 int const prev_depth = prev_pit->getDepth();
914 int const prev_labeltype = prev_pit->layout()->labeltype;
915 if (prev_depth <= cur_depth) {
916 if (prev_labeltype != LABEL_ENUMERATE) {
917 switch (pit->itemdepth) {
919 counters.reset("enumi");
921 counters.reset("enumii");
923 counters.reset("enumiii");
925 counters.reset("enumiv");
931 if (prev_pit == firstpit)
941 // set the counter of a paragraph. This includes the labels
942 void LyXText::setCounter(Buffer const & buf, ParagraphList::iterator pit)
944 BufferParams const & bufparams = buf.params();
945 LyXTextClass const & textclass = bufparams.getLyXTextClass();
946 LyXLayout_ptr const & layout = pit->layout();
947 ParagraphList::iterator first_pit = ownerParagraphs().begin();
948 Counters & counters = textclass.counters();
953 if (pit == first_pit) {
954 pit->params().appendix(pit->params().startOfAppendix());
956 pit->params().appendix(boost::prior(pit)->params().appendix());
957 if (!pit->params().appendix() &&
958 pit->params().startOfAppendix()) {
959 pit->params().appendix(true);
960 textclass.counters().reset();
963 // Maybe we have to increment the item depth.
964 incrementItemDepth(pit, first_pit);
967 // erase what was there before
968 pit->params().labelString(string());
970 if (layout->margintype == MARGIN_MANUAL) {
971 if (pit->params().labelWidthString().empty())
972 pit->setLabelWidthString(layout->labelstring());
974 pit->setLabelWidthString(string());
977 // is it a layout that has an automatic label?
978 if (layout->labeltype == LABEL_COUNTER) {
979 BufferParams const & bufparams = buf.params();
980 LyXTextClass const & textclass = bufparams.getLyXTextClass();
981 counters.step(layout->counter);
982 string label = expandLabel(textclass, layout, pit->params().appendix());
983 pit->params().labelString(label);
984 } else if (layout->labeltype == LABEL_ITEMIZE) {
985 // At some point of time we should do something more
986 // clever here, like:
987 // pit->params().labelString(
988 // bufparams.user_defined_bullet(pit->itemdepth).getText());
989 // for now, use a simple hardcoded label
991 switch (pit->itemdepth) {
1006 pit->params().labelString(itemlabel);
1007 } else if (layout->labeltype == LABEL_ENUMERATE) {
1008 // Maybe we have to reset the enumeration counter.
1009 resetEnumCounterIfNeeded(pit, first_pit, counters);
1012 // Yes I know this is a really, really! bad solution
1014 string enumcounter = "enum";
1016 switch (pit->itemdepth) {
1025 enumcounter += "iv";
1028 // not a valid enumdepth...
1032 counters.step(enumcounter);
1034 pit->params().labelString(counters.enumLabel(enumcounter));
1035 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1036 counters.step("bibitem");
1037 int number = counters.value("bibitem");
1038 if (pit->bibitem()) {
1039 pit->bibitem()->setCounter(number);
1040 pit->params().labelString(layout->labelstring());
1042 // In biblio should't be following counters but...
1044 string s = buf.B_(layout->labelstring());
1046 // the caption hack:
1047 if (layout->labeltype == LABEL_SENSITIVE) {
1048 ParagraphList::iterator end = ownerParagraphs().end();
1049 ParagraphList::iterator tmppit = pit;
1052 while (tmppit != end && tmppit->inInset()
1053 // the single '=' is intended below
1054 && (in = tmppit->inInset()->owner()))
1056 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1057 in->lyxCode() == InsetOld::WRAP_CODE) {
1061 Paragraph const * owner = &ownerPar(buf, in);
1063 for ( ; tmppit != end; ++tmppit)
1064 if (&*tmppit == owner)
1072 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1073 type = static_cast<InsetFloat*>(in)->params().type;
1074 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1075 type = static_cast<InsetWrap*>(in)->params().type;
1077 BOOST_ASSERT(false);
1079 Floating const & fl = textclass.floats().getType(type);
1081 counters.step(fl.type());
1083 // Doesn't work... yet.
1084 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
1086 // par->SetLayout(0);
1087 // s = layout->labelstring;
1088 s = _("Senseless: ");
1091 pit->params().labelString(s);
1097 // Updates all counters. Paragraphs with changed label string will be rebroken
1098 void LyXText::updateCounters()
1101 bv()->buffer()->params().getLyXTextClass().counters().reset();
1103 ParagraphList::iterator beg = ownerParagraphs().begin();
1104 ParagraphList::iterator end = ownerParagraphs().end();
1105 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1106 string const oldLabel = pit->params().labelString();
1108 size_t maxdepth = 0;
1110 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1112 if (pit->params().depth() > maxdepth)
1113 pit->params().depth(maxdepth);
1115 // setCounter can potentially change the labelString.
1116 setCounter(*bv()->buffer(), pit);
1118 string const & newLabel = pit->params().labelString();
1120 if (oldLabel != newLabel)
1126 void LyXText::insertInset(InsetOld * inset)
1128 if (!cursorPar()->insetAllowed(inset->lyxCode()))
1130 recUndo(cursor.par());
1132 cursorPar()->insertInset(cursor.pos(), inset);
1133 // Just to rebreak and refresh correctly.
1134 // The character will not be inserted a second time
1135 insertChar(Paragraph::META_INSET);
1136 // If we enter a highly editable inset the cursor should be before
1137 // the inset. After an Undo LyX tries to call inset->edit(...)
1138 // and fails if the cursor is behind the inset and getInset
1139 // does not return the inset!
1140 if (isHighlyEditableInset(inset))
1146 void LyXText::cutSelection(bool doclear, bool realcut)
1148 // Stuff what we got on the clipboard. Even if there is no selection.
1150 // There is a problem with having the stuffing here in that the
1151 // larger the selection the slower LyX will get. This can be
1152 // solved by running the line below only when the selection has
1153 // finished. The solution used currently just works, to make it
1154 // faster we need to be more clever and probably also have more
1155 // calls to stuffClipboard. (Lgb)
1156 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1158 // This doesn't make sense, if there is no selection
1159 if (!selection.set())
1162 // OK, we have a selection. This is always between selection.start
1163 // and selection.end
1165 // make sure that the depth behind the selection are restored, too
1166 ParagraphList::iterator endpit = boost::next(getPar(selection.end.par()));
1167 ParagraphList::iterator undoendpit = endpit;
1168 ParagraphList::iterator pars_end = ownerParagraphs().end();
1170 if (endpit != pars_end && endpit->getDepth()) {
1171 while (endpit != pars_end && endpit->getDepth()) {
1173 undoendpit = endpit;
1175 } else if (endpit != pars_end) {
1176 // because of parindents etc.
1180 recUndo(selection.start.par(), parOffset(undoendpit) - 1);
1182 endpit = getPar(selection.end.par());
1183 int endpos = selection.end.pos();
1185 BufferParams const & bufparams = bv()->buffer()->params();
1186 boost::tie(endpit, endpos) = realcut ?
1187 CutAndPaste::cutSelection(bufparams,
1189 getPar(selection.start.par()), endpit,
1190 selection.start.pos(), endpos,
1191 bufparams.textclass,
1193 : CutAndPaste::eraseSelection(bufparams,
1195 getPar(selection.start.par()), endpit,
1196 selection.start.pos(), endpos,
1198 // sometimes necessary
1200 getPar(selection.start.par())->stripLeadingSpaces();
1202 redoParagraphs(getPar(selection.start.par()), boost::next(endpit));
1203 // cutSelection can invalidate the cursor so we need to set
1205 // we prefer the end for when tracking changes
1207 cursor.par(parOffset(endpit));
1209 // need a valid cursor. (Lgb)
1212 setCursor(cursorPar(), cursor.pos());
1213 selection.cursor = cursor;
1218 void LyXText::copySelection()
1220 // stuff the selection onto the X clipboard, from an explicit copy request
1221 bv()->stuffClipboard(selectionAsString(*bv()->buffer(), true));
1223 // this doesnt make sense, if there is no selection
1224 if (!selection.set())
1227 // ok we have a selection. This is always between selection.start
1228 // and sel_end cursor
1230 // copy behind a space if there is one
1231 while (getPar(selection.start)->size() > selection.start.pos()
1232 && getPar(selection.start)->isLineSeparator(selection.start.pos())
1233 && (selection.start.par() != selection.end.par()
1234 || selection.start.pos() < selection.end.pos()))
1235 selection.start.pos(selection.start.pos() + 1);
1237 CutAndPaste::copySelection(getPar(selection.start.par()),
1238 getPar(selection.end.par()),
1239 selection.start.pos(), selection.end.pos(),
1240 bv()->buffer()->params().textclass);
1244 void LyXText::pasteSelection(size_t sel_index)
1246 // this does not make sense, if there is nothing to paste
1247 if (!CutAndPaste::checkPastePossible())
1250 recUndo(cursor.par());
1252 ParagraphList::iterator endpit;
1257 boost::tie(ppp, endpit) =
1258 CutAndPaste::pasteSelection(*bv()->buffer(),
1260 cursorPar(), cursor.pos(),
1261 bv()->buffer()->params().textclass,
1263 bufferErrors(*bv()->buffer(), el);
1264 bv()->showErrorList(_("Paste"));
1266 redoParagraphs(cursorPar(), endpit);
1268 setCursor(cursor.par(), cursor.pos());
1271 selection.cursor = cursor;
1272 setCursor(ppp.first, ppp.second);
1278 void LyXText::setSelectionRange(lyx::pos_type length)
1283 selection.cursor = cursor;
1290 // simple replacing. The font of the first selected character is used
1291 void LyXText::replaceSelectionWithString(string const & str)
1293 recUndo(cursor.par());
1296 if (!selection.set()) { // create a dummy selection
1297 selection.end = cursor;
1298 selection.start = cursor;
1301 // Get font setting before we cut
1302 pos_type pos = selection.end.pos();
1303 LyXFont const font = getPar(selection.start)
1304 ->getFontSettings(bv()->buffer()->params(),
1305 selection.start.pos());
1307 // Insert the new string
1308 string::const_iterator cit = str.begin();
1309 string::const_iterator end = str.end();
1310 for (; cit != end; ++cit) {
1311 getPar(selection.end)->insertChar(pos, (*cit), font);
1315 // Cut the selection
1316 cutSelection(true, false);
1322 // needed to insert the selection
1323 void LyXText::insertStringAsLines(string const & str)
1325 ParagraphList::iterator pit = cursorPar();
1326 pos_type pos = cursor.pos();
1327 ParagraphList::iterator endpit = boost::next(cursorPar());
1329 recUndo(cursor.par());
1331 // only to be sure, should not be neccessary
1334 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1336 redoParagraphs(cursorPar(), endpit);
1337 setCursor(cursorPar(), cursor.pos());
1338 selection.cursor = cursor;
1339 setCursor(pit, pos);
1344 // turns double-CR to single CR, others where converted into one
1345 // blank. Then InsertStringAsLines is called
1346 void LyXText::insertStringAsParagraphs(string const & str)
1348 string linestr(str);
1349 bool newline_inserted = false;
1350 string::size_type const siz = linestr.length();
1352 for (string::size_type i = 0; i < siz; ++i) {
1353 if (linestr[i] == '\n') {
1354 if (newline_inserted) {
1355 // we know that \r will be ignored by
1356 // InsertStringA. Of course, it is a dirty
1357 // trick, but it works...
1358 linestr[i - 1] = '\r';
1362 newline_inserted = true;
1364 } else if (IsPrintable(linestr[i])) {
1365 newline_inserted = false;
1368 insertStringAsLines(linestr);
1372 void LyXText::setCursor(ParagraphList::iterator pit, pos_type pos)
1374 setCursor(parOffset(pit), pos);
1378 bool LyXText::setCursor(paroffset_type par, pos_type pos, bool setfont, bool boundary)
1380 LyXCursor old_cursor = cursor;
1381 setCursorIntern(par, pos, setfont, boundary);
1382 return deleteEmptyParagraphMechanism(old_cursor);
1386 void LyXText::redoCursor()
1388 #warning maybe the same for selections?
1389 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1393 void LyXText::setCursor(LyXCursor & cur, paroffset_type par,
1394 pos_type pos, bool boundary)
1396 BOOST_ASSERT(par != int(ownerParagraphs().size()));
1400 cur.boundary(boundary);
1402 // no rows, no fun...
1403 if (ownerParagraphs().begin()->rows.empty())
1406 // get the cursor y position in text
1408 ParagraphList::iterator pit = getPar(par);
1409 RowList::iterator row = getRow(pit, pos);
1410 int y = pit->y + row->y_offset();
1412 // y is now the beginning of the cursor row
1413 y += row->baseline();
1414 // y is now the cursor baseline
1417 pos_type last = lastPos(*pit, row);
1419 // None of these should happen, but we're scaredy-cats
1420 if (pos > pit->size()) {
1421 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1424 } else if (pos > last + 1) {
1425 lyxerr << "dont like 2 please report" << endl;
1426 // This shouldn't happen.
1429 } else if (pos < row->pos()) {
1430 lyxerr << "dont like 3 please report" << endl;
1435 // now get the cursors x position
1436 float x = getCursorX(pit, row, pos, last, boundary);
1442 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1443 pos_type pos, pos_type last, bool boundary) const
1445 pos_type cursor_vpos = 0;
1446 double x = rit->x();
1447 double fill_separator = rit->fill_separator();
1448 double fill_hfill = rit->fill_hfill();
1449 double fill_label_hfill = rit->fill_label_hfill();
1450 pos_type const rit_pos = rit->pos();
1453 cursor_vpos = rit_pos;
1454 else if (pos > last && !boundary)
1455 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params()))
1456 ? rit_pos : last + 1;
1457 else if (pos > rit_pos && (pos > last || boundary))
1458 // Place cursor after char at (logical) position pos - 1
1459 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1460 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1462 // Place cursor before char at (logical) position pos
1463 cursor_vpos = (bidi_level(pos) % 2 == 0)
1464 ? log2vis(pos) : log2vis(pos) + 1;
1466 pos_type body_pos = pit->beginningOfBody();
1468 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1471 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1472 pos_type pos = vis2log(vpos);
1473 if (body_pos > 0 && pos == body_pos - 1) {
1474 x += fill_label_hfill +
1475 font_metrics::width(
1476 pit->layout()->labelsep, getLabelFont(pit));
1477 if (pit->isLineSeparator(body_pos - 1))
1478 x -= singleWidth(pit, body_pos - 1);
1481 if (hfillExpansion(*pit, rit, pos)) {
1482 x += singleWidth(pit, pos);
1483 if (pos >= body_pos)
1486 x += fill_label_hfill;
1487 } else if (pit->isSeparator(pos)) {
1488 x += singleWidth(pit, pos);
1489 if (pos >= body_pos)
1490 x += fill_separator;
1492 x += singleWidth(pit, pos);
1498 void LyXText::setCursorIntern(paroffset_type par,
1499 pos_type pos, bool setfont, bool boundary)
1501 setCursor(cursor, par, pos, boundary);
1507 void LyXText::setCurrentFont()
1509 pos_type pos = cursor.pos();
1510 ParagraphList::iterator pit = cursorPar();
1512 if (cursor.boundary() && pos > 0)
1516 if (pos == pit->size())
1518 else // potentional bug... BUG (Lgb)
1519 if (pit->isSeparator(pos)) {
1520 if (pos > cursorRow()->pos() &&
1521 bidi_level(pos) % 2 ==
1522 bidi_level(pos - 1) % 2)
1524 else if (pos + 1 < pit->size())
1529 BufferParams const & bufparams = bv()->buffer()->params();
1530 current_font = pit->getFontSettings(bufparams, pos);
1531 real_current_font = getFont(pit, pos);
1533 if (cursor.pos() == pit->size() &&
1534 isBoundary(*bv()->buffer(), *pit, cursor.pos()) &&
1535 !cursor.boundary()) {
1536 Language const * lang =
1537 pit->getParLanguage(bufparams);
1538 current_font.setLanguage(lang);
1539 current_font.setNumber(LyXFont::OFF);
1540 real_current_font.setLanguage(lang);
1541 real_current_font.setNumber(LyXFont::OFF);
1546 // returns the column near the specified x-coordinate of the row
1547 // x is set to the real beginning of this column
1548 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1549 RowList::iterator rit, int & x, bool & boundary) const
1551 double tmpx = rit->x();
1552 double fill_separator = rit->fill_separator();
1553 double fill_hfill = rit->fill_hfill();
1554 double fill_label_hfill = rit->fill_label_hfill();
1556 pos_type vc = rit->pos();
1557 pos_type last = lastPos(*pit, rit);
1559 LyXLayout_ptr const & layout = pit->layout();
1561 bool left_side = false;
1563 pos_type body_pos = pit->beginningOfBody();
1564 double last_tmpx = tmpx;
1567 (body_pos - 1 > last ||
1568 !pit->isLineSeparator(body_pos - 1)))
1571 // check for empty row
1577 while (vc <= last && tmpx <= x) {
1580 if (body_pos > 0 && c == body_pos - 1) {
1581 tmpx += fill_label_hfill +
1582 font_metrics::width(layout->labelsep, getLabelFont(pit));
1583 if (pit->isLineSeparator(body_pos - 1))
1584 tmpx -= singleWidth(pit, body_pos - 1);
1587 if (hfillExpansion(*pit, rit, c)) {
1588 tmpx += singleWidth(pit, c);
1592 tmpx += fill_label_hfill;
1593 } else if (pit->isSeparator(c)) {
1594 tmpx += singleWidth(pit, c);
1596 tmpx += fill_separator;
1598 tmpx += singleWidth(pit, c);
1603 if ((tmpx + last_tmpx) / 2 > x) {
1608 if (vc > last + 1) // This shouldn't happen.
1612 // This (rtl_support test) is not needed, but gives
1613 // some speedup if rtl_support == false
1614 bool const lastrow = lyxrc.rtl_support
1615 && boost::next(rit) == pit->rows.end();
1617 // If lastrow is false, we don't need to compute
1618 // the value of rtl.
1619 bool const rtl = (lastrow)
1620 ? pit->isRightToLeftPar(bv()->buffer()->params())
1623 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1624 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1626 else if (vc == rit->pos()) {
1628 if (bidi_level(c) % 2 == 1)
1631 c = vis2log(vc - 1);
1632 bool const rtl = (bidi_level(c) % 2 == 1);
1633 if (left_side == rtl) {
1635 boundary = isBoundary(*bv()->buffer(), *pit, c);
1639 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1640 if (bidi_level(last) % 2 == 0)
1641 tmpx -= singleWidth(pit, last);
1643 tmpx += singleWidth(pit, last);
1653 void LyXText::setCursorFromCoordinates(int x, int y)
1655 LyXCursor old_cursor = cursor;
1656 setCursorFromCoordinates(cursor, x, y);
1658 deleteEmptyParagraphMechanism(old_cursor);
1662 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1664 // Get the row first.
1665 ParagraphList::iterator pit;
1666 RowList::iterator rit = getRowNearY(y, pit);
1667 y = pit->y + rit->y_offset();
1670 pos_type const column = getColumnNearX(pit, rit, x, bound);
1671 cur.par(parOffset(pit));
1672 cur.pos(rit->pos() + column);
1674 cur.y(y + rit->baseline());
1676 cur.boundary(bound);
1680 void LyXText::cursorLeft(bool internal)
1682 if (cursor.pos() > 0) {
1683 bool boundary = cursor.boundary();
1684 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1685 if (!internal && !boundary &&
1686 isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos() + 1))
1687 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1688 } else if (cursor.par() != 0) {
1689 // steps into the paragraph above
1690 setCursor(cursor.par() - 1, boost::prior(cursorPar())->size());
1695 void LyXText::cursorRight(bool internal)
1697 bool const at_end = (cursor.pos() == cursorPar()->size());
1698 bool const at_newline = !at_end &&
1699 cursorPar()->isNewline(cursor.pos());
1701 if (!internal && cursor.boundary() && !at_newline)
1702 setCursor(cursor.par(), cursor.pos(), true, false);
1704 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1706 isBoundary(*bv()->buffer(), *cursorPar(), cursor.pos()))
1707 setCursor(cursor.par(), cursor.pos(), true, true);
1708 } else if (cursor.par() + 1 != int(ownerParagraphs().size()))
1709 setCursor(cursor.par() + 1, 0);
1713 void LyXText::cursorUp(bool selecting)
1716 int x = cursor.x_fix();
1717 int y = cursor.y() - cursorRow()->baseline() - 1;
1718 setCursorFromCoordinates(x, y);
1720 int topy = bv_owner->top_y();
1721 int y1 = cursor.y() - topy;
1724 InsetOld * inset_hit = checkInsetHit(x, y1);
1725 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1726 inset_hit->localDispatch(
1727 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1731 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1732 cursorRow()->baseline() << endl;
1733 setCursorFromCoordinates(cursor.x_fix(),
1734 cursor.y() - cursorRow()->baseline() - 1);
1739 void LyXText::cursorDown(bool selecting)
1742 int x = cursor.x_fix();
1743 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1744 setCursorFromCoordinates(x, y);
1745 if (!selecting && cursorRow() == cursorIRow()) {
1746 int topy = bv_owner->top_y();
1747 int y1 = cursor.y() - topy;
1750 InsetOld * inset_hit = checkInsetHit(x, y1);
1751 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1752 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1753 inset_hit->localDispatch(cmd);
1757 setCursorFromCoordinates(cursor.x_fix(),
1758 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1763 void LyXText::cursorUpParagraph()
1765 if (cursor.pos() > 0)
1766 setCursor(cursorPar(), 0);
1767 else if (cursorPar() != ownerParagraphs().begin())
1768 setCursor(boost::prior(cursorPar()), 0);
1772 void LyXText::cursorDownParagraph()
1774 ParagraphList::iterator par = cursorPar();
1775 ParagraphList::iterator next_par = boost::next(par);
1777 if (next_par != ownerParagraphs().end())
1778 setCursor(next_par, 0);
1780 setCursor(par, par->size());
1784 // fix the cursor `cur' after a characters has been deleted at `where'
1785 // position. Called by deleteEmptyParagraphMechanism
1786 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1788 // if cursor is not in the paragraph where the delete occured,
1790 if (cur.par() != where.par())
1793 // if cursor position is after the place where the delete occured,
1795 if (cur.pos() > where.pos())
1796 cur.pos(cur.pos()-1);
1798 // check also if we don't want to set the cursor on a spot behind the
1799 // pagragraph because we erased the last character.
1800 if (cur.pos() > getPar(cur)->size())
1801 cur.pos(getPar(cur)->size());
1803 // recompute row et al. for this cursor
1804 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1808 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1810 // Would be wrong to delete anything if we have a selection.
1811 if (selection.set())
1814 // Don't do anything if the cursor is invalid
1815 if (old_cursor.par() == -1)
1818 // We allow all kinds of "mumbo-jumbo" when freespacing.
1819 ParagraphList::iterator const old_pit = getPar(old_cursor);
1820 if (old_pit->isFreeSpacing())
1823 /* Ok I'll put some comments here about what is missing.
1824 I have fixed BackSpace (and thus Delete) to not delete
1825 double-spaces automagically. I have also changed Cut,
1826 Copy and Paste to hopefully do some sensible things.
1827 There are still some small problems that can lead to
1828 double spaces stored in the document file or space at
1829 the beginning of paragraphs. This happens if you have
1830 the cursor between to spaces and then save. Or if you
1831 cut and paste and the selection have a space at the
1832 beginning and then save right after the paste. I am
1833 sure none of these are very hard to fix, but I will
1834 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1835 that I can get some feedback. (Lgb)
1838 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1839 // delete the LineSeparator.
1842 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1843 // delete the LineSeparator.
1846 // If the pos around the old_cursor were spaces, delete one of them.
1847 if (old_cursor.par() != cursor.par()
1848 || old_cursor.pos() != cursor.pos()) {
1850 // Only if the cursor has really moved
1851 if (old_cursor.pos() > 0
1852 && old_cursor.pos() < old_pit->size()
1853 && old_pit->isLineSeparator(old_cursor.pos())
1854 && old_pit->isLineSeparator(old_cursor.pos() - 1)) {
1855 bool erased = old_pit->erase(old_cursor.pos() - 1);
1856 redoParagraph(old_pit);
1860 #ifdef WITH_WARNINGS
1861 #warning This will not work anymore when we have multiple views of the same buffer
1862 // In this case, we will have to correct also the cursors held by
1863 // other bufferviews. It will probably be easier to do that in a more
1864 // automated way in LyXCursor code. (JMarc 26/09/2001)
1866 // correct all cursors held by the LyXText
1867 fixCursorAfterDelete(cursor, old_cursor);
1868 fixCursorAfterDelete(selection.cursor, old_cursor);
1869 fixCursorAfterDelete(selection.start, old_cursor);
1870 fixCursorAfterDelete(selection.end, old_cursor);
1875 // don't delete anything if this is the ONLY paragraph!
1876 if (ownerParagraphs().size() == 1)
1879 // Do not delete empty paragraphs with keepempty set.
1880 if (old_pit->allowEmpty())
1883 // only do our magic if we changed paragraph
1884 if (old_cursor.par() == cursor.par())
1887 // record if we have deleted a paragraph
1888 // we can't possibly have deleted a paragraph before this point
1889 bool deleted = false;
1891 if (old_pit->empty() ||
1892 (old_pit->size() == 1 && old_pit->isLineSeparator(0))) {
1893 // ok, we will delete something
1894 LyXCursor tmpcursor;
1898 bool selection_position_was_oldcursor_position =
1899 selection.cursor.par() == old_cursor.par()
1900 && selection.cursor.pos() == old_cursor.pos();
1903 cursor = old_cursor; // that undo can restore the right cursor position
1905 ParagraphList::iterator endpit = boost::next(old_pit);
1906 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1909 recUndo(parOffset(old_pit), parOffset(endpit) - 1);
1913 ownerParagraphs().erase(old_pit);
1917 setCursorIntern(cursor.par(), cursor.pos());
1919 if (selection_position_was_oldcursor_position) {
1920 // correct selection
1921 selection.cursor = cursor;
1925 if (old_pit->stripLeadingSpaces()) {
1926 redoParagraph(old_pit);
1928 setCursorIntern(cursor.par(), cursor.pos());
1929 selection.cursor = cursor;
1936 ParagraphList & LyXText::ownerParagraphs() const
1938 return *paragraphs_;
1942 void LyXText::recUndo(paroffset_type first, paroffset_type last) const
1944 recordUndo(Undo::ATOMIC, this, first, last);
1948 void LyXText::recUndo(lyx::paroffset_type par) const
1950 recordUndo(Undo::ATOMIC, this, par, par);
1954 bool LyXText::isInInset() const
1956 // Sub-level has non-null bv owner and non-null inset owner.
1957 return inset_owner != 0;
1961 int defaultRowHeight()
1963 LyXFont const font(LyXFont::ALL_SANE);
1964 return int(font_metrics::maxAscent(font)
1965 + font_metrics::maxDescent(font) * 1.5);