1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
15 #include "paragraph.h"
16 #include "frontends/LyXView.h"
17 #include "undo_funcs.h"
19 #include "bufferparams.h"
21 #include "BufferView.h"
22 #include "CutAndPaste.h"
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
28 #include "FloatList.h"
30 #include "ParagraphParameters.h"
33 #include "insets/inseterror.h"
34 #include "insets/insetbibitem.h"
35 #include "insets/insetspecialchar.h"
36 #include "insets/insettext.h"
37 #include "insets/insetfloat.h"
38 #include "insets/insetwrap.h"
40 #include "support/LAssert.h"
41 #include "support/textutils.h"
42 #include "support/lstrings.h"
44 #include "support/BoostFormat.h"
54 LyXText::LyXText(BufferView * bv)
55 : height(0), width(0), anchor_row_offset_(0),
56 inset_owner(0), the_locking_inset(0), bv_owner(bv)
58 anchor_row_ = rows().end();
59 need_break_row = rows().end();
60 refresh_row = rows().end();
65 LyXText::LyXText(BufferView * bv, InsetText * inset)
66 : height(0), width(0), anchor_row_offset_(0),
67 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
69 anchor_row_ = rows().end();
70 need_break_row = rows().end();
71 refresh_row = rows().end();
76 void LyXText::init(BufferView * bview, bool reinit)
80 need_break_row = rows().end();
82 copylayouttype.erase();
85 } else if (!rowlist_.empty())
88 Paragraph * par = ownerParagraph();
89 current_font = getFont(bview->buffer(), par, 0);
92 insertParagraph(par, rowlist_.end());
95 setCursorIntern(rowlist_.begin()->par(), 0);
96 selection.cursor = cursor;
104 LyXFont const realizeFont(LyXFont const & font,
108 LyXTextClass const & tclass = buf->params.getLyXTextClass();
109 LyXFont tmpfont(font);
110 Paragraph::depth_type par_depth = par->getDepth();
112 // Resolve against environment font information
113 while (par && par_depth && !tmpfont.resolved()) {
114 par = par->outerHook();
116 tmpfont.realize(par->layout()->font);
117 par_depth = par->getDepth();
121 tmpfont.realize(tclass.defaultfont());
129 // Gets the fully instantiated font at a given position in a paragraph
130 // Basically the same routine as Paragraph::getFont() in paragraph.C.
131 // The difference is that this one is used for displaying, and thus we
132 // are allowed to make cosmetic improvements. For instance make footnotes
134 // If position is -1, we get the layout font of the paragraph.
135 // If position is -2, we get the font of the manual label of the paragraph.
136 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
139 lyx::Assert(pos >= 0);
141 LyXLayout_ptr const & layout = par->layout();
143 // We specialize the 95% common case:
144 if (!par->getDepth()) {
145 if (layout->labeltype == LABEL_MANUAL
146 && pos < par->beginningOfBody()) {
148 LyXFont f = par->getFontSettings(buf->params, pos);
150 par->inInset()->getDrawFont(f);
151 return f.realize(layout->reslabelfont);
153 LyXFont f = par->getFontSettings(buf->params, pos);
155 par->inInset()->getDrawFont(f);
156 return f.realize(layout->resfont);
160 // The uncommon case need not be optimized as much
164 if (pos < par->beginningOfBody()) {
166 layoutfont = layout->labelfont;
169 layoutfont = layout->font;
172 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
173 tmpfont.realize(layoutfont);
176 par->inInset()->getDrawFont(tmpfont);
178 return realizeFont(tmpfont, buf, par);
182 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
184 LyXLayout_ptr const & layout = par->layout();
186 if (!par->getDepth()) {
187 return layout->resfont;
190 return realizeFont(layout->font, buf, par);
194 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
196 LyXLayout_ptr const & layout = par->layout();
198 if (!par->getDepth()) {
199 return layout->reslabelfont;
202 return realizeFont(layout->labelfont, buf, par);
206 void LyXText::setCharFont(Paragraph * par,
207 pos_type pos, LyXFont const & fnt,
210 Buffer const * buf = bv()->buffer();
211 LyXFont font = getFont(buf, par, pos);
212 font.update(fnt, buf->params.language, toggleall);
213 // Let the insets convert their font
214 if (par->isInset(pos)) {
215 Inset * inset = par->getInset(pos);
216 if (isEditableInset(inset)) {
217 UpdatableInset * uinset =
218 static_cast<UpdatableInset *>(inset);
219 uinset->setFont(bv(), fnt, toggleall, true);
223 // Plug thru to version below:
224 setCharFont(buf, par, pos, font);
228 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
229 pos_type pos, LyXFont const & fnt)
233 LyXTextClass const & tclass = buf->params.getLyXTextClass();
234 LyXLayout_ptr const & layout = par->layout();
236 // Get concrete layout font to reduce against
239 if (pos < par->beginningOfBody())
240 layoutfont = layout->labelfont;
242 layoutfont = layout->font;
244 // Realize against environment font information
245 if (par->getDepth()) {
246 Paragraph * tp = par;
247 while (!layoutfont.resolved() && tp && tp->getDepth()) {
248 tp = tp->outerHook();
250 layoutfont.realize(tp->layout()->font);
254 layoutfont.realize(tclass.defaultfont());
256 // Now, reduce font against full layout font
257 font.reduce(layoutfont);
259 par->setFont(pos, font);
263 // removes the row and reset the touched counters
264 void LyXText::removeRow(RowList::iterator rit)
266 /* FIXME: when we cache the bview, this should just
267 * become a postPaint(), I think */
268 if (refresh_row == rit) {
269 if (rit == rows().begin())
270 refresh_row = boost::next(rit);
272 refresh_row = boost::prior(rit);
274 // what about refresh_y
277 if (anchor_row_ == rit) {
278 if (rit != rows().begin()) {
279 anchor_row_ = boost::prior(rit);
280 anchor_row_offset_ += boost::prior(rit)->height();
282 anchor_row_ = boost::next(rit);
283 anchor_row_offset_ -= rit->height();
287 // the text becomes smaller
288 height -= rit->height();
294 // remove all following rows of the paragraph of the specified row.
295 void LyXText::removeParagraph(RowList::iterator rit)
297 Paragraph * tmppar = rit->par();
300 while (rit != rows().end() && rit->par() == tmppar) {
301 RowList::iterator tmprit = boost::next(rit);
308 void LyXText::insertParagraph(Paragraph * par, RowList::iterator rowit)
310 // insert a new row, starting at position 0
311 RowList::iterator rit = rowlist_.insert(rowit, new Row(par, 0));
313 // and now append the whole paragraph before the new row
314 appendParagraph(rit);
318 Inset * LyXText::getInset() const
320 if (cursor.pos() < cursor.par()->size()
321 && cursor.par()->isInset(cursor.pos())) {
322 return cursor.par()->getInset(cursor.pos());
328 void LyXText::toggleInset()
330 Inset * inset = getInset();
331 // is there an editable inset at cursor position?
332 if (!isEditableInset(inset)) {
333 // No, try to see if we are inside a collapsable inset
334 if (inset_owner && inset_owner->owner()
335 && inset_owner->owner()->isOpen()) {
336 bv()->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
337 inset_owner->owner()->close(bv());
338 bv()->getLyXText()->cursorRight(bv());
342 //bv()->owner()->message(inset->editMessage());
344 // do we want to keep this?? (JMarc)
345 if (!isHighlyEditableInset(inset))
346 setCursorParUndo(bv());
348 if (inset->isOpen()) {
354 bv()->updateInset(inset);
358 /* used in setlayout */
359 // Asger is not sure we want to do this...
360 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
363 LyXLayout_ptr const & layout = par.layout();
366 for (pos_type pos = 0; pos < par.size(); ++pos) {
367 if (pos < par.beginningOfBody())
368 layoutfont = layout->labelfont;
370 layoutfont = layout->font;
372 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
373 tmpfont.reduce(layoutfont);
374 par.setFont(pos, tmpfont);
379 Paragraph * LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
380 LyXCursor & send_cur,
381 string const & layout)
383 Paragraph * endpar = send_cur.par()->next();
384 Paragraph * undoendpar = endpar;
386 if (endpar && endpar->getDepth()) {
387 while (endpar && endpar->getDepth()) {
388 endpar = endpar->next();
392 endpar = endpar->next(); // because of parindents etc.
395 setUndo(bv(), Undo::EDIT, sstart_cur.par(), undoendpar);
397 // ok we have a selection. This is always between sstart_cur
398 // and sel_end cursor
400 Paragraph * par = sstart_cur.par();
401 Paragraph * epar = send_cur.par()->next();
403 LyXLayout_ptr const & lyxlayout =
404 bv()->buffer()->params.getLyXTextClass()[layout];
407 par->applyLayout(lyxlayout);
408 makeFontEntriesLayoutSpecific(*bv()->buffer(), *par);
409 Paragraph * fppar = par;
410 fppar->params().spaceTop(lyxlayout->fill_top ?
411 VSpace(VSpace::VFILL)
412 : VSpace(VSpace::NONE));
413 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
414 VSpace(VSpace::VFILL)
415 : VSpace(VSpace::NONE));
416 if (lyxlayout->margintype == MARGIN_MANUAL)
417 par->setLabelWidthString(lyxlayout->labelstring());
420 } while (par != epar);
426 // set layout over selection and make a total rebreak of those paragraphs
427 void LyXText::setLayout(string const & layout)
429 LyXCursor tmpcursor = cursor; /* store the current cursor */
431 // if there is no selection just set the layout
432 // of the current paragraph */
433 if (!selection.set()) {
434 selection.start = cursor; // dummy selection
435 selection.end = cursor;
437 Paragraph * endpar = setLayout(cursor, selection.start,
438 selection.end, layout);
439 redoParagraphs(selection.start, endpar);
441 // we have to reset the selection, because the
442 // geometry could have changed
443 setCursor(selection.start.par(),
444 selection.start.pos(), false);
445 selection.cursor = cursor;
446 setCursor(selection.end.par(), selection.end.pos(), false);
450 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
454 // increment depth over selection and
455 // make a total rebreak of those paragraphs
456 void LyXText::incDepth()
458 // If there is no selection, just use the current paragraph
459 if (!selection.set()) {
460 selection.start = cursor; // dummy selection
461 selection.end = cursor;
464 // We end at the next paragraph with depth 0
465 Paragraph * endpar = selection.end.par()->next();
467 Paragraph * undoendpar = endpar;
469 if (endpar && endpar->getDepth()) {
470 while (endpar && endpar->getDepth()) {
471 endpar = endpar->next();
475 endpar = endpar->next(); // because of parindents etc.
478 setUndo(bv(), Undo::EDIT,
479 selection.start.par(), undoendpar);
481 LyXCursor tmpcursor = cursor; // store the current cursor
483 // ok we have a selection. This is always between sel_start_cursor
484 // and sel_end cursor
485 cursor = selection.start;
488 // NOTE: you can't change the depth of a bibliography entry
489 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
490 Paragraph * prev = cursor.par()->previous();
493 if (cursor.par()->getDepth()
494 < prev->getMaxDepthAfter()) {
495 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
499 if (cursor.par() == selection.end.par())
501 cursor.par(cursor.par()->next());
504 redoParagraphs(selection.start, endpar);
506 // we have to reset visual the selection because the
507 // geometry could have changed
508 setCursor(selection.start.par(), selection.start.pos());
509 selection.cursor = cursor;
510 setCursor(selection.end.par(), selection.end.pos());
513 setCursor(tmpcursor.par(), tmpcursor.pos());
517 // decrement depth over selection and
518 // make a total rebreak of those paragraphs
519 void LyXText::decDepth()
521 // if there is no selection just set the layout
522 // of the current paragraph
523 if (!selection.set()) {
524 selection.start = cursor; // dummy selection
525 selection.end = cursor;
527 Paragraph * endpar = selection.end.par()->next();
528 Paragraph * undoendpar = endpar;
530 if (endpar && endpar->getDepth()) {
531 while (endpar && endpar->getDepth()) {
532 endpar = endpar->next();
536 endpar = endpar->next(); // because of parindents etc.
539 setUndo(bv(), Undo::EDIT,
540 selection.start.par(), undoendpar);
542 LyXCursor tmpcursor = cursor; // store the current cursor
544 // ok we have a selection. This is always between sel_start_cursor
545 // and sel_end cursor
546 cursor = selection.start;
549 if (cursor.par()->params().depth()) {
550 cursor.par()->params()
551 .depth(cursor.par()->params().depth() - 1);
553 if (cursor.par() == selection.end.par()) {
556 cursor.par(cursor.par()->next());
559 redoParagraphs(selection.start, endpar);
561 // we have to reset the visual selection because the
562 // geometry could have changed
563 setCursor(selection.start.par(), selection.start.pos());
564 selection.cursor = cursor;
565 setCursor(selection.end.par(), selection.end.pos());
568 setCursor(tmpcursor.par(), tmpcursor.pos());
572 // set font over selection and make a total rebreak of those paragraphs
573 void LyXText::setFont(LyXFont const & font, bool toggleall)
575 // if there is no selection just set the current_font
576 if (!selection.set()) {
577 // Determine basis font
579 if (cursor.pos() < cursor.par()->beginningOfBody()) {
580 layoutfont = getLabelFont(bv()->buffer(),
583 layoutfont = getLayoutFont(bv()->buffer(),
586 // Update current font
587 real_current_font.update(font,
588 bv()->buffer()->params.language,
591 // Reduce to implicit settings
592 current_font = real_current_font;
593 current_font.reduce(layoutfont);
594 // And resolve it completely
595 real_current_font.realize(layoutfont);
600 LyXCursor tmpcursor = cursor; // store the current cursor
602 // ok we have a selection. This is always between sel_start_cursor
603 // and sel_end cursor
605 setUndo(bv(), Undo::EDIT,
606 selection.start.par(), selection.end.par()->next());
608 cursor = selection.start;
609 while (cursor.par() != selection.end.par() ||
610 cursor.pos() < selection.end.pos())
612 if (cursor.pos() < cursor.par()->size()) {
613 // an open footnote should behave like a closed one
614 setCharFont(cursor.par(), cursor.pos(),
616 cursor.pos(cursor.pos() + 1);
619 cursor.par(cursor.par()->next());
624 redoParagraphs(selection.start, selection.end.par()->next());
626 // we have to reset the selection, because the
627 // geometry could have changed, but we keep
628 // it for user convenience
629 setCursor(selection.start.par(), selection.start.pos());
630 selection.cursor = cursor;
631 setCursor(selection.end.par(), selection.end.pos());
633 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
634 tmpcursor.boundary());
638 void LyXText::redoHeightOfParagraph()
640 Row * tmprow = cursor.row();
641 int y = cursor.y() - tmprow->baseline();
643 setHeightOfRow(tmprow);
645 while (tmprow->previous()
646 && tmprow->previous()->par() == tmprow->par()) {
647 tmprow = tmprow->previous();
648 y -= tmprow->height();
649 setHeightOfRow(tmprow);
654 setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
658 void LyXText::redoDrawingOfParagraph(LyXCursor const & cur)
660 Row * tmprow = cur.row();
662 int y = cur.y() - tmprow->baseline();
663 setHeightOfRow(tmprow);
665 while (tmprow->previous()
666 && tmprow->previous()->par() == tmprow->par()) {
667 tmprow = tmprow->previous();
668 y -= tmprow->height();
672 setCursor(cur.par(), cur.pos());
676 // deletes and inserts again all paragaphs between the cursor
677 // and the specified par
678 // This function is needed after SetLayout and SetFont etc.
679 void LyXText::redoParagraphs(LyXCursor const & cur,
680 Paragraph const * endpar)
682 RowList::iterator tmprit = cur.row();
684 int y = cur.y() - tmprit->baseline();
686 Paragraph * first_phys_par;
687 if (tmprit == rows().begin()) {
688 // A trick/hack for UNDO.
689 // This is needed because in an UNDO/REDO we could have
690 // changed the ownerParagrah() so the paragraph inside
691 // the row is NOT my really first par anymore.
692 // Got it Lars ;) (Jug 20011206)
693 first_phys_par = ownerParagraph();
695 // In here prevrit could be set to rows().end(). (Lgb)
697 first_phys_par = tmprit->par();
698 while (tmprit != rows().begin()
699 && boost::prior(tmprit)->par() == first_phys_par)
702 y -= tmprit->height();
705 // Is it possible to put the prevrit setting in here? (Lgb)
708 RowList::iterator prevrit;
709 bool good_prevrit = false;
711 // It seems to mee that good_prevrit is not needed if we let
712 // a bad prevrit have the value rows().end() (Lgb)
713 if (tmprit != rows().begin()) {
714 prevrit = boost::prior(tmprit);
719 while (tmprit != rows().end() && tmprit->par() != endpar) {
720 RowList::iterator tmprit2 = tmprit++;
724 // Reinsert the paragraphs.
725 Paragraph * tmppar = first_phys_par;
727 // See if this loop can be rewritten as a while loop instead.
728 // That should also make the code a bit easier to read. (Lgb)
731 insertParagraph(tmppar, tmprit);
732 while (tmprit != rows().end()
733 && tmprit->par() == tmppar) {
736 tmppar = tmppar->next();
738 } while (tmppar && tmppar != endpar);
741 // If the above changes are done, then we can compare prevrit
742 // with rows().end() here. (Lgb)
744 setHeightOfRow(prevrit);
745 const_cast<LyXText *>(this)->postPaint(y - prevrit->height());
747 setHeightOfRow(rows().begin());
748 const_cast<LyXText *>(this)->postPaint(0);
750 if (tmprit != rows().end())
751 setHeightOfRow(tmprit);
756 void LyXText::fullRebreak()
758 if (rows().empty()) {
762 if (need_break_row != rows().end()) {
763 breakAgain(need_break_row);
764 need_break_row = rows().end();
770 // important for the screen
773 // the cursor set functions have a special mechanism. When they
774 // realize, that you left an empty paragraph, they will delete it.
775 // They also delete the corresponding row
777 // need the selection cursor:
778 void LyXText::setSelection()
780 bool const lsel = selection.set();
782 if (!selection.set()) {
783 last_sel_cursor = selection.cursor;
784 selection.start = selection.cursor;
785 selection.end = selection.cursor;
790 // first the toggling area
791 if (cursor.y() < last_sel_cursor.y()
792 || (cursor.y() == last_sel_cursor.y()
793 && cursor.x() < last_sel_cursor.x())) {
794 toggle_end_cursor = last_sel_cursor;
795 toggle_cursor = cursor;
797 toggle_end_cursor = cursor;
798 toggle_cursor = last_sel_cursor;
801 last_sel_cursor = cursor;
803 // and now the whole selection
805 if (selection.cursor.par() == cursor.par())
806 if (selection.cursor.pos() < cursor.pos()) {
807 selection.end = cursor;
808 selection.start = selection.cursor;
810 selection.end = selection.cursor;
811 selection.start = cursor;
813 else if (selection.cursor.y() < cursor.y() ||
814 (selection.cursor.y() == cursor.y()
815 && selection.cursor.x() < cursor.x())) {
816 selection.end = cursor;
817 selection.start = selection.cursor;
820 selection.end = selection.cursor;
821 selection.start = cursor;
824 // a selection with no contents is not a selection
825 if (selection.start.par() == selection.end.par() &&
826 selection.start.pos() == selection.end.pos())
827 selection.set(false);
829 if (inset_owner && (selection.set() || lsel))
830 inset_owner->setUpdateStatus(bv(), InsetText::SELECTION);
834 string const LyXText::selectionAsString(Buffer const * buffer,
837 if (!selection.set()) return string();
839 // should be const ...
840 Paragraph * startpar(selection.start.par());
841 Paragraph * endpar(selection.end.par());
842 pos_type const startpos(selection.start.pos());
843 pos_type const endpos(selection.end.pos());
845 if (startpar == endpar) {
846 return startpar->asString(buffer, startpos, endpos, label);
851 // First paragraph in selection
852 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
854 // The paragraphs in between (if any)
855 LyXCursor tmpcur(selection.start);
856 tmpcur.par(tmpcur.par()->next());
857 while (tmpcur.par() != endpar) {
858 result += tmpcur.par()->asString(buffer, 0,
859 tmpcur.par()->size(),
861 tmpcur.par(tmpcur.par()->next());
864 // Last paragraph in selection
865 result += endpar->asString(buffer, 0, endpos, label);
871 void LyXText::clearSelection()
873 selection.set(false);
874 selection.mark(false);
875 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
876 // reset this in the bv_owner!
877 if (bv_owner && bv_owner->text)
878 bv_owner->text->xsel_cache.set(false);
882 void LyXText::cursorHome()
884 setCursor(cursor.par(), cursor.row()->pos());
888 void LyXText::cursorEnd()
890 if (cursor.par()->empty())
893 if (!cursor.row()->next()
894 || cursor.row()->next()->par() != cursor.row()->par()) {
895 setCursor(cursor.par(), cursor.row()->lastPos() + 1);
897 if (!cursor.par()->empty() &&
898 (cursor.par()->getChar(cursor.row()->lastPos()) == ' '
899 || cursor.par()->isNewline(cursor.row()->lastPos()))) {
900 setCursor(cursor.par(), cursor.row()->lastPos());
902 setCursor(cursor.par(),
903 cursor.row()->lastPos() + 1);
909 void LyXText::cursorTop()
911 while (cursor.par()->previous())
912 cursor.par(cursor.par()->previous());
913 setCursor(cursor.par(), 0);
917 void LyXText::cursorBottom()
919 while (cursor.par()->next())
920 cursor.par(cursor.par()->next());
921 setCursor(cursor.par(), cursor.par()->size());
925 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
927 // If the mask is completely neutral, tell user
928 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
929 // Could only happen with user style
930 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
934 // Try implicit word selection
935 // If there is a change in the language the implicit word selection
937 LyXCursor resetCursor = cursor;
938 bool implicitSelection = (font.language() == ignore_language
939 && font.number() == LyXFont::IGNORE)
940 ? selectWordWhenUnderCursor(WHOLE_WORD_STRICT) : false;
943 setFont(font, toggleall);
945 // Implicit selections are cleared afterwards
946 //and cursor is set to the original position.
947 if (implicitSelection) {
949 cursor = resetCursor;
950 setCursor(cursor.par(), cursor.pos());
951 selection.cursor = cursor;
954 inset_owner->setUpdateStatus(bv(), InsetText::CURSOR_PAR);
958 string LyXText::getStringToIndex()
960 // Try implicit word selection
961 // If there is a change in the language the implicit word selection
963 LyXCursor const reset_cursor = cursor;
964 bool const implicitSelection = selectWordWhenUnderCursor(PREVIOUS_WORD);
967 if (!selection.set())
968 bv()->owner()->message(_("Nothing to index!"));
969 else if (selection.start.par() != selection.end.par())
970 bv()->owner()->message(_("Cannot index more than one paragraph!"));
972 idxstring = selectionAsString(bv()->buffer(), false);
974 // Reset cursors to their original position.
975 cursor = reset_cursor;
976 setCursor(cursor.par(), cursor.pos());
977 selection.cursor = cursor;
979 // Clear the implicit selection.
980 if (implicitSelection)
987 // the DTP switches for paragraphs. LyX will store them in the first
988 // physicla paragraph. When a paragraph is broken, the top settings rest,
989 // the bottom settings are given to the new one. So I can make shure,
990 // they do not duplicate themself and you cannnot make dirty things with
993 void LyXText::setParagraph(bool line_top, bool line_bottom,
994 bool pagebreak_top, bool pagebreak_bottom,
995 VSpace const & space_top,
996 VSpace const & space_bottom,
997 Spacing const & spacing,
999 string const & labelwidthstring,
1002 LyXCursor tmpcursor = cursor;
1003 if (!selection.set()) {
1004 selection.start = cursor;
1005 selection.end = cursor;
1008 // make sure that the depth behind the selection are restored, too
1009 Paragraph * endpar = selection.end.par()->next();
1010 Paragraph * undoendpar = endpar;
1012 if (endpar && endpar->getDepth()) {
1013 while (endpar && endpar->getDepth()) {
1014 endpar = endpar->next();
1015 undoendpar = endpar;
1019 // because of parindents etc.
1020 endpar = endpar->next();
1023 setUndo(bv(), Undo::EDIT, selection.start.par(), undoendpar);
1026 Paragraph * tmppar = selection.end.par();
1028 while (tmppar != selection.start.par()->previous()) {
1029 setCursor(tmppar, 0);
1030 postPaint(cursor.y() - cursor.row()->baseline());
1031 cursor.par()->params().lineTop(line_top);
1032 cursor.par()->params().lineBottom(line_bottom);
1033 cursor.par()->params().pagebreakTop(pagebreak_top);
1034 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1035 cursor.par()->params().spaceTop(space_top);
1036 cursor.par()->params().spaceBottom(space_bottom);
1037 cursor.par()->params().spacing(spacing);
1038 // does the layout allow the new alignment?
1039 LyXLayout_ptr const & layout = cursor.par()->layout();
1041 if (align == LYX_ALIGN_LAYOUT)
1042 align = layout->align;
1043 if (align & layout->alignpossible) {
1044 if (align == layout->align)
1045 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1047 cursor.par()->params().align(align);
1049 cursor.par()->setLabelWidthString(labelwidthstring);
1050 cursor.par()->params().noindent(noindent);
1051 tmppar = cursor.par()->previous();
1054 redoParagraphs(selection.start, endpar);
1057 setCursor(selection.start.par(), selection.start.pos());
1058 selection.cursor = cursor;
1059 setCursor(selection.end.par(), selection.end.pos());
1061 setCursor(tmpcursor.par(), tmpcursor.pos());
1063 bv()->updateInset(inset_owner);
1067 // set the counter of a paragraph. This includes the labels
1068 void LyXText::setCounter(Buffer const * buf, Paragraph * par)
1070 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1071 LyXLayout_ptr const & layout = par->layout();
1073 if (par->previous()) {
1075 par->params().appendix(par->previous()->params().appendix());
1076 if (!par->params().appendix() && par->params().startOfAppendix()) {
1077 par->params().appendix(true);
1078 textclass.counters().reset();
1080 par->enumdepth = par->previous()->enumdepth;
1081 par->itemdepth = par->previous()->itemdepth;
1083 par->params().appendix(par->params().startOfAppendix());
1088 /* Maybe we have to increment the enumeration depth.
1089 * BUT, enumeration in a footnote is considered in isolation from its
1090 * surrounding paragraph so don't increment if this is the
1091 * first line of the footnote
1092 * AND, bibliographies can't have their depth changed ie. they
1093 * are always of depth 0
1096 && par->previous()->getDepth() < par->getDepth()
1097 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1098 && par->enumdepth < 3
1099 && layout->labeltype != LABEL_BIBLIO) {
1103 // Maybe we have to decrement the enumeration depth, see note above
1105 && par->previous()->getDepth() > par->getDepth()
1106 && layout->labeltype != LABEL_BIBLIO) {
1107 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1110 if (!par->params().labelString().empty()) {
1111 par->params().labelString(string());
1114 if (layout->margintype == MARGIN_MANUAL) {
1115 if (par->params().labelWidthString().empty()) {
1116 par->setLabelWidthString(layout->labelstring());
1119 par->setLabelWidthString(string());
1122 // is it a layout that has an automatic label?
1123 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1124 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1128 if (i >= 0 && i <= buf->params.secnumdepth) {
1132 textclass.counters().step(layout->latexname());
1134 // Is there a label? Useful for Chapter layout
1135 if (!par->params().appendix()) {
1136 s << layout->labelstring();
1138 s << layout->labelstring_appendix();
1141 // Use of an integer is here less than elegant. For now.
1142 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1143 if (!par->params().appendix()) {
1144 numbertype = "sectioning";
1146 numbertype = "appendix";
1147 if (par->isRightToLeftPar(buf->params))
1148 langtype = "hebrew";
1153 s << textclass.counters()
1154 .numberLabel(layout->latexname(),
1155 numbertype, langtype, head);
1157 par->params().labelString(STRCONV(s.str()));
1159 // reset enum counters
1160 textclass.counters().reset("enum");
1161 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1162 textclass.counters().reset("enum");
1163 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1165 // Yes I know this is a really, really! bad solution
1167 string enumcounter("enum");
1169 switch (par->enumdepth) {
1178 enumcounter += "iv";
1181 // not a valid enumdepth...
1185 textclass.counters().step(enumcounter);
1187 s << textclass.counters()
1188 .numberLabel(enumcounter, "enumeration");
1189 par->params().labelString(STRCONV(s.str()));
1191 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1192 textclass.counters().step("bibitem");
1193 int number = textclass.counters().value("bibitem");
1194 if (par->bibitem()) {
1195 par->bibitem()->setCounter(number);
1196 par->params().labelString(layout->labelstring());
1198 // In biblio should't be following counters but...
1200 string s = layout->labelstring();
1202 // the caption hack:
1203 if (layout->labeltype == LABEL_SENSITIVE) {
1204 Paragraph * tmppar = par;
1207 while (tmppar && tmppar->inInset()
1208 // the single '=' is intended below
1209 && (in = tmppar->inInset()->owner())) {
1210 if (in->lyxCode() == Inset::FLOAT_CODE ||
1211 in->lyxCode() == Inset::WRAP_CODE) {
1215 tmppar = in->parOwner();
1221 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1223 textclass.counters().step(fl.type());
1225 // Doesn't work... yet.
1226 #if USE_BOOST_FORMAT
1227 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1228 // s << boost::format(_("%1$s %1$d:")
1230 // % buf->counters().value(fl.name());
1233 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1234 o << fl.name() << " #:";
1235 s = STRCONV(o.str());
1238 // par->SetLayout(0);
1239 // s = layout->labelstring;
1240 s = _("Senseless: ");
1243 par->params().labelString(s);
1245 // reset the enumeration counter. They are always reset
1246 // when there is any other layout between
1247 // Just fall-through between the cases so that all
1248 // enum counters deeper than enumdepth is also reset.
1249 switch (par->enumdepth) {
1251 textclass.counters().reset("enumi");
1253 textclass.counters().reset("enumii");
1255 textclass.counters().reset("enumiii");
1257 textclass.counters().reset("enumiv");
1263 // Updates all counters. Paragraphs with changed label string will be rebroken
1264 void LyXText::updateCounters()
1266 RowList::iterator rowit = rows().begin();
1267 Paragraph * par = rowit->par();
1269 // CHECK if this is really needed. (Lgb)
1270 bv()->buffer()->params.getLyXTextClass().counters().reset();
1273 while (rowit->par() != par)
1276 string const oldLabel = par->params().labelString();
1278 // setCounter can potentially change the labelString.
1279 setCounter(bv()->buffer(), par);
1281 string const & newLabel = par->params().labelString();
1283 if (oldLabel.empty() && !newLabel.empty()) {
1284 removeParagraph(rowit);
1285 appendParagraph(rowit);
1293 void LyXText::insertInset(Inset * inset)
1295 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1297 setUndo(bv(), Undo::FINISH, cursor.par(), cursor.par()->next());
1299 cursor.par()->insertInset(cursor.pos(), inset);
1300 // Just to rebreak and refresh correctly.
1301 // The character will not be inserted a second time
1302 insertChar(Paragraph::META_INSET);
1303 // If we enter a highly editable inset the cursor should be to before
1304 // the inset. This couldn't happen before as Undo was not handled inside
1305 // inset now after the Undo LyX tries to call inset->Edit(...) again
1306 // and cannot do this as the cursor is behind the inset and GetInset
1307 // does not return the inset!
1308 if (isHighlyEditableInset(inset)) {
1315 void LyXText::copyEnvironmentType()
1317 copylayouttype = cursor.par()->layout()->name();
1321 void LyXText::pasteEnvironmentType()
1323 // do nothing if there has been no previous copyEnvironmentType()
1324 if (!copylayouttype.empty())
1325 setLayout(copylayouttype);
1329 void LyXText::cutSelection(bool doclear, bool realcut)
1331 // Stuff what we got on the clipboard. Even if there is no selection.
1333 // There is a problem with having the stuffing here in that the
1334 // larger the selection the slower LyX will get. This can be
1335 // solved by running the line below only when the selection has
1336 // finished. The solution used currently just works, to make it
1337 // faster we need to be more clever and probably also have more
1338 // calls to stuffClipboard. (Lgb)
1339 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1341 // This doesn't make sense, if there is no selection
1342 if (!selection.set())
1345 // OK, we have a selection. This is always between selection.start
1346 // and selection.end
1348 // make sure that the depth behind the selection are restored, too
1349 Paragraph * endpar = selection.end.par()->next();
1350 Paragraph * undoendpar = endpar;
1352 if (endpar && endpar->getDepth()) {
1353 while (endpar && endpar->getDepth()) {
1354 endpar = endpar->next();
1355 undoendpar = endpar;
1357 } else if (endpar) {
1358 endpar = endpar->next(); // because of parindents etc.
1361 setUndo(bv(), Undo::DELETE,
1362 selection.start.par(), undoendpar);
1364 // there are two cases: cut only within one paragraph or
1365 // more than one paragraph
1366 if (selection.start.par() == selection.end.par()) {
1367 // only within one paragraph
1368 endpar = selection.end.par();
1369 int pos = selection.end.pos();
1370 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1371 selection.start.pos(), pos,
1372 bv()->buffer()->params.textclass,
1374 selection.end.pos(pos);
1376 endpar = selection.end.par();
1377 int pos = selection.end.pos();
1378 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1379 selection.start.pos(), pos,
1380 bv()->buffer()->params.textclass,
1383 selection.end.par(endpar);
1384 selection.end.pos(pos);
1385 cursor.pos(selection.end.pos());
1387 endpar = endpar->next();
1389 // sometimes necessary
1391 selection.start.par()->stripLeadingSpaces();
1393 redoParagraphs(selection.start, endpar);
1395 // cutSelection can invalidate the cursor so we need to set
1397 // we prefer the end for when tracking changes
1398 cursor = selection.end;
1400 // need a valid cursor. (Lgb)
1403 setCursor(cursor.par(), cursor.pos());
1404 selection.cursor = cursor;
1409 void LyXText::copySelection()
1411 // stuff the selection onto the X clipboard, from an explicit copy request
1412 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1414 // this doesnt make sense, if there is no selection
1415 if (!selection.set())
1418 // ok we have a selection. This is always between selection.start
1419 // and sel_end cursor
1421 // copy behind a space if there is one
1422 while (selection.start.par()->size() > selection.start.pos()
1423 && selection.start.par()->isLineSeparator(selection.start.pos())
1424 && (selection.start.par() != selection.end.par()
1425 || selection.start.pos() < selection.end.pos()))
1426 selection.start.pos(selection.start.pos() + 1);
1428 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1429 selection.start.pos(), selection.end.pos(),
1430 bv()->buffer()->params.textclass);
1434 void LyXText::pasteSelection()
1436 // this does not make sense, if there is nothing to paste
1437 if (!CutAndPaste::checkPastePossible())
1440 setUndo(bv(), Undo::INSERT,
1441 cursor.par(), cursor.par()->next());
1444 Paragraph * actpar = cursor.par();
1445 int pos = cursor.pos();
1447 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1448 bv()->buffer()->params.textclass);
1450 redoParagraphs(cursor, endpar);
1452 setCursor(cursor.par(), cursor.pos());
1455 selection.cursor = cursor;
1456 setCursor(actpar, pos);
1462 void LyXText::setSelectionRange(lyx::pos_type length)
1467 selection.cursor = cursor;
1474 // simple replacing. The font of the first selected character is used
1475 void LyXText::replaceSelectionWithString(string const & str)
1477 setCursorParUndo(bv());
1480 if (!selection.set()) { // create a dummy selection
1481 selection.end = cursor;
1482 selection.start = cursor;
1485 // Get font setting before we cut
1486 pos_type pos = selection.end.pos();
1487 LyXFont const font = selection.start.par()
1488 ->getFontSettings(bv()->buffer()->params,
1489 selection.start.pos());
1491 // Insert the new string
1492 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1493 selection.end.par()->insertChar(pos, (*cit), font);
1497 // Cut the selection
1498 cutSelection(true, false);
1504 // needed to insert the selection
1505 void LyXText::insertStringAsLines(string const & str)
1507 Paragraph * par = cursor.par();
1508 pos_type pos = cursor.pos();
1509 Paragraph * endpar = cursor.par()->next();
1511 setCursorParUndo(bv());
1513 // only to be sure, should not be neccessary
1516 bv()->buffer()->insertStringAsLines(par, pos, current_font, str);
1518 redoParagraphs(cursor, endpar);
1519 setCursor(cursor.par(), cursor.pos());
1520 selection.cursor = cursor;
1521 setCursor(par, pos);
1526 // turns double-CR to single CR, others where converted into one
1527 // blank. Then InsertStringAsLines is called
1528 void LyXText::insertStringAsParagraphs(string const & str)
1530 string linestr(str);
1531 bool newline_inserted = false;
1532 for (string::size_type i = 0; i < linestr.length(); ++i) {
1533 if (linestr[i] == '\n') {
1534 if (newline_inserted) {
1535 // we know that \r will be ignored by
1536 // InsertStringA. Of course, it is a dirty
1537 // trick, but it works...
1538 linestr[i - 1] = '\r';
1542 newline_inserted = true;
1544 } else if (IsPrintable(linestr[i])) {
1545 newline_inserted = false;
1548 insertStringAsLines(linestr);
1552 void LyXText::checkParagraph(Paragraph * par, pos_type pos)
1554 LyXCursor tmpcursor;
1558 RowList::iterator row = getRow(par, pos, y);
1559 RowList::iterator beg = rows().begin();
1561 // is there a break one row above
1563 && boost::prior(row)->par() == row->par()) {
1564 z = rowBreakPoint(*boost::prior(row));
1565 if (z >= row->pos()) {
1566 // set the dimensions of the row above
1567 y -= boost::prior(row)->height();
1570 breakAgain(boost::prior(row));
1572 // set the cursor again. Otherwise
1573 // dangling pointers are possible
1574 setCursor(cursor.par(), cursor.pos(),
1575 false, cursor.boundary());
1576 selection.cursor = cursor;
1581 int const tmpheight = row->height();
1582 pos_type const tmplast = row->lastPos();
1585 if (row->height() == tmpheight && row->lastPos() == tmplast) {
1586 postRowPaint(&*row, y);
1591 // check the special right address boxes
1592 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1594 tmpcursor.row(&*row);
1599 redoDrawingOfParagraph(tmpcursor);
1602 // set the cursor again. Otherwise dangling pointers are possible
1603 // also set the selection
1605 if (selection.set()) {
1607 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1608 false, selection.cursor.boundary());
1609 selection.cursor = cursor;
1610 setCursorIntern(selection.start.par(),
1611 selection.start.pos(),
1612 false, selection.start.boundary());
1613 selection.start = cursor;
1614 setCursorIntern(selection.end.par(),
1615 selection.end.pos(),
1616 false, selection.end.boundary());
1617 selection.end = cursor;
1618 setCursorIntern(last_sel_cursor.par(),
1619 last_sel_cursor.pos(),
1620 false, last_sel_cursor.boundary());
1621 last_sel_cursor = cursor;
1624 setCursorIntern(cursor.par(), cursor.pos(),
1625 false, cursor.boundary());
1629 // returns false if inset wasn't found
1630 bool LyXText::updateInset(Inset * inset)
1632 // first check the current paragraph
1633 int pos = cursor.par()->getPositionOfInset(inset);
1635 checkParagraph(cursor.par(), pos);
1639 // check every paragraph
1641 Paragraph * par = ownerParagraph();
1643 pos = par->getPositionOfInset(inset);
1645 checkParagraph(par, pos);
1655 bool LyXText::setCursor(Paragraph * par,
1657 bool setfont, bool boundary)
1659 LyXCursor old_cursor = cursor;
1660 setCursorIntern(par, pos, setfont, boundary);
1661 return deleteEmptyParagraphMechanism(old_cursor);
1665 void LyXText::setCursor(LyXCursor & cur, Paragraph * par,
1666 pos_type pos, bool boundary)
1672 cur.boundary(boundary);
1674 // get the cursor y position in text
1676 RowList::iterator row = getRow(par, pos, y);
1677 RowList::iterator beg = rows().begin();
1679 RowList::iterator old_row = row;
1681 // if we are before the first char of this row and are still in the
1682 // same paragraph and there is a previous row then put the cursor on
1683 // the end of the previous row
1684 cur.iy(y + row->baseline());
1686 if (row != beg && pos &&
1687 boost::prior(row)->par() == row->par() &&
1688 pos < par->size() &&
1689 par->getChar(pos) == Paragraph::META_INSET &&
1690 (ins = par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1697 // y is now the beginning of the cursor row
1698 y += row->baseline();
1699 // y is now the cursor baseline
1702 pos_type last = old_row->lastPrintablePos();
1704 // None of these should happen, but we're scaredy-cats
1705 if (pos > par->size()) {
1706 lyxerr << "dont like 1 please report" << endl;
1709 } else if (pos > last + 1) {
1710 lyxerr << "dont like 2 please report" << endl;
1711 // This shouldn't happen.
1714 } else if (pos < row->pos()) {
1715 lyxerr << "dont like 3 please report" << endl;
1720 // now get the cursors x position
1721 float x = getCursorX(&*row, pos, last, boundary);
1724 if (old_row != row) {
1725 x = getCursorX(&*old_row, pos, last, boundary);
1729 //if the cursor is in a visible row, anchor to it
1731 if (topy < y && y < topy + bv()->workHeight())
1736 float LyXText::getCursorX(RowList::iterator rit,
1737 pos_type pos, pos_type last, bool boundary) const
1739 pos_type cursor_vpos = 0;
1741 float fill_separator;
1743 float fill_label_hfill;
1744 // This call HAS to be here because of the BidiTables!!!
1745 prepareToPrint(rit, x, fill_separator, fill_hfill,
1748 if (last < rit->pos())
1749 cursor_vpos = rit->pos();
1750 else if (pos > last && !boundary)
1751 cursor_vpos = (rit->par()->isRightToLeftPar(bv()->buffer()->params))
1752 ? rit->pos() : last + 1;
1753 else if (pos > rit->pos() &&
1754 (pos > last || boundary))
1755 /// Place cursor after char at (logical) position pos - 1
1756 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1757 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1759 /// Place cursor before char at (logical) position pos
1760 cursor_vpos = (bidi_level(pos) % 2 == 0)
1761 ? log2vis(pos) : log2vis(pos) + 1;
1763 pos_type body_pos = rit->par()->beginningOfBody();
1764 if ((body_pos > 0) &&
1765 ((body_pos - 1 > last) ||
1766 !rit->par()->isLineSeparator(body_pos - 1)))
1769 for (pos_type vpos = rit->pos(); vpos < cursor_vpos; ++vpos) {
1770 pos_type pos = vis2log(vpos);
1771 if (body_pos > 0 && pos == body_pos - 1) {
1772 x += fill_label_hfill +
1773 font_metrics::width(
1774 rit->par()->layout()->labelsep,
1775 getLabelFont(bv()->buffer(),
1777 if (rit->par()->isLineSeparator(body_pos - 1))
1778 x -= singleWidth(rit->par(), body_pos - 1);
1780 if (rit->hfillExpansion(pos)) {
1781 x += singleWidth(rit->par(), pos);
1782 if (pos >= body_pos)
1785 x += fill_label_hfill;
1786 } else if (rit->par()->isSeparator(pos)) {
1787 x += singleWidth(rit->par(), pos);
1788 if (pos >= body_pos)
1789 x += fill_separator;
1791 x += singleWidth(rit->par(), pos);
1797 void LyXText::setCursorIntern(Paragraph * par,
1798 pos_type pos, bool setfont, bool boundary)
1800 InsetText * it = static_cast<InsetText *>(par->inInset());
1802 if (it != inset_owner) {
1803 lyxerr[Debug::INSETS] << "InsetText is " << it
1805 << "inset_owner is "
1806 << inset_owner << endl;
1807 #ifdef WITH_WARNINGS
1808 #warning I believe this code is wrong. (Lgb)
1809 #warning Jürgen, have a look at this. (Lgb)
1810 #warning Hmmm, I guess you are right but we
1811 #warning should verify when this is needed
1813 // Jürgen, would you like to have a look?
1814 // I guess we need to move the outer cursor
1815 // and open and lock the inset (bla bla bla)
1816 // stuff I don't know... so can you have a look?
1818 // I moved the lyxerr stuff in here so we can see if
1819 // this is actually really needed and where!
1821 // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1826 setCursor(cursor, par, pos, boundary);
1832 void LyXText::setCurrentFont()
1834 pos_type pos = cursor.pos();
1835 if (cursor.boundary() && pos > 0)
1839 if (pos == cursor.par()->size())
1841 else // potentional bug... BUG (Lgb)
1842 if (cursor.par()->isSeparator(pos)) {
1843 if (pos > cursor.row()->pos() &&
1844 bidi_level(pos) % 2 ==
1845 bidi_level(pos - 1) % 2)
1847 else if (pos + 1 < cursor.par()->size())
1853 cursor.par()->getFontSettings(bv()->buffer()->params, pos);
1854 real_current_font = getFont(bv()->buffer(), cursor.par(), pos);
1856 if (cursor.pos() == cursor.par()->size() &&
1857 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()) &&
1858 !cursor.boundary()) {
1859 Language const * lang =
1860 cursor.par()->getParLanguage(bv()->buffer()->params);
1861 current_font.setLanguage(lang);
1862 current_font.setNumber(LyXFont::OFF);
1863 real_current_font.setLanguage(lang);
1864 real_current_font.setNumber(LyXFont::OFF);
1869 // returns the column near the specified x-coordinate of the row
1870 // x is set to the real beginning of this column
1872 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1875 float fill_separator;
1877 float fill_label_hfill;
1879 prepareToPrint(rit, tmpx, fill_separator,
1880 fill_hfill, fill_label_hfill);
1882 pos_type vc = rit->pos();
1883 pos_type last = rit->lastPrintablePos();
1886 LyXLayout_ptr const & layout = rit->par()->layout();
1888 bool left_side = false;
1890 pos_type body_pos = rit->par()->beginningOfBody();
1891 float last_tmpx = tmpx;
1894 (body_pos - 1 > last ||
1895 !rit->par()->isLineSeparator(body_pos - 1)))
1898 // check for empty row
1899 if (!rit->par()->size()) {
1904 while (vc <= last && tmpx <= x) {
1907 if (body_pos > 0 && c == body_pos - 1) {
1908 tmpx += fill_label_hfill +
1909 font_metrics::width(layout->labelsep,
1910 getLabelFont(bv()->buffer(), rit->par()));
1911 if (rit->par()->isLineSeparator(body_pos - 1))
1912 tmpx -= singleWidth(rit->par(), body_pos - 1);
1915 if (rit->hfillExpansion(c)) {
1916 tmpx += singleWidth(rit->par(), c);
1920 tmpx += fill_label_hfill;
1921 } else if (rit->par()->isSeparator(c)) {
1922 tmpx += singleWidth(rit->par(), c);
1924 tmpx+= fill_separator;
1926 tmpx += singleWidth(rit->par(), c);
1931 if ((tmpx + last_tmpx) / 2 > x) {
1936 if (vc > last + 1) // This shouldn't happen.
1940 // This (rtl_support test) is not needed, but gives
1941 // some speedup if rtl_support=false
1942 bool const lastrow = lyxrc.rtl_support &&
1943 (boost::next(rit) == rowlist_.end() ||
1944 boost::next(rit)->par() != rit->par());
1945 // If lastrow is false, we don't need to compute
1946 // the value of rtl.
1947 bool const rtl = (lastrow)
1948 ? rit->par()->isRightToLeftPar(bv()->buffer()->params)
1951 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1952 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1954 else if (vc == rit->pos()) {
1956 if (bidi_level(c) % 2 == 1)
1959 c = vis2log(vc - 1);
1960 bool const rtl = (bidi_level(c) % 2 == 1);
1961 if (left_side == rtl) {
1963 boundary = isBoundary(bv()->buffer(), rit->par(), c);
1967 if (rit->pos() <= last && c > last
1968 && rit->par()->isNewline(last)) {
1969 if (bidi_level(last) % 2 == 0)
1970 tmpx -= singleWidth(rit->par(), last);
1972 tmpx += singleWidth(rit->par(), last);
1982 void LyXText::setCursorFromCoordinates(int x, int y)
1984 LyXCursor old_cursor = cursor;
1986 setCursorFromCoordinates(cursor, x, y);
1988 deleteEmptyParagraphMechanism(old_cursor);
1995 * return true if the cursor given is at the end of a row,
1996 * and the next row is filled by an inset that spans an entire
1999 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2002 Row const & next = *row.next();
2004 if (next.pos() != cur.pos() || next.par() != cur.par())
2006 if (!cur.par()->isInset(cur.pos()))
2008 Inset const * inset = cur.par()->getInset(cur.pos());
2009 if (inset->needFullRow() || inset->display())
2016 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
2018 // Get the row first.
2020 RowList::iterator row = getRowNearY(y);
2022 pos_type const column = getColumnNearX(&*row, x, bound);
2023 cur.par(row->par());
2024 cur.pos(row->pos() + column);
2026 cur.y(y + row->baseline());
2029 if (beforeFullRowInset(*row, cur)) {
2030 pos_type last = row->lastPrintablePos();
2031 float x = getCursorX(row->next(), cur.pos(), last, bound);
2033 cur.iy(y + row->height() + row->next()->baseline());
2034 cur.irow(row->next());
2040 cur.boundary(bound);
2044 void LyXText::cursorLeft(bool internal)
2046 if (cursor.pos() > 0) {
2047 bool boundary = cursor.boundary();
2048 setCursor(cursor.par(), cursor.pos() - 1, true, false);
2049 if (!internal && !boundary &&
2050 isBoundary(bv()->buffer(), cursor.par(), cursor.pos() + 1))
2051 setCursor(cursor.par(), cursor.pos() + 1, true, true);
2052 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2053 Paragraph * par = cursor.par()->previous();
2054 setCursor(par, par->size());
2059 void LyXText::cursorRight(bool internal)
2061 if (!internal && cursor.boundary() &&
2062 !cursor.par()->isNewline(cursor.pos()))
2063 setCursor(cursor.par(), cursor.pos(), true, false);
2064 else if (cursor.pos() < cursor.par()->size()) {
2065 setCursor(cursor.par(), cursor.pos() + 1, true, false);
2067 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()))
2068 setCursor(cursor.par(), cursor.pos(), true, true);
2069 } else if (cursor.par()->next())
2070 setCursor(cursor.par()->next(), 0);
2074 void LyXText::cursorUp(bool selecting)
2077 int x = cursor.x_fix();
2078 int y = cursor.y() - cursor.row()->baseline() - 1;
2079 setCursorFromCoordinates(x, y);
2082 int y1 = cursor.iy() - topy;
2085 Inset * inset_hit = checkInsetHit(x, y1);
2086 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2087 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2091 setCursorFromCoordinates(bv(), cursor.x_fix(),
2092 cursor.y() - cursor.row()->baseline() - 1);
2097 void LyXText::cursorDown(bool selecting)
2100 int x = cursor.x_fix();
2101 int y = cursor.y() - cursor.row()->baseline() +
2102 cursor.row()->height() + 1;
2103 setCursorFromCoordinates(x, y);
2104 if (!selecting && cursor.row() == cursor.irow()) {
2106 int y1 = cursor.iy() - topy;
2109 Inset * inset_hit = checkInsetHit(x, y1);
2110 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2111 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2115 setCursorFromCoordinates(bv(), cursor.x_fix(),
2116 cursor.y() - cursor.row()->baseline()
2117 + cursor.row()->height() + 1);
2122 void LyXText::cursorUpParagraph()
2124 if (cursor.pos() > 0) {
2125 setCursor(cursor.par(), 0);
2127 else if (cursor.par()->previous()) {
2128 setCursor(cursor.par()->previous(), 0);
2133 void LyXText::cursorDownParagraph()
2135 if (cursor.par()->next()) {
2136 setCursor(cursor.par()->next(), 0);
2138 setCursor(cursor.par(), cursor.par()->size());
2142 // fix the cursor `cur' after a characters has been deleted at `where'
2143 // position. Called by deleteEmptyParagraphMechanism
2144 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2145 LyXCursor const & where)
2147 // if cursor is not in the paragraph where the delete occured,
2149 if (cur.par() != where.par())
2152 // if cursor position is after the place where the delete occured,
2154 if (cur.pos() > where.pos())
2155 cur.pos(cur.pos()-1);
2157 // check also if we don't want to set the cursor on a spot behind the
2158 // pagragraph because we erased the last character.
2159 if (cur.pos() > cur.par()->size())
2160 cur.pos(cur.par()->size());
2162 // recompute row et al. for this cursor
2163 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2167 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2169 // Would be wrong to delete anything if we have a selection.
2170 if (selection.set())
2173 // We allow all kinds of "mumbo-jumbo" when freespacing.
2174 if (old_cursor.par()->layout()->free_spacing
2175 || old_cursor.par()->isFreeSpacing()) {
2179 /* Ok I'll put some comments here about what is missing.
2180 I have fixed BackSpace (and thus Delete) to not delete
2181 double-spaces automagically. I have also changed Cut,
2182 Copy and Paste to hopefully do some sensible things.
2183 There are still some small problems that can lead to
2184 double spaces stored in the document file or space at
2185 the beginning of paragraphs. This happens if you have
2186 the cursor betwenn to spaces and then save. Or if you
2187 cut and paste and the selection have a space at the
2188 beginning and then save right after the paste. I am
2189 sure none of these are very hard to fix, but I will
2190 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2191 that I can get some feedback. (Lgb)
2194 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2195 // delete the LineSeparator.
2198 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2199 // delete the LineSeparator.
2202 // If the pos around the old_cursor were spaces, delete one of them.
2203 if (old_cursor.par() != cursor.par()
2204 || old_cursor.pos() != cursor.pos()) {
2205 // Only if the cursor has really moved
2207 if (old_cursor.pos() > 0
2208 && old_cursor.pos() < old_cursor.par()->size()
2209 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2210 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2211 old_cursor.par()->erase(old_cursor.pos() - 1);
2212 redoParagraphs(old_cursor, old_cursor.par()->next());
2214 #ifdef WITH_WARNINGS
2215 #warning This will not work anymore when we have multiple views of the same buffer
2216 // In this case, we will have to correct also the cursors held by
2217 // other bufferviews. It will probably be easier to do that in a more
2218 // automated way in LyXCursor code. (JMarc 26/09/2001)
2220 // correct all cursors held by the LyXText
2221 fixCursorAfterDelete(cursor, old_cursor);
2222 fixCursorAfterDelete(selection.cursor,
2224 fixCursorAfterDelete(selection.start,
2226 fixCursorAfterDelete(selection.end, old_cursor);
2227 fixCursorAfterDelete(last_sel_cursor,
2229 fixCursorAfterDelete(toggle_cursor, old_cursor);
2230 fixCursorAfterDelete(toggle_end_cursor,
2236 // don't delete anything if this is the ONLY paragraph!
2237 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2240 // Do not delete empty paragraphs with keepempty set.
2241 if (old_cursor.par()->layout()->keepempty)
2244 // only do our magic if we changed paragraph
2245 if (old_cursor.par() == cursor.par())
2248 // record if we have deleted a paragraph
2249 // we can't possibly have deleted a paragraph before this point
2250 bool deleted = false;
2252 if ((old_cursor.par()->empty()
2253 || (old_cursor.par()->size() == 1
2254 && old_cursor.par()->isLineSeparator(0)))) {
2255 // ok, we will delete anything
2256 LyXCursor tmpcursor;
2260 if (old_cursor.row()->previous()) {
2261 const_cast<LyXText *>(this)->postPaint(old_cursor.y() - old_cursor.row()->baseline()
2262 - old_cursor.row()->previous()->height());
2264 cursor = old_cursor; // that undo can restore the right cursor position
2265 Paragraph * endpar = old_cursor.par()->next();
2266 if (endpar && endpar->getDepth()) {
2267 while (endpar && endpar->getDepth()) {
2268 endpar = endpar->next();
2271 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2275 removeRow(old_cursor.row());
2276 if (ownerParagraph() == old_cursor.par()) {
2277 ownerParagraph(ownerParagraph()->next());
2280 delete old_cursor.par();
2282 /* Breakagain the next par. Needed because of
2283 * the parindent that can occur or dissappear.
2284 * The next row can change its height, if
2285 * there is another layout before */
2286 if (refresh_row != rows().end()) {
2287 if (refresh_row->next()) {
2288 breakAgain(refresh_row->next());
2291 setHeightOfRow(refresh_row);
2294 Row * nextrow = old_cursor.row()->next();
2295 const_cast<LyXText *>(this)->postPaint(
2296 old_cursor.y() - old_cursor.row()->baseline());
2299 cursor = old_cursor; // that undo can restore the right cursor position
2300 Paragraph * endpar = old_cursor.par()->next();
2301 if (endpar && endpar->getDepth()) {
2302 while (endpar && endpar->getDepth()) {
2303 endpar = endpar->next();
2306 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2310 removeRow(old_cursor.row());
2312 if (ownerParagraph() == old_cursor.par()) {
2313 ownerParagraph(ownerParagraph()->next());
2316 delete old_cursor.par();
2318 /* Breakagain the next par. Needed because of
2319 the parindent that can occur or dissappear.
2320 The next row can change its height, if
2321 there is another layout before */
2323 breakAgain(nextrow);
2329 setCursorIntern(cursor.par(), cursor.pos());
2331 if (selection.cursor.par() == old_cursor.par()
2332 && selection.cursor.pos() == old_cursor.pos()) {
2333 // correct selection
2334 selection.cursor = cursor;
2338 if (old_cursor.par()->stripLeadingSpaces()) {
2339 redoParagraphs(old_cursor,
2340 old_cursor.par()->next());
2342 setCursorIntern(cursor.par(), cursor.pos());
2343 selection.cursor = cursor;
2350 Paragraph * LyXText::ownerParagraph() const
2353 return inset_owner->paragraph();
2355 return &*(bv_owner->buffer()->paragraphs.begin());
2359 void LyXText::ownerParagraph(Paragraph * p) const
2362 inset_owner->paragraph(p);
2364 bv_owner->buffer()->paragraphs.set(p);
2369 void LyXText::ownerParagraph(int id, Paragraph * p) const
2371 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2372 if (op && op->inInset()) {
2373 static_cast<InsetText *>(op->inInset())->paragraph(p);
2380 LyXText::refresh_status LyXText::refreshStatus() const
2382 return refresh_status_;
2386 void LyXText::clearPaint()
2388 refresh_status_ = REFRESH_NONE;
2389 refresh_row = rows().end();
2394 void LyXText::postPaint(int start_y)
2396 refresh_status old = refresh_status_;
2398 refresh_status_ = REFRESH_AREA;
2399 refresh_row = rows().end();
2401 if (old != REFRESH_NONE && refresh_y < start_y)
2404 refresh_y = start_y;
2409 // We are an inset's lyxtext. Tell the top-level lyxtext
2410 // it needs to update the row we're in.
2411 LyXText * t = bv()->text;
2412 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2416 // FIXME: we should probably remove this y parameter,
2417 // make refresh_y be 0, and use row->y etc.
2418 void LyXText::postRowPaint(RowList::iterator rit, int start_y)
2420 if (refresh_status_ != REFRESH_NONE && refresh_y < start_y) {
2421 refresh_status_ = REFRESH_AREA;
2424 refresh_y = start_y;
2427 if (refresh_status_ == REFRESH_AREA)
2430 refresh_status_ = REFRESH_ROW;
2436 // We are an inset's lyxtext. Tell the top-level lyxtext
2437 // it needs to update the row we're in.
2438 LyXText * t = bv()->text;
2439 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2443 bool LyXText::isInInset() const
2445 // Sub-level has non-null bv owner and
2446 // non-null inset owner.
2447 return inset_owner != 0 && bv_owner != 0;
2451 int defaultRowHeight()
2453 LyXFont const font(LyXFont::ALL_SANE);
2454 return int(font_metrics::maxAscent(font)
2455 + font_metrics::maxDescent(font) * 1.5);