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 void LyXText::redoParagraphInternal(ParagraphList::iterator pit)
549 RowList::iterator rit = pit->rows.begin();
550 RowList::iterator end = pit->rows.end();
552 // remove rows of paragraph, keep track of height changes
553 for (int i = 0; rit != end; ++rit, ++i)
554 height -= rit->height();
558 InsetList::iterator ii = pit->insetlist.begin();
559 InsetList::iterator iend = pit->insetlist.end();
560 for (; ii != iend; ++ii) {
562 MetricsInfo mi(bv(), getFont(pit, ii->pos), workWidth());
563 ii->inset->metrics(mi, dim);
566 // rebreak the paragraph
567 for (pos_type z = 0; z < pit->size() + 1; ) {
569 z = rowBreakPoint(pit, row) + 1;
571 pit->rows.push_back(row);
574 // set height and fill of rows
575 for (rit = pit->rows.begin(); rit != end; ++rit) {
576 rit->fill(fill(pit, rit, workWidth()));
577 prepareToPrint(pit, rit);
578 setHeightOfRow(pit, rit);
579 height += rit->height();
582 //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
586 // rebreaks all paragraphs between the specified pars
587 // This function is needed after SetLayout and SetFont etc.
588 void LyXText::redoParagraphs(ParagraphList::iterator start,
589 ParagraphList::iterator end)
591 for ( ; start != end; ++start)
592 redoParagraphInternal(start);
593 updateRowPositions();
597 void LyXText::redoParagraph(ParagraphList::iterator pit)
599 redoParagraphInternal(pit);
600 updateRowPositions();
604 void LyXText::fullRebreak()
606 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
608 selection.cursor = cursor;
612 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
614 //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
615 // << " workWidth: " << workWidth() << endl;
616 //Assert(mi.base.textwidth);
623 redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
626 dim.asc = firstRow()->ascent_of_text();
627 dim.des = height - dim.asc;
628 dim.wid = std::max(mi.base.textwidth, int(width));
632 // important for the screen
635 // the cursor set functions have a special mechanism. When they
636 // realize, that you left an empty paragraph, they will delete it.
637 // They also delete the corresponding row
639 // need the selection cursor:
640 void LyXText::setSelection()
642 TextCursor::setSelection();
647 void LyXText::clearSelection()
649 TextCursor::clearSelection();
651 // reset this in the bv_owner!
652 if (bv_owner && bv_owner->text)
653 bv_owner->text->xsel_cache.set(false);
657 void LyXText::cursorHome()
659 setCursor(cursor.par(), cursorRow()->pos());
663 void LyXText::cursorEnd()
665 if (cursor.par()->empty())
668 RowList::iterator rit = cursorRow();
669 RowList::iterator next_rit = boost::next(rit);
670 RowList::iterator end = boost::next(rit);
671 ParagraphList::iterator pit = cursor.par();
672 pos_type last_pos = lastPos(*pit, rit);
674 if (next_rit == end) {
678 (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
683 setCursor(pit, last_pos);
687 void LyXText::cursorTop()
689 setCursor(ownerParagraphs().begin(), 0);
693 void LyXText::cursorBottom()
695 ParagraphList::iterator lastpit =
696 boost::prior(ownerParagraphs().end());
697 setCursor(lastpit, lastpit->size());
701 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
703 // If the mask is completely neutral, tell user
704 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
705 // Could only happen with user style
706 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
710 // Try implicit word selection
711 // If there is a change in the language the implicit word selection
713 LyXCursor resetCursor = cursor;
714 bool implicitSelection = (font.language() == ignore_language
715 && font.number() == LyXFont::IGNORE)
716 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
719 setFont(font, toggleall);
721 // Implicit selections are cleared afterwards
722 //and cursor is set to the original position.
723 if (implicitSelection) {
725 cursor = resetCursor;
726 setCursor(cursor.par(), cursor.pos());
727 selection.cursor = cursor;
732 string LyXText::getStringToIndex()
734 // Try implicit word selection
735 // If there is a change in the language the implicit word selection
737 LyXCursor const reset_cursor = cursor;
738 bool const implicitSelection =
739 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
742 if (!selection.set())
743 bv()->owner()->message(_("Nothing to index!"));
744 else if (selection.start.par() != selection.end.par())
745 bv()->owner()->message(_("Cannot index more than one paragraph!"));
747 idxstring = selectionAsString(bv()->buffer(), false);
749 // Reset cursors to their original position.
750 cursor = reset_cursor;
751 setCursor(cursor.par(), cursor.pos());
752 selection.cursor = cursor;
754 // Clear the implicit selection.
755 if (implicitSelection)
762 // the DTP switches for paragraphs. LyX will store them in the first
763 // physical paragraph. When a paragraph is broken, the top settings rest,
764 // the bottom settings are given to the new one. So I can make sure,
765 // they do not duplicate themself and you cannnot make dirty things with
768 void LyXText::setParagraph(bool line_top, bool line_bottom,
769 bool pagebreak_top, bool pagebreak_bottom,
770 VSpace const & space_top,
771 VSpace const & space_bottom,
772 Spacing const & spacing,
774 string const & labelwidthstring,
777 LyXCursor tmpcursor = cursor;
778 if (!selection.set()) {
779 selection.start = cursor;
780 selection.end = cursor;
783 // make sure that the depth behind the selection are restored, too
784 ParagraphList::iterator endpit = boost::next(selection.end.par());
785 ParagraphList::iterator undoendpit = endpit;
786 ParagraphList::iterator pars_end = ownerParagraphs().end();
788 if (endpit != pars_end && endpit->getDepth()) {
789 while (endpit != pars_end && endpit->getDepth()) {
793 } else if (endpit != pars_end) {
794 // because of parindents etc.
798 recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
799 boost::prior(undoendpit));
802 ParagraphList::iterator tmppit = selection.end.par();
804 while (tmppit != boost::prior(selection.start.par())) {
805 setCursor(tmppit, 0);
807 ParagraphList::iterator pit = cursor.par();
808 ParagraphParameters & params = pit->params();
810 params.lineTop(line_top);
811 params.lineBottom(line_bottom);
812 params.pagebreakTop(pagebreak_top);
813 params.pagebreakBottom(pagebreak_bottom);
814 params.spaceTop(space_top);
815 params.spaceBottom(space_bottom);
816 params.spacing(spacing);
817 // does the layout allow the new alignment?
818 LyXLayout_ptr const & layout = pit->layout();
820 if (align == LYX_ALIGN_LAYOUT)
821 align = layout->align;
822 if (align & layout->alignpossible) {
823 if (align == layout->align)
824 params.align(LYX_ALIGN_LAYOUT);
828 pit->setLabelWidthString(labelwidthstring);
829 params.noindent(noindent);
830 tmppit = boost::prior(pit);
833 redoParagraphs(selection.start.par(), endpit);
836 setCursor(selection.start.par(), selection.start.pos());
837 selection.cursor = cursor;
838 setCursor(selection.end.par(), selection.end.pos());
840 setCursor(tmpcursor.par(), tmpcursor.pos());
846 // set the counter of a paragraph. This includes the labels
847 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
849 LyXTextClass const & textclass = buf->params.getLyXTextClass();
850 LyXLayout_ptr const & layout = pit->layout();
852 if (pit != ownerParagraphs().begin()) {
854 pit->params().appendix(boost::prior(pit)->params().appendix());
855 if (!pit->params().appendix() &&
856 pit->params().startOfAppendix()) {
857 pit->params().appendix(true);
858 textclass.counters().reset();
860 pit->enumdepth = boost::prior(pit)->enumdepth;
861 pit->itemdepth = boost::prior(pit)->itemdepth;
863 pit->params().appendix(pit->params().startOfAppendix());
868 // Maybe we have to increment the enumeration depth.
869 // BUT, enumeration in a footnote is considered in isolation from its
870 // surrounding paragraph so don't increment if this is the
871 // first line of the footnote
872 // AND, bibliographies can't have their depth changed ie. they
873 // are always of depth 0
874 if (pit != ownerParagraphs().begin()
875 && boost::prior(pit)->getDepth() < pit->getDepth()
876 && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
877 && pit->enumdepth < 3
878 && layout->labeltype != LABEL_BIBLIO) {
882 // Maybe we have to decrement the enumeration depth, see note above
883 if (pit != ownerParagraphs().begin()
884 && boost::prior(pit)->getDepth() > pit->getDepth()
885 && layout->labeltype != LABEL_BIBLIO) {
886 pit->enumdepth = depthHook(pit, ownerParagraphs(),
887 pit->getDepth())->enumdepth;
890 if (!pit->params().labelString().empty()) {
891 pit->params().labelString(string());
894 if (layout->margintype == MARGIN_MANUAL) {
895 if (pit->params().labelWidthString().empty())
896 pit->setLabelWidthString(layout->labelstring());
898 pit->setLabelWidthString(string());
901 // is it a layout that has an automatic label?
902 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
903 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
907 if (i >= 0 && i <= buf->params.secnumdepth) {
911 textclass.counters().step(layout->latexname());
913 // Is there a label? Useful for Chapter layout
914 if (!pit->params().appendix()) {
915 s << buf->B_(layout->labelstring());
917 s << buf->B_(layout->labelstring_appendix());
920 // Use of an integer is here less than elegant. For now.
921 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
922 if (!pit->params().appendix()) {
923 numbertype = "sectioning";
925 numbertype = "appendix";
926 if (pit->isRightToLeftPar(buf->params))
933 << textclass.counters()
934 .numberLabel(layout->latexname(),
935 numbertype, langtype, head);
937 pit->params().labelString(STRCONV(s.str()));
939 // reset enum counters
940 textclass.counters().reset("enum");
941 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
942 textclass.counters().reset("enum");
943 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
945 // Yes I know this is a really, really! bad solution
947 string enumcounter("enum");
949 switch (pit->enumdepth) {
961 // not a valid enumdepth...
965 textclass.counters().step(enumcounter);
967 s << textclass.counters()
968 .numberLabel(enumcounter, "enumeration");
969 pit->params().labelString(STRCONV(s.str()));
971 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
972 textclass.counters().step("bibitem");
973 int number = textclass.counters().value("bibitem");
974 if (pit->bibitem()) {
975 pit->bibitem()->setCounter(number);
976 pit->params().labelString(layout->labelstring());
978 // In biblio should't be following counters but...
980 string s = buf->B_(layout->labelstring());
983 if (layout->labeltype == LABEL_SENSITIVE) {
984 ParagraphList::iterator end = ownerParagraphs().end();
985 ParagraphList::iterator tmppit = pit;
988 while (tmppit != end && tmppit->inInset()
989 // the single '=' is intended below
990 && (in = tmppit->inInset()->owner()))
992 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
993 in->lyxCode() == InsetOld::WRAP_CODE) {
997 tmppit = ownerParagraphs().begin();
998 for ( ; tmppit != end; ++tmppit)
999 if (&*tmppit == in->parOwner())
1007 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1008 type = static_cast<InsetFloat*>(in)->params().type;
1009 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1010 type = static_cast<InsetWrap*>(in)->params().type;
1014 Floating const & fl = textclass.floats().getType(type);
1016 textclass.counters().step(fl.type());
1018 // Doesn't work... yet.
1019 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1021 // par->SetLayout(0);
1022 // s = layout->labelstring;
1023 s = _("Senseless: ");
1026 pit->params().labelString(s);
1028 // reset the enumeration counter. They are always reset
1029 // when there is any other layout between
1030 // Just fall-through between the cases so that all
1031 // enum counters deeper than enumdepth is also reset.
1032 switch (pit->enumdepth) {
1034 textclass.counters().reset("enumi");
1036 textclass.counters().reset("enumii");
1038 textclass.counters().reset("enumiii");
1040 textclass.counters().reset("enumiv");
1046 // Updates all counters. Paragraphs with changed label string will be rebroken
1047 void LyXText::updateCounters()
1050 bv()->buffer()->params.getLyXTextClass().counters().reset();
1052 ParagraphList::iterator beg = ownerParagraphs().begin();
1053 ParagraphList::iterator end = ownerParagraphs().end();
1054 for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
1055 string const oldLabel = pit->params().labelString();
1057 size_t maxdepth = 0;
1059 maxdepth = boost::prior(pit)->getMaxDepthAfter();
1061 if (pit->params().depth() > maxdepth)
1062 pit->params().depth(maxdepth);
1064 // setCounter can potentially change the labelString.
1065 setCounter(bv()->buffer(), pit);
1067 string const & newLabel = pit->params().labelString();
1069 if (oldLabel != newLabel)
1075 void LyXText::insertInset(InsetOld * inset)
1077 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1079 recordUndo(bv(), Undo::ATOMIC, cursor.par());
1081 cursor.par()->insertInset(cursor.pos(), inset);
1082 // Just to rebreak and refresh correctly.
1083 // The character will not be inserted a second time
1084 insertChar(Paragraph::META_INSET);
1085 // If we enter a highly editable inset the cursor should be before
1086 // the inset. After an Undo LyX tries to call inset->edit(...)
1087 // and fails if the cursor is behind the inset and getInset
1088 // does not return the inset!
1089 if (isHighlyEditableInset(inset))
1095 void LyXText::cutSelection(bool doclear, bool realcut)
1097 // Stuff what we got on the clipboard. Even if there is no selection.
1099 // There is a problem with having the stuffing here in that the
1100 // larger the selection the slower LyX will get. This can be
1101 // solved by running the line below only when the selection has
1102 // finished. The solution used currently just works, to make it
1103 // faster we need to be more clever and probably also have more
1104 // calls to stuffClipboard. (Lgb)
1105 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1107 // This doesn't make sense, if there is no selection
1108 if (!selection.set())
1111 // OK, we have a selection. This is always between selection.start
1112 // and selection.end
1114 // make sure that the depth behind the selection are restored, too
1115 ParagraphList::iterator endpit = boost::next(selection.end.par());
1116 ParagraphList::iterator undoendpit = endpit;
1117 ParagraphList::iterator pars_end = ownerParagraphs().end();
1119 if (endpit != pars_end && endpit->getDepth()) {
1120 while (endpit != pars_end && endpit->getDepth()) {
1122 undoendpit = endpit;
1124 } else if (endpit != pars_end) {
1125 // because of parindents etc.
1129 recordUndo(bv(), Undo::DELETE, selection.start.par(),
1130 boost::prior(undoendpit));
1132 endpit = selection.end.par();
1133 int endpos = selection.end.pos();
1135 boost::tie(endpit, endpos) = realcut ?
1136 CutAndPaste::cutSelection(bv()->buffer()->params,
1138 selection.start.par(), endpit,
1139 selection.start.pos(), endpos,
1140 bv()->buffer()->params.textclass,
1142 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1144 selection.start.par(), endpit,
1145 selection.start.pos(), endpos,
1147 // sometimes necessary
1149 selection.start.par()->stripLeadingSpaces();
1151 redoParagraphs(selection.start.par(), boost::next(endpit));
1152 // cutSelection can invalidate the cursor so we need to set
1154 // we prefer the end for when tracking changes
1158 // need a valid cursor. (Lgb)
1161 setCursor(cursor.par(), cursor.pos());
1162 selection.cursor = cursor;
1167 void LyXText::copySelection()
1169 // stuff the selection onto the X clipboard, from an explicit copy request
1170 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1172 // this doesnt make sense, if there is no selection
1173 if (!selection.set())
1176 // ok we have a selection. This is always between selection.start
1177 // and sel_end cursor
1179 // copy behind a space if there is one
1180 while (selection.start.par()->size() > selection.start.pos()
1181 && selection.start.par()->isLineSeparator(selection.start.pos())
1182 && (selection.start.par() != selection.end.par()
1183 || selection.start.pos() < selection.end.pos()))
1184 selection.start.pos(selection.start.pos() + 1);
1186 CutAndPaste::copySelection(selection.start.par(),
1187 selection.end.par(),
1188 selection.start.pos(), selection.end.pos(),
1189 bv()->buffer()->params.textclass);
1193 void LyXText::pasteSelection(size_t sel_index)
1195 // this does not make sense, if there is nothing to paste
1196 if (!CutAndPaste::checkPastePossible())
1199 recordUndo(bv(), Undo::INSERT, cursor.par());
1201 ParagraphList::iterator endpit;
1206 boost::tie(ppp, endpit) =
1207 CutAndPaste::pasteSelection(*bv()->buffer(),
1209 cursor.par(), cursor.pos(),
1210 bv()->buffer()->params.textclass,
1212 bufferErrors(*bv()->buffer(), el);
1213 bv()->showErrorList(_("Paste"));
1215 redoParagraphs(cursor.par(), endpit);
1217 setCursor(cursor.par(), cursor.pos());
1220 selection.cursor = cursor;
1221 setCursor(ppp.first, ppp.second);
1227 void LyXText::setSelectionRange(lyx::pos_type length)
1232 selection.cursor = cursor;
1239 // simple replacing. The font of the first selected character is used
1240 void LyXText::replaceSelectionWithString(string const & str)
1242 recordUndo(bv(), Undo::ATOMIC);
1245 if (!selection.set()) { // create a dummy selection
1246 selection.end = cursor;
1247 selection.start = cursor;
1250 // Get font setting before we cut
1251 pos_type pos = selection.end.pos();
1252 LyXFont const font = selection.start.par()
1253 ->getFontSettings(bv()->buffer()->params,
1254 selection.start.pos());
1256 // Insert the new string
1257 string::const_iterator cit = str.begin();
1258 string::const_iterator end = str.end();
1259 for (; cit != end; ++cit) {
1260 selection.end.par()->insertChar(pos, (*cit), font);
1264 // Cut the selection
1265 cutSelection(true, false);
1271 // needed to insert the selection
1272 void LyXText::insertStringAsLines(string const & str)
1274 ParagraphList::iterator pit = cursor.par();
1275 pos_type pos = cursor.pos();
1276 ParagraphList::iterator endpit = boost::next(cursor.par());
1278 recordUndo(bv(), Undo::ATOMIC);
1280 // only to be sure, should not be neccessary
1283 bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1285 redoParagraphs(cursor.par(), endpit);
1286 setCursor(cursor.par(), cursor.pos());
1287 selection.cursor = cursor;
1288 setCursor(pit, pos);
1293 // turns double-CR to single CR, others where converted into one
1294 // blank. Then InsertStringAsLines is called
1295 void LyXText::insertStringAsParagraphs(string const & str)
1297 string linestr(str);
1298 bool newline_inserted = false;
1299 string::size_type const siz = linestr.length();
1301 for (string::size_type i = 0; i < siz; ++i) {
1302 if (linestr[i] == '\n') {
1303 if (newline_inserted) {
1304 // we know that \r will be ignored by
1305 // InsertStringA. Of course, it is a dirty
1306 // trick, but it works...
1307 linestr[i - 1] = '\r';
1311 newline_inserted = true;
1313 } else if (IsPrintable(linestr[i])) {
1314 newline_inserted = false;
1317 insertStringAsLines(linestr);
1321 bool LyXText::setCursor(ParagraphList::iterator pit,
1323 bool setfont, bool boundary)
1325 LyXCursor old_cursor = cursor;
1326 setCursorIntern(pit, pos, setfont, boundary);
1327 return deleteEmptyParagraphMechanism(old_cursor);
1331 void LyXText::redoCursor()
1333 #warning maybe the same for selections?
1334 setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
1338 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1339 pos_type pos, bool boundary)
1341 Assert(pit != ownerParagraphs().end());
1345 cur.boundary(boundary);
1349 // get the cursor y position in text
1351 RowList::iterator row = getRow(pit, pos);
1354 RowList::iterator old_row = row;
1355 // if we are before the first char of this row and are still in the
1356 // same paragraph and there is a previous row then put the cursor on
1357 // the end of the previous row
1358 cur.iy(y + row->baseline());
1359 if (row != pit->rows.begin()
1361 && pos < pit->size()
1362 && pit->getChar(pos) == Paragraph::META_INSET) {
1363 InsetOld * ins = pit->getInset(pos);
1364 if (ins && (ins->needFullRow() || ins->display())) {
1370 // y is now the beginning of the cursor row
1371 y += row->baseline();
1372 // y is now the cursor baseline
1375 pos_type last = lastPrintablePos(*pit, old_row);
1377 // None of these should happen, but we're scaredy-cats
1378 if (pos > pit->size()) {
1379 lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
1382 } else if (pos > last + 1) {
1383 lyxerr << "dont like 2 please report" << endl;
1384 // This shouldn't happen.
1387 } else if (pos < row->pos()) {
1388 lyxerr << "dont like 3 please report" << endl;
1393 // now get the cursors x position
1394 float x = getCursorX(pit, row, pos, last, boundary);
1397 if (old_row != row) {
1398 x = getCursorX(pit, old_row, pos, last, boundary);
1402 /* We take out this for the time being because 1) the redraw code is not
1403 prepared to this yet and 2) because some good policy has yet to be decided
1404 while editting: for instance how to act on rows being created/deleted
1408 //if the cursor is in a visible row, anchor to it
1410 if (topy < y && y < topy + bv()->workHeight())
1416 float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
1417 pos_type pos, pos_type last, bool boundary) const
1419 pos_type cursor_vpos = 0;
1420 double x = rit->x();
1421 double fill_separator = rit->fill_separator();
1422 double fill_hfill = rit->fill_hfill();
1423 double fill_label_hfill = rit->fill_label_hfill();
1424 pos_type const rit_pos = rit->pos();
1427 cursor_vpos = rit_pos;
1428 else if (pos > last && !boundary)
1429 cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
1430 ? rit_pos : last + 1;
1431 else if (pos > rit_pos && (pos > last || boundary))
1432 // Place cursor after char at (logical) position pos - 1
1433 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1434 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1436 // Place cursor before char at (logical) position pos
1437 cursor_vpos = (bidi_level(pos) % 2 == 0)
1438 ? log2vis(pos) : log2vis(pos) + 1;
1440 pos_type body_pos = pit->beginningOfBody();
1442 (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
1445 for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1446 pos_type pos = vis2log(vpos);
1447 if (body_pos > 0 && pos == body_pos - 1) {
1448 x += fill_label_hfill +
1449 font_metrics::width(
1450 pit->layout()->labelsep, getLabelFont(pit));
1451 if (pit->isLineSeparator(body_pos - 1))
1452 x -= singleWidth(pit, body_pos - 1);
1455 if (hfillExpansion(*pit, rit, pos)) {
1456 x += singleWidth(pit, pos);
1457 if (pos >= body_pos)
1460 x += fill_label_hfill;
1461 } else if (pit->isSeparator(pos)) {
1462 x += singleWidth(pit, pos);
1463 if (pos >= body_pos)
1464 x += fill_separator;
1466 x += singleWidth(pit, pos);
1472 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1473 pos_type pos, bool setfont, bool boundary)
1475 setCursor(cursor, pit, pos, boundary);
1481 void LyXText::setCurrentFont()
1483 pos_type pos = cursor.pos();
1484 ParagraphList::iterator pit = cursor.par();
1486 if (cursor.boundary() && pos > 0)
1490 if (pos == pit->size())
1492 else // potentional bug... BUG (Lgb)
1493 if (pit->isSeparator(pos)) {
1494 if (pos > cursorRow()->pos() &&
1495 bidi_level(pos) % 2 ==
1496 bidi_level(pos - 1) % 2)
1498 else if (pos + 1 < pit->size())
1503 current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1504 real_current_font = getFont(pit, pos);
1506 if (cursor.pos() == pit->size() &&
1507 isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1508 !cursor.boundary()) {
1509 Language const * lang =
1510 pit->getParLanguage(bv()->buffer()->params);
1511 current_font.setLanguage(lang);
1512 current_font.setNumber(LyXFont::OFF);
1513 real_current_font.setLanguage(lang);
1514 real_current_font.setNumber(LyXFont::OFF);
1519 // returns the column near the specified x-coordinate of the row
1520 // x is set to the real beginning of this column
1521 pos_type LyXText::getColumnNearX(ParagraphList::iterator pit,
1522 RowList::iterator rit, int & x, bool & boundary) const
1524 double tmpx = rit->x();
1525 double fill_separator = rit->fill_separator();
1526 double fill_hfill = rit->fill_hfill();
1527 double fill_label_hfill = rit->fill_label_hfill();
1529 pos_type vc = rit->pos();
1530 pos_type last = lastPrintablePos(*pit, rit);
1532 LyXLayout_ptr const & layout = pit->layout();
1534 bool left_side = false;
1536 pos_type body_pos = pit->beginningOfBody();
1537 double last_tmpx = tmpx;
1540 (body_pos - 1 > last ||
1541 !pit->isLineSeparator(body_pos - 1)))
1544 // check for empty row
1550 while (vc <= last && tmpx <= x) {
1553 if (body_pos > 0 && c == body_pos - 1) {
1554 tmpx += fill_label_hfill +
1555 font_metrics::width(layout->labelsep, getLabelFont(pit));
1556 if (pit->isLineSeparator(body_pos - 1))
1557 tmpx -= singleWidth(pit, body_pos - 1);
1560 if (hfillExpansion(*pit, rit, c)) {
1561 tmpx += singleWidth(pit, c);
1565 tmpx += fill_label_hfill;
1566 } else if (pit->isSeparator(c)) {
1567 tmpx += singleWidth(pit, c);
1569 tmpx += fill_separator;
1571 tmpx += singleWidth(pit, c);
1576 if ((tmpx + last_tmpx) / 2 > x) {
1581 if (vc > last + 1) // This shouldn't happen.
1585 // This (rtl_support test) is not needed, but gives
1586 // some speedup if rtl_support == false
1587 bool const lastrow = lyxrc.rtl_support
1588 && boost::next(rit) == pit->rows.end();
1590 // If lastrow is false, we don't need to compute
1591 // the value of rtl.
1592 bool const rtl = (lastrow)
1593 ? pit->isRightToLeftPar(bv()->buffer()->params)
1596 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1597 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1599 else if (vc == rit->pos()) {
1601 if (bidi_level(c) % 2 == 1)
1604 c = vis2log(vc - 1);
1605 bool const rtl = (bidi_level(c) % 2 == 1);
1606 if (left_side == rtl) {
1608 boundary = isBoundary(bv()->buffer(), *pit, c);
1612 if (rit->pos() <= last && c > last && pit->isNewline(last)) {
1613 if (bidi_level(last) % 2 == 0)
1614 tmpx -= singleWidth(pit, last);
1616 tmpx += singleWidth(pit, last);
1626 void LyXText::setCursorFromCoordinates(int x, int y)
1628 LyXCursor old_cursor = cursor;
1629 setCursorFromCoordinates(cursor, x, y);
1631 deleteEmptyParagraphMechanism(old_cursor);
1638 * return true if the cursor given is at the end of a row,
1639 * and the next row is filled by an inset that spans an entire
1642 bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1644 RowList::iterator row = lt.getRow(cur);
1645 RowList::iterator next = boost::next(row);
1647 if (next == cur.par()->rows.end() || next->pos() != cur.pos())
1650 if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
1653 InsetOld const * inset = cur.par()->getInset(cur.pos());
1654 if (inset->needFullRow() || inset->display())
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);
1670 pos_type const column = getColumnNearX(pit, rit, x, bound);
1672 cur.pos(rit->pos() + column);
1674 cur.y(y + rit->baseline());
1676 if (beforeFullRowInset(*this, cur)) {
1677 pos_type const last = lastPrintablePos(*pit, rit);
1678 RowList::iterator next_rit = rit;
1679 ParagraphList::iterator next_pit = pit;
1680 nextRow(next_pit, next_rit);
1681 cur.ix(int(getCursorX(pit, next_rit, cur.pos(), last, bound)));
1682 cur.iy(y + rit->height() + next_rit->baseline());
1687 cur.boundary(bound);
1691 void LyXText::cursorLeft(bool internal)
1693 if (cursor.pos() > 0) {
1694 bool boundary = cursor.boundary();
1695 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1696 if (!internal && !boundary &&
1697 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1698 setCursor(cursor.par(), cursor.pos() + 1, true, true);
1699 } else if (cursor.par() != ownerParagraphs().begin()) {
1700 // steps into the paragraph above
1701 ParagraphList::iterator pit = boost::prior(cursor.par());
1702 setCursor(pit, pit->size());
1707 void LyXText::cursorRight(bool internal)
1709 bool const at_end = (cursor.pos() == cursor.par()->size());
1710 bool const at_newline = !at_end &&
1711 cursor.par()->isNewline(cursor.pos());
1713 if (!internal && cursor.boundary() && !at_newline)
1714 setCursor(cursor.par(), cursor.pos(), true, false);
1716 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1718 isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1719 setCursor(cursor.par(), cursor.pos(), true, true);
1720 } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1721 setCursor(boost::next(cursor.par()), 0);
1725 void LyXText::cursorUp(bool selecting)
1728 int x = cursor.x_fix();
1729 int y = cursor.y() - cursorRow()->baseline() - 1;
1730 setCursorFromCoordinates(x, y);
1733 int y1 = cursor.iy() - topy;
1736 InsetOld * inset_hit = checkInsetHit(x, y1);
1737 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1738 inset_hit->localDispatch(
1739 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1743 lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
1744 cursorRow()->baseline() << endl;
1745 setCursorFromCoordinates(cursor.x_fix(),
1746 cursor.y() - cursorRow()->baseline() - 1);
1751 void LyXText::cursorDown(bool selecting)
1754 int x = cursor.x_fix();
1755 int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1756 setCursorFromCoordinates(x, y);
1757 if (!selecting && cursorRow() == cursorIRow()) {
1759 int y1 = cursor.iy() - topy;
1762 InsetOld * inset_hit = checkInsetHit(x, y1);
1763 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1764 FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1765 inset_hit->localDispatch(cmd);
1769 setCursorFromCoordinates(cursor.x_fix(),
1770 cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
1775 void LyXText::cursorUpParagraph()
1777 if (cursor.pos() > 0)
1778 setCursor(cursor.par(), 0);
1779 else if (cursor.par() != ownerParagraphs().begin())
1780 setCursor(boost::prior(cursor.par()), 0);
1784 void LyXText::cursorDownParagraph()
1786 ParagraphList::iterator par = cursor.par();
1787 ParagraphList::iterator next_par = boost::next(par);
1789 if (next_par != ownerParagraphs().end())
1790 setCursor(next_par, 0);
1792 setCursor(par, par->size());
1796 // fix the cursor `cur' after a characters has been deleted at `where'
1797 // position. Called by deleteEmptyParagraphMechanism
1798 void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
1800 // if cursor is not in the paragraph where the delete occured,
1802 if (cur.par() != where.par())
1805 // if cursor position is after the place where the delete occured,
1807 if (cur.pos() > where.pos())
1808 cur.pos(cur.pos()-1);
1810 // check also if we don't want to set the cursor on a spot behind the
1811 // pagragraph because we erased the last character.
1812 if (cur.pos() > cur.par()->size())
1813 cur.pos(cur.par()->size());
1815 // recompute row et al. for this cursor
1816 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1820 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1822 // Would be wrong to delete anything if we have a selection.
1823 if (selection.set())
1826 // We allow all kinds of "mumbo-jumbo" when freespacing.
1827 if (old_cursor.par()->isFreeSpacing())
1830 /* Ok I'll put some comments here about what is missing.
1831 I have fixed BackSpace (and thus Delete) to not delete
1832 double-spaces automagically. I have also changed Cut,
1833 Copy and Paste to hopefully do some sensible things.
1834 There are still some small problems that can lead to
1835 double spaces stored in the document file or space at
1836 the beginning of paragraphs. This happens if you have
1837 the cursor between to spaces and then save. Or if you
1838 cut and paste and the selection have a space at the
1839 beginning and then save right after the paste. I am
1840 sure none of these are very hard to fix, but I will
1841 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1842 that I can get some feedback. (Lgb)
1845 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1846 // delete the LineSeparator.
1849 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1850 // delete the LineSeparator.
1853 // If the pos around the old_cursor were spaces, delete one of them.
1854 if (old_cursor.par() != cursor.par()
1855 || old_cursor.pos() != cursor.pos()) {
1857 // Only if the cursor has really moved
1858 if (old_cursor.pos() > 0
1859 && old_cursor.pos() < old_cursor.par()->size()
1860 && old_cursor.par()->isLineSeparator(old_cursor.pos())
1861 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
1862 bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
1863 redoParagraph(old_cursor.par());
1867 #ifdef WITH_WARNINGS
1868 #warning This will not work anymore when we have multiple views of the same buffer
1869 // In this case, we will have to correct also the cursors held by
1870 // other bufferviews. It will probably be easier to do that in a more
1871 // automated way in LyXCursor code. (JMarc 26/09/2001)
1873 // correct all cursors held by the LyXText
1874 fixCursorAfterDelete(cursor, old_cursor);
1875 fixCursorAfterDelete(selection.cursor, old_cursor);
1876 fixCursorAfterDelete(selection.start, old_cursor);
1877 fixCursorAfterDelete(selection.end, old_cursor);
1882 // don't delete anything if this is the ONLY paragraph!
1883 if (ownerParagraphs().size() == 1)
1886 // Do not delete empty paragraphs with keepempty set.
1887 if (old_cursor.par()->allowEmpty())
1890 // only do our magic if we changed paragraph
1891 if (old_cursor.par() == cursor.par())
1894 // record if we have deleted a paragraph
1895 // we can't possibly have deleted a paragraph before this point
1896 bool deleted = false;
1898 if (old_cursor.par()->empty() ||
1899 (old_cursor.par()->size() == 1 &&
1900 old_cursor.par()->isLineSeparator(0))) {
1901 // ok, we will delete something
1902 LyXCursor tmpcursor;
1906 bool selection_position_was_oldcursor_position = (
1907 selection.cursor.par() == old_cursor.par()
1908 && selection.cursor.pos() == old_cursor.pos());
1911 cursor = old_cursor; // that undo can restore the right cursor position
1913 ParagraphList::iterator endpit = boost::next(old_cursor.par());
1914 while (endpit != ownerParagraphs().end() && endpit->getDepth())
1917 recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
1921 ownerParagraphs().erase(old_cursor.par());
1925 setCursorIntern(cursor.par(), cursor.pos());
1927 if (selection_position_was_oldcursor_position) {
1928 // correct selection
1929 selection.cursor = cursor;
1933 if (old_cursor.par()->stripLeadingSpaces()) {
1934 redoParagraph(old_cursor.par());
1936 setCursorIntern(cursor.par(), cursor.pos());
1937 selection.cursor = cursor;
1944 ParagraphList & LyXText::ownerParagraphs() const
1950 bool LyXText::isInInset() const
1952 // Sub-level has non-null bv owner and non-null inset owner.
1953 return inset_owner != 0;
1957 int defaultRowHeight()
1959 LyXFont const font(LyXFont::ALL_SANE);
1960 return int(font_metrics::maxAscent(font)
1961 + font_metrics::maxDescent(font) * 1.5);