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 "paragraph.h"
26 #include "funcrequest.h"
27 #include "frontends/LyXView.h"
28 #include "undo_funcs.h"
30 #include "buffer_funcs.h"
31 #include "bufferparams.h"
32 #include "errorlist.h"
34 #include "BufferView.h"
35 #include "CutAndPaste.h"
36 #include "frontends/Painter.h"
37 #include "frontends/font_metrics.h"
40 #include "FloatList.h"
42 #include "ParagraphParameters.h"
44 #include "lyxrow_funcs.h"
45 #include "metricsinfo.h"
46 #include "paragraph_funcs.h"
48 #include "insets/insetbibitem.h"
49 #include "insets/insetenv.h"
50 #include "insets/insetfloat.h"
51 #include "insets/insetwrap.h"
53 #include "support/LAssert.h"
54 #include "support/textutils.h"
55 #include "support/lstrings.h"
57 #include <boost/tuple/tuple.hpp>
61 using namespace lyx::support;
71 LyXText::LyXText(BufferView * bv, InsetText * inset, bool ininset,
72 ParagraphList & paragraphs)
73 : height(0), width(0), anchor_y_(0),
74 inset_owner(inset), the_locking_inset(0), bv_owner(bv),
75 in_inset_(ininset), paragraphs_(paragraphs)
80 void LyXText::init(BufferView * bview)
84 ParagraphList::iterator const beg = ownerParagraphs().begin();
85 ParagraphList::iterator const end = ownerParagraphs().end();
86 for (ParagraphList::iterator pit = beg; pit != end; ++pit)
94 current_font = getFont(beg, 0);
96 redoParagraphs(beg, end);
97 setCursorIntern(beg, 0);
98 selection.cursor = cursor;
104 // Gets the fully instantiated font at a given position in a paragraph
105 // Basically the same routine as Paragraph::getFont() in paragraph.C.
106 // The difference is that this one is used for displaying, and thus we
107 // are allowed to make cosmetic improvements. For instance make footnotes
109 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
113 LyXLayout_ptr const & layout = pit->layout();
115 BufferParams const & params = bv()->buffer()->params;
117 // We specialize the 95% common case:
118 if (!pit->getDepth()) {
119 if (layout->labeltype == LABEL_MANUAL
120 && pos < pit->beginningOfBody()) {
122 LyXFont f = pit->getFontSettings(params, pos);
124 pit->inInset()->getDrawFont(f);
125 return f.realize(layout->reslabelfont);
127 LyXFont f = pit->getFontSettings(params, pos);
129 pit->inInset()->getDrawFont(f);
130 return f.realize(layout->resfont);
134 // The uncommon case need not be optimized as much
138 if (pos < pit->beginningOfBody()) {
140 layoutfont = layout->labelfont;
143 layoutfont = layout->font;
146 LyXFont tmpfont = pit->getFontSettings(params, pos);
147 tmpfont.realize(layoutfont);
150 pit->inInset()->getDrawFont(tmpfont);
152 // Realize with the fonts of lesser depth.
153 tmpfont.realize(outerFont(pit, ownerParagraphs()));
154 tmpfont.realize(defaultfont_);
160 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
162 LyXLayout_ptr const & layout = pit->layout();
164 if (!pit->getDepth())
165 return layout->resfont;
167 LyXFont font = layout->font;
168 // Realize with the fonts of lesser depth.
169 font.realize(outerFont(pit, ownerParagraphs()));
170 font.realize(defaultfont_);
176 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
178 LyXLayout_ptr const & layout = pit->layout();
180 if (!pit->getDepth())
181 return layout->reslabelfont;
183 LyXFont font = layout->labelfont;
184 // Realize with the fonts of lesser depth.
185 font.realize(outerFont(pit, ownerParagraphs()));
186 font.realize(defaultfont_);
192 void LyXText::setCharFont(ParagraphList::iterator pit,
193 pos_type pos, LyXFont const & fnt,
196 BufferParams const & params = bv()->buffer()->params;
197 LyXFont font = getFont(pit, pos);
198 font.update(fnt, params.language, toggleall);
199 // Let the insets convert their font
200 if (pit->isInset(pos)) {
201 InsetOld * inset = pit->getInset(pos);
202 if (isEditableInset(inset)) {
203 static_cast<UpdatableInset *>(inset)
204 ->setFont(bv(), fnt, toggleall, true);
208 // Plug through to version below:
209 setCharFont(pit, pos, font);
213 void LyXText::setCharFont(
214 ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
217 LyXLayout_ptr const & layout = pit->layout();
219 // Get concrete layout font to reduce against
222 if (pos < pit->beginningOfBody())
223 layoutfont = layout->labelfont;
225 layoutfont = layout->font;
227 // Realize against environment font information
228 if (pit->getDepth()) {
229 ParagraphList::iterator tp = pit;
230 while (!layoutfont.resolved() &&
231 tp != ownerParagraphs().end() &&
233 tp = outerHook(tp, ownerParagraphs());
234 if (tp != ownerParagraphs().end())
235 layoutfont.realize(tp->layout()->font);
239 layoutfont.realize(defaultfont_);
241 // Now, reduce font against full layout font
242 font.reduce(layoutfont);
244 pit->setFont(pos, font);
248 InsetOld * LyXText::getInset() const
250 ParagraphList::iterator pit = cursor.par();
251 pos_type const pos = cursor.pos();
253 if (pos < pit->size() && pit->isInset(pos)) {
254 return pit->getInset(pos);
260 void LyXText::toggleInset()
262 InsetOld * inset = getInset();
263 // is there an editable inset at cursor position?
264 if (!isEditableInset(inset)) {
265 // No, try to see if we are inside a collapsable inset
266 if (inset_owner && inset_owner->owner()
267 && inset_owner->owner()->isOpen()) {
268 bv()->unlockInset(inset_owner->owner());
269 inset_owner->owner()->close(bv());
270 bv()->getLyXText()->cursorRight(bv());
274 //bv()->owner()->message(inset->editMessage());
276 // do we want to keep this?? (JMarc)
277 if (!isHighlyEditableInset(inset))
278 recordUndo(bv(), Undo::ATOMIC);
289 /* used in setlayout */
290 // Asger is not sure we want to do this...
291 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
294 LyXLayout_ptr const & layout = par.layout();
295 pos_type const psize = par.size();
298 for (pos_type pos = 0; pos < psize; ++pos) {
299 if (pos < par.beginningOfBody())
300 layoutfont = layout->labelfont;
302 layoutfont = layout->font;
304 LyXFont tmpfont = par.getFontSettings(params, pos);
305 tmpfont.reduce(layoutfont);
306 par.setFont(pos, tmpfont);
311 ParagraphList::iterator
312 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
313 LyXCursor & send_cur,
314 string const & layout)
316 ParagraphList::iterator endpit = boost::next(send_cur.par());
317 ParagraphList::iterator undoendpit = endpit;
318 ParagraphList::iterator pars_end = ownerParagraphs().end();
320 if (endpit != pars_end && endpit->getDepth()) {
321 while (endpit != pars_end && endpit->getDepth()) {
325 } else if (endpit != pars_end) {
326 // because of parindents etc.
330 recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
332 // ok we have a selection. This is always between sstart_cur
333 // and sel_end cursor
335 ParagraphList::iterator pit = sstart_cur.par();
336 ParagraphList::iterator epit = boost::next(send_cur.par());
338 LyXLayout_ptr const & lyxlayout =
339 bv()->buffer()->params.getLyXTextClass()[layout];
342 pit->applyLayout(lyxlayout);
343 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
344 pit->params().spaceTop(lyxlayout->fill_top ?
345 VSpace(VSpace::VFILL)
346 : VSpace(VSpace::NONE));
347 pit->params().spaceBottom(lyxlayout->fill_bottom ?
348 VSpace(VSpace::VFILL)
349 : VSpace(VSpace::NONE));
350 if (lyxlayout->margintype == MARGIN_MANUAL)
351 pit->setLabelWidthString(lyxlayout->labelstring());
354 } while (pit != epit);
360 // set layout over selection and make a total rebreak of those paragraphs
361 void LyXText::setLayout(string const & layout)
363 LyXCursor tmpcursor = cursor; // store the current cursor
365 // if there is no selection just set the layout
366 // of the current paragraph
367 if (!selection.set()) {
368 selection.start = cursor; // dummy selection
369 selection.end = cursor;
372 // special handling of new environment insets
373 BufferParams const & params = bv()->buffer()->params;
374 LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
375 if (lyxlayout->is_environment) {
376 // move everything in a new environment inset
377 lyxerr << "setting layout " << layout << endl;
378 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
379 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
380 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
381 InsetOld * inset = new InsetEnvironment(params, layout);
382 if (bv()->insertInset(inset)) {
384 //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
391 ParagraphList::iterator endpit = setLayout(cursor, selection.start,
392 selection.end, layout);
393 redoParagraphs(selection.start.par(), endpit);
395 // we have to reset the selection, because the
396 // geometry could have changed
397 setCursor(selection.start.par(), selection.start.pos(), false);
398 selection.cursor = cursor;
399 setCursor(selection.end.par(), selection.end.pos(), false);
403 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
407 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
409 ParagraphList::iterator pit = cursor.par();
410 ParagraphList::iterator end = cursor.par();
411 ParagraphList::iterator start = pit;
413 if (selection.set()) {
414 pit = selection.start.par();
415 end = selection.end.par();
419 ParagraphList::iterator pastend = boost::next(end);
422 recordUndo(bv(), Undo::ATOMIC, start, end);
424 bool changed = false;
426 int prev_after_depth = 0;
427 #warning parlist ... could be nicer ?
428 if (start != ownerParagraphs().begin()) {
429 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
433 int const depth = pit->params().depth();
434 if (type == bv_funcs::INC_DEPTH) {
435 if (depth < prev_after_depth
436 && pit->layout()->labeltype != LABEL_BIBLIO) {
439 pit->params().depth(depth + 1);
444 pit->params().depth(depth - 1);
447 prev_after_depth = pit->getMaxDepthAfter();
459 redoParagraphs(start, pastend);
461 // We need to actually move the text->cursor. I don't
462 // understand why ...
463 LyXCursor tmpcursor = cursor;
465 // we have to reset the visual selection because the
466 // geometry could have changed
467 if (selection.set()) {
468 setCursor(selection.start.par(), selection.start.pos());
469 selection.cursor = cursor;
470 setCursor(selection.end.par(), selection.end.pos());
473 // this handles the counter labels, and also fixes up
474 // depth values for follow-on (child) paragraphs
478 setCursor(tmpcursor.par(), tmpcursor.pos());
484 // set font over selection and make a total rebreak of those paragraphs
485 void LyXText::setFont(LyXFont const & font, bool toggleall)
487 // if there is no selection just set the current_font
488 if (!selection.set()) {
489 // Determine basis font
491 if (cursor.pos() < cursor.par()->beginningOfBody()) {
492 layoutfont = getLabelFont(cursor.par());
494 layoutfont = getLayoutFont(cursor.par());
496 // Update current font
497 real_current_font.update(font,
498 bv()->buffer()->params.language,
501 // Reduce to implicit settings
502 current_font = real_current_font;
503 current_font.reduce(layoutfont);
504 // And resolve it completely
505 real_current_font.realize(layoutfont);
510 LyXCursor tmpcursor = cursor; // store the current cursor
512 // ok we have a selection. This is always between sel_start_cursor
513 // and sel_end cursor
515 recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
517 cursor = selection.start;
518 while (cursor.par() != selection.end.par() ||
519 cursor.pos() < selection.end.pos())
521 if (cursor.pos() < cursor.par()->size()) {
522 // an open footnote should behave like a closed one
523 setCharFont(cursor.par(), cursor.pos(),
525 cursor.pos(cursor.pos() + 1);
528 cursor.par(boost::next(cursor.par()));
533 redoParagraph(selection.start.par());
535 // we have to reset the selection, because the
536 // geometry could have changed, but we keep
537 // it for user convenience
538 setCursor(selection.start.par(), selection.start.pos());
539 selection.cursor = cursor;
540 setCursor(selection.end.par(), selection.end.pos());
542 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
543 tmpcursor.boundary());
547 // rebreaks all paragraphs between the specified pars
548 // This function is needed after SetLayout and SetFont etc.
549 void LyXText::redoParagraphs(ParagraphList::iterator start,
550 ParagraphList::iterator end)
552 for ( ; start != end; ++start)
553 redoParagraph(start);
557 void LyXText::redoParagraph(ParagraphList::iterator pit)
559 RowList::iterator rit = pit->rows.begin();
560 RowList::iterator end = pit->rows.end();
562 // remove rows of paragraph, keep track of height changes
563 for (int i = 0; rit != end; ++rit, ++i)
564 height -= rit->height();
568 InsetList::iterator ii = pit->insetlist.begin();
569 InsetList::iterator iend = pit->insetlist.end();
570 for (; ii != iend; ++ii) {
572 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
573 ii->inset->metrics(mi, dim);
576 // rebreak the paragraph
577 for (pos_type z = 0; z < pit->size() + 1; ) {
579 z = rowBreakPoint(pit, row) + 1;
581 pit->rows.push_back(row);
584 // set height and fill of rows
585 for (rit = pit->rows.begin(); rit != end; ++rit) {
586 rit->fill(fill(pit, rit, workWidth()));
587 prepareToPrint(pit, rit);
588 setHeightOfRow(pit, rit);
589 height += rit->height();
592 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
596 void LyXText::fullRebreak()
598 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
600 selection.cursor = cursor;
604 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
606 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
607 // << " workWidth: " << workWidth() << endl;
608 //Assert(mi.base.textwidth);
615 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
616 updateRowPositions();
619 dim.asc = firstRow()->ascent_of_text();
620 dim.des = height - dim.asc;
621 dim.wid = std::max(mi.base.textwidth, int(width));
625 // important for the screen
628 // the cursor set functions have a special mechanism. When they
629 // realize, that you left an empty paragraph, they will delete it.
630 // They also delete the corresponding row
632 // need the selection cursor:
633 void LyXText::setSelection()
635 TextCursor::setSelection();
640 void LyXText::clearSelection()
642 TextCursor::clearSelection();
644 // reset this in the bv_owner!
645 if (bv_owner && bv_owner->text)
646 bv_owner->text->xsel_cache.set(false);
650 void LyXText::cursorHome()
652 setCursor(cursor.par(), cursorRow()->pos());
656 void LyXText::cursorEnd()
658 if (cursor.par()->empty())
661 RowList::iterator rit = cursorRow();
662 RowList::iterator next_rit = boost::next(rit);
663 RowList::iterator end = boost::next(rit);
664 ParagraphList::iterator pit = cursor.par();
665 pos_type last_pos = lastPos(*pit, rit);
667 if (next_rit == end) {
671 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
676 setCursor(pit, last_pos);
680 void LyXText::cursorTop()
682 setCursor(ownerParagraphs().begin(), 0);
686 void LyXText::cursorBottom()
688 ParagraphList::iterator lastpit =
689 boost::prior(ownerParagraphs().end());
690 setCursor(lastpit, lastpit->size());
694 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
696 // If the mask is completely neutral, tell user
697 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
698 // Could only happen with user style
699 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
703 // Try implicit word selection
704 // If there is a change in the language the implicit word selection
706 LyXCursor resetCursor = cursor;
707 bool implicitSelection = (font.language() == ignore_language
708 && font.number() == LyXFont::IGNORE)
709 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
712 setFont(font, toggleall);
714 // Implicit selections are cleared afterwards
715 //and cursor is set to the original position.
716 if (implicitSelection) {
718 cursor = resetCursor;
719 setCursor(cursor.par(), cursor.pos());
720 selection.cursor = cursor;
725 string LyXText::getStringToIndex()
727 // Try implicit word selection
728 // If there is a change in the language the implicit word selection
730 LyXCursor const reset_cursor = cursor;
731 bool const implicitSelection =
732 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
735 if (!selection.set())
736 bv()->owner()->message(_("Nothing to index!"));
737 else if (selection.start.par() != selection.end.par())
738 bv()->owner()->message(_("Cannot index more than one paragraph!"));
740 idxstring = selectionAsString(bv()->buffer(), false);
742 // Reset cursors to their original position.
743 cursor = reset_cursor;
744 setCursor(cursor.par(), cursor.pos());
745 selection.cursor = cursor;
747 // Clear the implicit selection.
748 if (implicitSelection)
755 // the DTP switches for paragraphs. LyX will store them in the first
756 // physical paragraph. When a paragraph is broken, the top settings rest,
757 // the bottom settings are given to the new one. So I can make sure,
758 // they do not duplicate themself and you cannnot make dirty things with
761 void LyXText::setParagraph(bool line_top, bool line_bottom,
762 bool pagebreak_top, bool pagebreak_bottom,
763 VSpace const & space_top,
764 VSpace const & space_bottom,
765 Spacing const & spacing,
767 string const & labelwidthstring,
770 LyXCursor tmpcursor = cursor;
771 if (!selection.set()) {
772 selection.start = cursor;
773 selection.end = cursor;
776 // make sure that the depth behind the selection are restored, too
777 ParagraphList::iterator endpit = boost::next(selection.end.par());
778 ParagraphList::iterator undoendpit = endpit;
779 ParagraphList::iterator pars_end = ownerParagraphs().end();
781 if (endpit != pars_end && endpit->getDepth()) {
782 while (endpit != pars_end && endpit->getDepth()) {
786 } else if (endpit != pars_end) {
787 // because of parindents etc.
791 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
792 boost::prior(undoendpit));
795 ParagraphList::iterator tmppit = selection.end.par();
797 while (tmppit != boost::prior(selection.start.par())) {
798 setCursor(tmppit, 0);
800 ParagraphList::iterator pit = cursor.par();
801 ParagraphParameters & params = pit->params();
803 params.lineTop(line_top);
804 params.lineBottom(line_bottom);
805 params.pagebreakTop(pagebreak_top);
806 params.pagebreakBottom(pagebreak_bottom);
807 params.spaceTop(space_top);
808 params.spaceBottom(space_bottom);
809 params.spacing(spacing);
810 // does the layout allow the new alignment?
811 LyXLayout_ptr const & layout = pit->layout();
813 if (align == LYX_ALIGN_LAYOUT)
814 align = layout->align;
815 if (align & layout->alignpossible) {
816 if (align == layout->align)
817 params.align(LYX_ALIGN_LAYOUT);
821 pit->setLabelWidthString(labelwidthstring);
822 params.noindent(noindent);
823 tmppit = boost::prior(pit);
826 redoParagraphs(selection.start.par(), endpit);
829 setCursor(selection.start.par(), selection.start.pos());
830 selection.cursor = cursor;
831 setCursor(selection.end.par(), selection.end.pos());
833 setCursor(tmpcursor.par(), tmpcursor.pos());
839 // set the counter of a paragraph. This includes the labels
840 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
842 LyXTextClass const & textclass = buf->params.getLyXTextClass();
843 LyXLayout_ptr const & layout = pit->layout();
845 if (pit != ownerParagraphs().begin()) {
847 pit->params().appendix(boost::prior(pit)->params().appendix());
848 if (!pit->params().appendix() &&
849 pit->params().startOfAppendix()) {
850 pit->params().appendix(true);
851 textclass.counters().reset();
853 pit->enumdepth = boost::prior(pit)->enumdepth;
854 pit->itemdepth = boost::prior(pit)->itemdepth;
856 pit->params().appendix(pit->params().startOfAppendix());
861 // Maybe we have to increment the enumeration depth.
862 // BUT, enumeration in a footnote is considered in isolation from its
863 // surrounding paragraph so don't increment if this is the
864 // first line of the footnote
865 // AND, bibliographies can't have their depth changed ie. they
866 // are always of depth 0
867 if (pit != ownerParagraphs().begin()
868 && boost::prior(pit)->getDepth() < pit->getDepth()
869 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
870 && pit->enumdepth < 3
871 && layout->labeltype != LABEL_BIBLIO) {
875 // Maybe we have to decrement the enumeration depth, see note above
876 if (pit != ownerParagraphs().begin()
877 && boost::prior(pit)->getDepth() > pit->getDepth()
878 && layout->labeltype != LABEL_BIBLIO) {
879 pit->enumdepth = depthHook(pit, ownerParagraphs(),
880 pit->getDepth())->enumdepth;
883 if (!pit->params().labelString().empty()) {
884 pit->params().labelString(string());
887 if (layout->margintype == MARGIN_MANUAL) {
888 if (pit->params().labelWidthString().empty())
889 pit->setLabelWidthString(layout->labelstring());
891 pit->setLabelWidthString(string());
894 // is it a layout that has an automatic label?
895 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
896 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
900 if (i >= 0 && i <= buf->params.secnumdepth) {
904 textclass.counters().step(layout->latexname());
906 // Is there a label? Useful for Chapter layout
907 if (!pit->params().appendix()) {
908 s << buf->B_(layout->labelstring());
910 s << buf->B_(layout->labelstring_appendix());
913 // Use of an integer is here less than elegant. For now.
914 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
915 if (!pit->params().appendix()) {
916 numbertype = "sectioning";
918 numbertype = "appendix";
919 if (pit->isRightToLeftPar(buf->params))
926 << textclass.counters()
927 .numberLabel(layout->latexname(),
928 numbertype, langtype, head);
930 pit->params().labelString(STRCONV(s.str()));
932 // reset enum counters
933 textclass.counters().reset("enum");
934 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
935 textclass.counters().reset("enum");
936 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
938 // Yes I know this is a really, really! bad solution
940 string enumcounter("enum");
942 switch (pit->enumdepth) {
954 // not a valid enumdepth...
958 textclass.counters().step(enumcounter);
960 s << textclass.counters()
961 .numberLabel(enumcounter, "enumeration");
962 pit->params().labelString(STRCONV(s.str()));
964 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
965 textclass.counters().step("bibitem");
966 int number = textclass.counters().value("bibitem");
967 if (pit->bibitem()) {
968 pit->bibitem()->setCounter(number);
969 pit->params().labelString(layout->labelstring());
971 // In biblio should't be following counters but...
973 string s = buf->B_(layout->labelstring());
976 if (layout->labeltype == LABEL_SENSITIVE) {
977 ParagraphList::iterator end = ownerParagraphs().end();
978 ParagraphList::iterator tmppit = pit;
981 while (tmppit != end && tmppit->inInset()
982 // the single '=' is intended below
983 && (in = tmppit->inInset()->owner()))
985 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
986 in->lyxCode() == InsetOld::WRAP_CODE) {
990 tmppit = ownerParagraphs().begin();
991 for ( ; tmppit != end; ++tmppit)
992 if (&*tmppit == in->parOwner())
1000 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1001 type = static_cast<InsetFloat*>(in)->params().type;
1002 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1003 type = static_cast<InsetWrap*>(in)->params().type;
1007 Floating const & fl = textclass.floats().getType(type);
1009 textclass.counters().step(fl.type());
1011 // Doesn't work... yet.
1012 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1014 // par->SetLayout(0);
1015 // s = layout->labelstring;
1016 s = _("Senseless: ");
1019 pit->params().labelString(s);
1021 // reset the enumeration counter. They are always reset
1022 // when there is any other layout between
1023 // Just fall-through between the cases so that all
1024 // enum counters deeper than enumdepth is also reset.
1025 switch (pit->enumdepth) {
1027 textclass.counters().reset("enumi");
1029 textclass.counters().reset("enumii");
1031 textclass.counters().reset("enumiii");
1033 textclass.counters().reset("enumiv");
1039 // Updates all counters. Paragraphs with changed label string will be rebroken
1040 void LyXText::updateCounters()
1043 bv()->buffer()->params.getLyXTextClass().counters().reset();
1045 ParagraphList::iterator beg = ownerParagraphs().begin();
1046 ParagraphList::iterator end = ownerParagraphs().end();
1047 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1048 string const oldLabel = pit->params().labelString();
1050 size_t maxdepth = 0;
1052 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1054 if (pit->params().depth() > maxdepth)
1055 pit->params().depth(maxdepth);
1057 // setCounter can potentially change the labelString.
1058 setCounter(bv()->buffer(), pit);
1060 string const & newLabel = pit->params().labelString();
1062 if (oldLabel != newLabel)
1068 void LyXText::insertInset(InsetOld * inset)
1070 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1072 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1074 cursor.par()->insertInset(cursor.pos(), inset);
1075 // Just to rebreak and refresh correctly.
1076 // The character will not be inserted a second time
1077 insertChar(Paragraph::META_INSET);
1078 // If we enter a highly editable inset the cursor should be before
1079 // the inset. After an Undo LyX tries to call inset->edit(...)
1080 // and fails if the cursor is behind the inset and getInset
1081 // does not return the inset!
1082 if (isHighlyEditableInset(inset))
1088 void LyXText::cutSelection(bool doclear, bool realcut)
1090 // Stuff what we got on the clipboard. Even if there is no selection.
1092 // There is a problem with having the stuffing here in that the
1093 // larger the selection the slower LyX will get. This can be
1094 // solved by running the line below only when the selection has
1095 // finished. The solution used currently just works, to make it
1096 // faster we need to be more clever and probably also have more
1097 // calls to stuffClipboard. (Lgb)
1098 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1100 // This doesn't make sense, if there is no selection
1101 if (!selection.set())
1104 // OK, we have a selection. This is always between selection.start
1105 // and selection.end
1107 // make sure that the depth behind the selection are restored, too
1108 ParagraphList::iterator endpit = boost::next(selection.end.par());
1109 ParagraphList::iterator undoendpit = endpit;
1110 ParagraphList::iterator pars_end = ownerParagraphs().end();
1112 if (endpit != pars_end && endpit->getDepth()) {
1113 while (endpit != pars_end && endpit->getDepth()) {
1115 undoendpit = endpit;
1117 } else if (endpit != pars_end) {
1118 // because of parindents etc.
1122 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1123 boost::prior(undoendpit));
1125 endpit = selection.end.par();
1126 int endpos = selection.end.pos();
1128 boost::tie(endpit, endpos) = realcut ?
1129 CutAndPaste::cutSelection(bv()->buffer()->params,
1131 selection.start.par(), endpit,
1132 selection.start.pos(), endpos,
1133 bv()->buffer()->params.textclass,
1135 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1137 selection.start.par(), endpit,
1138 selection.start.pos(), endpos,
1140 // sometimes necessary
1142 selection.start.par()->stripLeadingSpaces();
1144 redoParagraphs(selection.start.par(), boost::next(endpit));
1145 // cutSelection can invalidate the cursor so we need to set
1147 // we prefer the end for when tracking changes
1151 // need a valid cursor. (Lgb)
1154 setCursor(cursor.par(), cursor.pos());
1155 selection.cursor = cursor;
1160 void LyXText::copySelection()
1162 // stuff the selection onto the X clipboard, from an explicit copy request
1163 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1165 // this doesnt make sense, if there is no selection
1166 if (!selection.set())
1169 // ok we have a selection. This is always between selection.start
1170 // and sel_end cursor
1172 // copy behind a space if there is one
1173 while (selection.start.par()->size() > selection.start.pos()
1174 && selection.start.par()->isLineSeparator(selection.start.pos())
1175 && (selection.start.par() != selection.end.par()
1176 || selection.start.pos() < selection.end.pos()))
1177 selection.start.pos(selection.start.pos() + 1);
1179 CutAndPaste::copySelection(selection.start.par(),
1180 selection.end.par(),
1181 selection.start.pos(), selection.end.pos(),
1182 bv()->buffer()->params.textclass);
1186 void LyXText::pasteSelection(size_t sel_index)
1188 // this does not make sense, if there is nothing to paste
1189 if (!CutAndPaste::checkPastePossible())
1192 recordUndo(bv(), Undo::INSERT, cursor.par());
1194 ParagraphList::iterator endpit;
1199 boost::tie(ppp, endpit) =
1200 CutAndPaste::pasteSelection(*bv()->buffer(),
1202 cursor.par(), cursor.pos(),
1203 bv()->buffer()->params.textclass,
1205 bufferErrors(*bv()->buffer(), el);
1206 bv()->showErrorList(_("Paste"));
1208 redoParagraphs(cursor.par(), endpit);
1210 setCursor(cursor.par(), cursor.pos());
1213 selection.cursor = cursor;
1214 setCursor(ppp.first, ppp.second);
1220 void LyXText::setSelectionRange(lyx::pos_type length)
1225 selection.cursor = cursor;
1232 // simple replacing. The font of the first selected character is used
1233 void LyXText::replaceSelectionWithString(string const & str)
1235 recordUndo(bv(), Undo::ATOMIC);
1238 if (!selection.set()) { // create a dummy selection
1239 selection.end = cursor;
1240 selection.start = cursor;
1243 // Get font setting before we cut
1244 pos_type pos = selection.end.pos();
1245 LyXFont const font = selection.start.par()
1246 ->getFontSettings(bv()->buffer()->params,
1247 selection.start.pos());
1249 // Insert the new string
1250 string::const_iterator cit = str.begin();
1251 string::const_iterator end = str.end();
1252 for (; cit != end; ++cit) {
1253 selection.end.par()->insertChar(pos, (*cit), font);
1257 // Cut the selection
1258 cutSelection(true, false);
1264 // needed to insert the selection
1265 void LyXText::insertStringAsLines(string const & str)
1267 ParagraphList::iterator pit = cursor.par();
1268 pos_type pos = cursor.pos();
1269 ParagraphList::iterator endpit = boost::next(cursor.par());
1271 recordUndo(bv(), Undo::ATOMIC);
1273 // only to be sure, should not be neccessary
1276 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1278 redoParagraphs(cursor.par(), endpit);
1279 setCursor(cursor.par(), cursor.pos());
1280 selection.cursor = cursor;
1281 setCursor(pit, pos);
1286 // turns double-CR to single CR, others where converted into one
1287 // blank. Then InsertStringAsLines is called
1288 void LyXText::insertStringAsParagraphs(string const & str)
1290 string linestr(str);
1291 bool newline_inserted = false;
1292 string::size_type const siz = linestr.length();
1294 for (string::size_type i = 0; i < siz; ++i) {
1295 if (linestr[i] == '\n') {
1296 if (newline_inserted) {
1297 // we know that \r will be ignored by
1298 // InsertStringA. Of course, it is a dirty
1299 // trick, but it works...
1300 linestr[i - 1] = '\r';
1304 newline_inserted = true;
1306 } else if (IsPrintable(linestr[i])) {
1307 newline_inserted = false;
1310 insertStringAsLines(linestr);
1314 bool LyXText::setCursor(ParagraphList::iterator pit,
1316 bool setfont, bool boundary)
1318 LyXCursor old_cursor = cursor;
1319 setCursorIntern(pit, pos, setfont, boundary);
1320 return deleteEmptyParagraphMechanism(old_cursor);
1324 void LyXText::redoCursor()
1326 #warning maybe the same for selections?
1327 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1331 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1332 pos_type pos, bool boundary)
1334 Assert(pit != ownerParagraphs().end());
1338 cur.boundary(boundary);
1342 // get the cursor y position in text
1344 RowList::iterator row = getRow(pit, pos, y);
1345 RowList::iterator old_row = row;
1346 // if we are before the first char of this row and are still in the
1347 // same paragraph and there is a previous row then put the cursor on
1348 // the end of the previous row
1349 cur.iy(y + row->baseline());
1350 if (row != pit->rows.begin()
1352 && pos < pit->size()
1353 && pit->getChar(pos) == Paragraph::META_INSET) {
1354 InsetOld * ins = pit->getInset(pos);
1355 if (ins && (ins->needFullRow() || ins->display())) {
1361 // y is now the beginning of the cursor row
1362 y += row->baseline();
1363 // y is now the cursor baseline
1366 pos_type last = lastPrintablePos(*pit, old_row);
1368 // None of these should happen, but we're scaredy-cats
1369 if (pos > pit->size()) {
1370 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1373 } else if (pos > last + 1) {
1374 lyxerr << "dont like 2 please report" << endl;
1375 // This shouldn't happen.
1378 } else if (pos < row->pos()) {
1379 lyxerr << "dont like 3 please report" << endl;
1384 // now get the cursors x position
1385 float x = getCursorX(pit, row, pos, last, boundary);
1388 if (old_row != row) {
1389 x = getCursorX(pit, old_row, pos, last, boundary);
1393 /* We take out this for the time being because 1) the redraw code is not
1394 prepared to this yet and 2) because some good policy has yet to be decided
1395 while editting: for instance how to act on rows being created/deleted
1399 //if the cursor is in a visible row, anchor to it
1401 if (topy < y && y < topy + bv()->workHeight())
1407 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1408 pos_type pos, pos_type last, bool boundary) const
1410 pos_type cursor_vpos = 0;
1411 double x = rit->x();
1412 double fill_separator = rit->fill_separator();
1413 double fill_hfill = rit->fill_hfill();
1414 double fill_label_hfill = rit->fill_label_hfill();
1415 pos_type const rit_pos = rit->pos();
1418 cursor_vpos = rit_pos;
1419 else if (pos > last && !boundary)
1420 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1421 ? rit_pos : last + 1;
1422 else if (pos > rit_pos && (pos > last || boundary))
1423 // Place cursor after char at (logical) position pos - 1
1424 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1425 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1427 // Place cursor before char at (logical) position pos
1428 cursor_vpos = (bidi_level(pos) % 2 == 0)
1429 ? log2vis(pos) : log2vis(pos) + 1;
1431 pos_type body_pos = pit->beginningOfBody();
1433 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1436 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1437 pos_type pos = vis2log(vpos);
1438 if (body_pos > 0 && pos == body_pos - 1) {
1439 x += fill_label_hfill +
1440 font_metrics::width(
1441 pit->layout()->labelsep, getLabelFont(pit));
1442 if (pit->isLineSeparator(body_pos - 1))
1443 x -= singleWidth(pit, body_pos - 1);
1446 if (hfillExpansion(*pit, rit, pos)) {
1447 x += singleWidth(pit, pos);
1448 if (pos >= body_pos)
1451 x += fill_label_hfill;
1452 } else if (pit->isSeparator(pos)) {
1453 x += singleWidth(pit, pos);
1454 if (pos >= body_pos)
1455 x += fill_separator;
1457 x += singleWidth(pit, pos);
1463 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1464 pos_type pos, bool setfont, bool boundary)
1466 setCursor(cursor, pit, pos, boundary);
1472 void LyXText::setCurrentFont()
1474 pos_type pos = cursor.pos();
1475 ParagraphList::iterator pit = cursor.par();
1477 if (cursor.boundary() && pos > 0)
1481 if (pos == pit->size())
1483 else // potentional bug... BUG (Lgb)
1484 if (pit->isSeparator(pos)) {
1485 if (pos > cursorRow()->pos() &&
1486 bidi_level(pos) % 2 ==
1487 bidi_level(pos - 1) % 2)
1489 else if (pos + 1 < pit->size())
1494 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1495 real_current_font = getFont(pit, pos);
1497 if (cursor.pos() == pit->size() &&
1498 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1499 !cursor.boundary()) {
1500 Language const * lang =
1501 pit->getParLanguage(bv()->buffer()->params);
1502 current_font.setLanguage(lang);
1503 current_font.setNumber(LyXFont::OFF);
1504 real_current_font.setLanguage(lang);
1505 real_current_font.setNumber(LyXFont::OFF);
1510 // returns the column near the specified x-coordinate of the row
1511 // x is set to the real beginning of this column
1512 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1513 RowList::iterator rit, int & x, bool & boundary) const
1515 double tmpx = rit->x();
1516 double fill_separator = rit->fill_separator();
1517 double fill_hfill = rit->fill_hfill();
1518 double fill_label_hfill = rit->fill_label_hfill();
1520 pos_type vc = rit->pos();
1521 pos_type last = lastPrintablePos(*pit, rit);
1523 LyXLayout_ptr const & layout = pit->layout();
1525 bool left_side = false;
1527 pos_type body_pos = pit->beginningOfBody();
1528 double last_tmpx = tmpx;
1531 (body_pos - 1 > last ||
1532 !pit->isLineSeparator(body_pos - 1)))
1535 // check for empty row
1541 while (vc <= last && tmpx <= x) {
1544 if (body_pos > 0 && c == body_pos - 1) {
1545 tmpx += fill_label_hfill +
1546 font_metrics::width(layout->labelsep, getLabelFont(pit));
1547 if (pit->isLineSeparator(body_pos - 1))
1548 tmpx -= singleWidth(pit, body_pos - 1);
1551 if (hfillExpansion(*pit, rit, c)) {
1552 tmpx += singleWidth(pit, c);
1556 tmpx += fill_label_hfill;
1557 } else if (pit->isSeparator(c)) {
1558 tmpx += singleWidth(pit, c);
1560 tmpx += fill_separator;
1562 tmpx += singleWidth(pit, c);
1567 if ((tmpx + last_tmpx) / 2 > x) {
1572 if (vc > last + 1) // This shouldn't happen.
1576 // This (rtl_support test) is not needed, but gives
1577 // some speedup if rtl_support == false
1578 bool const lastrow = lyxrc.rtl_support
1579 && boost::next(rit) == pit->rows.end();
1581 // If lastrow is false, we don't need to compute
1582 // the value of rtl.
1583 bool const rtl = (lastrow)
1584 ? pit->isRightToLeftPar(bv()->buffer()->params)
1587 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1588 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1590 else if (vc == rit->pos()) {
1592 if (bidi_level(c) % 2 == 1)
1595 c = vis2log(vc - 1);
1596 bool const rtl = (bidi_level(c) % 2 == 1);
1597 if (left_side == rtl) {
1599 boundary = isBoundary(bv()->buffer(), *pit, c);
1603 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1604 if (bidi_level(last) % 2 == 0)
1605 tmpx -= singleWidth(pit, last);
1607 tmpx += singleWidth(pit, last);
1617 void LyXText::setCursorFromCoordinates(int x, int y)
1619 LyXCursor old_cursor = cursor;
1620 setCursorFromCoordinates(cursor, x, y);
1622 deleteEmptyParagraphMechanism(old_cursor);
1629 * return true if the cursor given is at the end of a row,
1630 * and the next row is filled by an inset that spans an entire
1633 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1635 RowList::iterator row = lt.getRow(cur);
1636 RowList::iterator next = boost::next(row);
1638 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1641 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1644 InsetOld const * inset = cur.par()->getInset(cur.pos());
1645 if (inset->needFullRow() || inset->display())
1653 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1655 // Get the row first.
1656 ParagraphList::iterator pit;
1657 RowList::iterator rit = getRowNearY(y, pit);
1659 pos_type const column = getColumnNearX(pit, rit, x, bound);
1661 cur.pos(rit->pos() + column);
1663 cur.y(y + rit->baseline());
1665 if (beforeFullRowInset(*this, cur)) {
1666 pos_type const last = lastPrintablePos(*pit, rit);
1667 RowList::iterator next_rit = rit;
1668 ParagraphList::iterator next_pit = pit;
1669 nextRow(next_pit, next_rit);
1670 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1671 cur.iy(y + rit->height() + next_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(), *cursor.par(), cursor.pos() + 1))
1687 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1688 } else if (cursor.par() != ownerParagraphs().begin()) {
1689 // steps into the paragraph above
1690 ParagraphList::iterator pit = boost::prior(cursor.par());
1691 setCursor(pit, pit->size());
1696 void LyXText::cursorRight(bool internal)
1698 bool const at_end = (cursor.pos() == cursor.par()->size());
1699 bool const at_newline = !at_end &&
1700 cursor.par()->isNewline(cursor.pos());
1702 if (!internal && cursor.boundary() && !at_newline)
1703 setCursor(cursor.par(), cursor.pos(), true, false);
1705 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1707 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1708 setCursor(cursor.par(), cursor.pos(), true, true);
1709 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1710 setCursor(boost::next(cursor.par()), 0);
1714 void LyXText::cursorUp(bool selecting)
1717 int x = cursor.x_fix();
1718 int y = cursor.y() - cursorRow()->baseline() - 1;
1719 setCursorFromCoordinates(x, y);
1722 int y1 = cursor.iy() - topy;
1725 InsetOld * inset_hit = checkInsetHit(x, y1);
1726 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1727 inset_hit->localDispatch(
1728 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1732 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1733 cursorRow()->baseline() << endl;
1734 setCursorFromCoordinates(cursor.x_fix(),
1735 cursor.y() - cursorRow()->baseline() - 1);
1740 void LyXText::cursorDown(bool selecting)
1743 int x = cursor.x_fix();
1744 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1745 setCursorFromCoordinates(x, y);
1746 if (!selecting && cursorRow() == cursorIRow()) {
1748 int y1 = cursor.iy() - topy;
1751 InsetOld * inset_hit = checkInsetHit(x, y1);
1752 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1753 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1754 inset_hit->localDispatch(cmd);
1758 setCursorFromCoordinates(cursor.x_fix(),
1759 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1764 void LyXText::cursorUpParagraph()
1766 if (cursor.pos() > 0)
1767 setCursor(cursor.par(), 0);
1768 else if (cursor.par() != ownerParagraphs().begin())
1769 setCursor(boost::prior(cursor.par()), 0);
1773 void LyXText::cursorDownParagraph()
1775 ParagraphList::iterator par = cursor.par();
1776 ParagraphList::iterator next_par = boost::next(par);
1778 if (next_par != ownerParagraphs().end())
1779 setCursor(next_par, 0);
1781 setCursor(par, par->size());
1785 // fix the cursor `cur' after a characters has been deleted at `where'
1786 // position. Called by deleteEmptyParagraphMechanism
1787 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1789 // if cursor is not in the paragraph where the delete occured,
1791 if (cur.par() != where.par())
1794 // if cursor position is after the place where the delete occured,
1796 if (cur.pos() > where.pos())
1797 cur.pos(cur.pos()-1);
1799 // check also if we don't want to set the cursor on a spot behind the
1800 // pagragraph because we erased the last character.
1801 if (cur.pos() > cur.par()->size())
1802 cur.pos(cur.par()->size());
1804 // recompute row et al. for this cursor
1805 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1809 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1811 // Would be wrong to delete anything if we have a selection.
1812 if (selection.set())
1815 // We allow all kinds of "mumbo-jumbo" when freespacing.
1816 if (old_cursor.par()->isFreeSpacing())
1819 /* Ok I'll put some comments here about what is missing.
1820 I have fixed BackSpace (and thus Delete) to not delete
1821 double-spaces automagically. I have also changed Cut,
1822 Copy and Paste to hopefully do some sensible things.
1823 There are still some small problems that can lead to
1824 double spaces stored in the document file or space at
1825 the beginning of paragraphs. This happens if you have
1826 the cursor between to spaces and then save. Or if you
1827 cut and paste and the selection have a space at the
1828 beginning and then save right after the paste. I am
1829 sure none of these are very hard to fix, but I will
1830 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1831 that I can get some feedback. (Lgb)
1834 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1835 // delete the LineSeparator.
1838 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1839 // delete the LineSeparator.
1842 // If the pos around the old_cursor were spaces, delete one of them.
1843 if (old_cursor.par() != cursor.par()
1844 || old_cursor.pos() != cursor.pos()) {
1846 // Only if the cursor has really moved
1847 if (old_cursor.pos() > 0
1848 && old_cursor.pos() < old_cursor.par()->size()
1849 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1850 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1851 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1852 redoParagraph(old_cursor.par());
1856 #ifdef WITH_WARNINGS
1857 #warning This will not work anymore when we have multiple views of the same buffer
1858 // In this case, we will have to correct also the cursors held by
1859 // other bufferviews. It will probably be easier to do that in a more
1860 // automated way in LyXCursor code. (JMarc 26/09/2001)
1862 // correct all cursors held by the LyXText
1863 fixCursorAfterDelete(cursor, old_cursor);
1864 fixCursorAfterDelete(selection.cursor, old_cursor);
1865 fixCursorAfterDelete(selection.start, old_cursor);
1866 fixCursorAfterDelete(selection.end, old_cursor);
1871 // don't delete anything if this is the ONLY paragraph!
1872 if (ownerParagraphs().size() == 1)
1875 // Do not delete empty paragraphs with keepempty set.
1876 if (old_cursor.par()->allowEmpty())
1879 // only do our magic if we changed paragraph
1880 if (old_cursor.par() == cursor.par())
1883 // record if we have deleted a paragraph
1884 // we can't possibly have deleted a paragraph before this point
1885 bool deleted = false;
1887 if (old_cursor.par()->empty() ||
1888 (old_cursor.par()->size() == 1 &&
1889 old_cursor.par()->isLineSeparator(0))) {
1890 // ok, we will delete something
1891 LyXCursor tmpcursor;
1895 bool selection_position_was_oldcursor_position = (
1896 selection.cursor.par() == old_cursor.par()
1897 && selection.cursor.pos() == old_cursor.pos());
1900 cursor = old_cursor; // that undo can restore the right cursor position
1902 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1903 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1906 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1910 ownerParagraphs().erase(old_cursor.par());
1914 setCursorIntern(cursor.par(), cursor.pos());
1916 if (selection_position_was_oldcursor_position) {
1917 // correct selection
1918 selection.cursor = cursor;
1922 if (old_cursor.par()->stripLeadingSpaces()) {
1923 redoParagraph(old_cursor.par());
1925 setCursorIntern(cursor.par(), cursor.pos());
1926 selection.cursor = cursor;
1933 ParagraphList & LyXText::ownerParagraphs() const
1939 bool LyXText::isInInset() const
1941 // Sub-level has non-null bv owner and non-null inset owner.
1942 return inset_owner != 0;
1946 int defaultRowHeight()
1948 LyXFont const font(LyXFont::ALL_SANE);
1949 return int(font_metrics::maxAscent(font)
1950 + font_metrics::maxDescent(font) * 1.5);