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"
32 #include "lyxrow_funcs.h"
34 #include "insets/inseterror.h"
35 #include "insets/insetbibitem.h"
36 #include "insets/insetspecialchar.h"
37 #include "insets/insettext.h"
38 #include "insets/insetfloat.h"
39 #include "insets/insetwrap.h"
41 #include "support/LAssert.h"
42 #include "support/textutils.h"
43 #include "support/lstrings.h"
45 #include "support/BoostFormat.h"
55 LyXText::LyXText(BufferView * bv)
56 : height(0), width(0), anchor_row_offset_(0),
57 inset_owner(0), the_locking_inset(0), bv_owner(bv)
59 anchor_row_ = rows().end();
60 need_break_row = rows().end();
61 refresh_row = rows().end();
67 LyXText::LyXText(BufferView * bv, InsetText * inset)
68 : height(0), width(0), anchor_row_offset_(0),
69 inset_owner(inset), the_locking_inset(0), bv_owner(bv)
71 anchor_row_ = rows().end();
72 need_break_row = rows().end();
73 refresh_row = rows().end();
79 void LyXText::init(BufferView * bview, bool reinit)
83 need_break_row = rows().end();
85 copylayouttype.erase();
88 } else if (!rowlist_.empty())
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(par, rowlist_.end());
98 setCursorIntern(rowlist_.begin()->par(), 0);
99 selection.cursor = cursor;
107 LyXFont const realizeFont(LyXFont const & font,
111 LyXTextClass const & tclass = buf->params.getLyXTextClass();
112 LyXFont tmpfont(font);
113 Paragraph::depth_type par_depth = par->getDepth();
115 // Resolve against environment font information
116 while (par && par_depth && !tmpfont.resolved()) {
117 par = par->outerHook();
119 tmpfont.realize(par->layout()->font);
120 par_depth = par->getDepth();
124 tmpfont.realize(tclass.defaultfont());
132 // Gets the fully instantiated font at a given position in a paragraph
133 // Basically the same routine as Paragraph::getFont() in paragraph.C.
134 // The difference is that this one is used for displaying, and thus we
135 // are allowed to make cosmetic improvements. For instance make footnotes
137 // If position is -1, we get the layout font of the paragraph.
138 // If position is -2, we get the font of the manual label of the paragraph.
139 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
142 lyx::Assert(pos >= 0);
144 LyXLayout_ptr const & layout = par->layout();
146 // We specialize the 95% common case:
147 if (!par->getDepth()) {
148 if (layout->labeltype == LABEL_MANUAL
149 && pos < par->beginningOfBody()) {
151 LyXFont f = par->getFontSettings(buf->params, pos);
153 par->inInset()->getDrawFont(f);
154 return f.realize(layout->reslabelfont);
156 LyXFont f = par->getFontSettings(buf->params, pos);
158 par->inInset()->getDrawFont(f);
159 return f.realize(layout->resfont);
163 // The uncommon case need not be optimized as much
167 if (pos < par->beginningOfBody()) {
169 layoutfont = layout->labelfont;
172 layoutfont = layout->font;
175 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
176 tmpfont.realize(layoutfont);
179 par->inInset()->getDrawFont(tmpfont);
181 return realizeFont(tmpfont, buf, par);
185 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
187 LyXLayout_ptr const & layout = par->layout();
189 if (!par->getDepth()) {
190 return layout->resfont;
193 return realizeFont(layout->font, buf, par);
197 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
199 LyXLayout_ptr const & layout = par->layout();
201 if (!par->getDepth()) {
202 return layout->reslabelfont;
205 return realizeFont(layout->labelfont, buf, par);
209 void LyXText::setCharFont(Paragraph * par,
210 pos_type pos, LyXFont const & fnt,
213 Buffer const * buf = bv()->buffer();
214 LyXFont font = getFont(buf, par, pos);
215 font.update(fnt, buf->params.language, toggleall);
216 // Let the insets convert their font
217 if (par->isInset(pos)) {
218 Inset * inset = par->getInset(pos);
219 if (isEditableInset(inset)) {
220 UpdatableInset * uinset =
221 static_cast<UpdatableInset *>(inset);
222 uinset->setFont(bv(), fnt, toggleall, true);
226 // Plug thru to version below:
227 setCharFont(buf, par, pos, font);
231 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
232 pos_type pos, LyXFont const & fnt)
236 LyXTextClass const & tclass = buf->params.getLyXTextClass();
237 LyXLayout_ptr const & layout = par->layout();
239 // Get concrete layout font to reduce against
242 if (pos < par->beginningOfBody())
243 layoutfont = layout->labelfont;
245 layoutfont = layout->font;
247 // Realize against environment font information
248 if (par->getDepth()) {
249 Paragraph * tp = par;
250 while (!layoutfont.resolved() && tp && tp->getDepth()) {
251 tp = tp->outerHook();
253 layoutfont.realize(tp->layout()->font);
257 layoutfont.realize(tclass.defaultfont());
259 // Now, reduce font against full layout font
260 font.reduce(layoutfont);
262 par->setFont(pos, font);
266 // removes the row and reset the touched counters
267 void LyXText::removeRow(RowList::iterator rit)
269 /* FIXME: when we cache the bview, this should just
270 * become a postPaint(), I think */
271 if (refresh_row == rit) {
272 if (rit == rows().begin())
273 refresh_row = boost::next(rit);
275 refresh_row = boost::prior(rit);
277 // what about refresh_y
280 if (anchor_row_ == rit) {
281 if (rit != rows().begin()) {
282 anchor_row_ = boost::prior(rit);
283 anchor_row_offset_ += boost::prior(rit)->height();
285 anchor_row_ = boost::next(rit);
286 anchor_row_offset_ -= rit->height();
290 // the text becomes smaller
291 height -= rit->height();
297 // remove all following rows of the paragraph of the specified row.
298 void LyXText::removeParagraph(RowList::iterator rit)
300 Paragraph * tmppar = rit->par();
303 while (rit != rows().end() && rit->par() == tmppar) {
304 RowList::iterator tmprit = boost::next(rit);
311 void LyXText::insertParagraph(Paragraph * par, RowList::iterator rowit)
313 // insert a new row, starting at position 0
315 RowList::iterator rit = rowlist_.insert(rowit, newrow);
317 // and now append the whole paragraph before the new row
318 appendParagraph(rit);
322 Inset * LyXText::getInset() const
324 if (cursor.pos() < cursor.par()->size()
325 && cursor.par()->isInset(cursor.pos())) {
326 return cursor.par()->getInset(cursor.pos());
332 void LyXText::toggleInset()
334 Inset * inset = getInset();
335 // is there an editable inset at cursor position?
336 if (!isEditableInset(inset)) {
337 // No, try to see if we are inside a collapsable inset
338 if (inset_owner && inset_owner->owner()
339 && inset_owner->owner()->isOpen()) {
340 bv()->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
341 inset_owner->owner()->close(bv());
342 bv()->getLyXText()->cursorRight(bv());
346 //bv()->owner()->message(inset->editMessage());
348 // do we want to keep this?? (JMarc)
349 if (!isHighlyEditableInset(inset))
350 setCursorParUndo(bv());
352 if (inset->isOpen()) {
358 bv()->updateInset(inset);
362 /* used in setlayout */
363 // Asger is not sure we want to do this...
364 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
367 LyXLayout_ptr const & layout = par.layout();
370 for (pos_type pos = 0; pos < par.size(); ++pos) {
371 if (pos < par.beginningOfBody())
372 layoutfont = layout->labelfont;
374 layoutfont = layout->font;
376 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
377 tmpfont.reduce(layoutfont);
378 par.setFont(pos, tmpfont);
383 Paragraph * LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
384 LyXCursor & send_cur,
385 string const & layout)
387 Paragraph * endpar = send_cur.par()->next();
388 Paragraph * undoendpar = endpar;
390 if (endpar && endpar->getDepth()) {
391 while (endpar && endpar->getDepth()) {
392 endpar = endpar->next();
396 endpar = endpar->next(); // because of parindents etc.
399 setUndo(bv(), Undo::EDIT, sstart_cur.par(), undoendpar);
401 // ok we have a selection. This is always between sstart_cur
402 // and sel_end cursor
404 Paragraph * par = sstart_cur.par();
405 Paragraph * epar = send_cur.par()->next();
407 LyXLayout_ptr const & lyxlayout =
408 bv()->buffer()->params.getLyXTextClass()[layout];
411 par->applyLayout(lyxlayout);
412 makeFontEntriesLayoutSpecific(*bv()->buffer(), *par);
413 Paragraph * fppar = par;
414 fppar->params().spaceTop(lyxlayout->fill_top ?
415 VSpace(VSpace::VFILL)
416 : VSpace(VSpace::NONE));
417 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
418 VSpace(VSpace::VFILL)
419 : VSpace(VSpace::NONE));
420 if (lyxlayout->margintype == MARGIN_MANUAL)
421 par->setLabelWidthString(lyxlayout->labelstring());
424 } while (par != epar);
430 // set layout over selection and make a total rebreak of those paragraphs
431 void LyXText::setLayout(string const & layout)
433 LyXCursor tmpcursor = cursor; /* store the current cursor */
435 // if there is no selection just set the layout
436 // of the current paragraph */
437 if (!selection.set()) {
438 selection.start = cursor; // dummy selection
439 selection.end = cursor;
441 Paragraph * endpar = setLayout(cursor, selection.start,
442 selection.end, layout);
443 redoParagraphs(selection.start, endpar);
445 // we have to reset the selection, because the
446 // geometry could have changed
447 setCursor(selection.start.par(),
448 selection.start.pos(), false);
449 selection.cursor = cursor;
450 setCursor(selection.end.par(), selection.end.pos(), false);
454 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
458 // increment depth over selection and
459 // make a total rebreak of those paragraphs
460 void LyXText::incDepth()
462 // If there is no selection, just use the current paragraph
463 if (!selection.set()) {
464 selection.start = cursor; // dummy selection
465 selection.end = cursor;
468 // We end at the next paragraph with depth 0
469 Paragraph * endpar = selection.end.par()->next();
471 Paragraph * undoendpar = endpar;
473 if (endpar && endpar->getDepth()) {
474 while (endpar && endpar->getDepth()) {
475 endpar = endpar->next();
479 endpar = endpar->next(); // because of parindents etc.
482 setUndo(bv(), Undo::EDIT,
483 selection.start.par(), undoendpar);
485 LyXCursor tmpcursor = cursor; // store the current cursor
487 // ok we have a selection. This is always between sel_start_cursor
488 // and sel_end cursor
489 cursor = selection.start;
492 // NOTE: you can't change the depth of a bibliography entry
493 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
494 Paragraph * prev = cursor.par()->previous();
497 if (cursor.par()->getDepth()
498 < prev->getMaxDepthAfter()) {
499 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
503 if (cursor.par() == selection.end.par())
505 cursor.par(cursor.par()->next());
508 redoParagraphs(selection.start, endpar);
510 // we have to reset visual the selection because the
511 // geometry could have changed
512 setCursor(selection.start.par(), selection.start.pos());
513 selection.cursor = cursor;
514 setCursor(selection.end.par(), selection.end.pos());
517 setCursor(tmpcursor.par(), tmpcursor.pos());
521 // decrement depth over selection and
522 // make a total rebreak of those paragraphs
523 void LyXText::decDepth()
525 // if there is no selection just set the layout
526 // of the current paragraph
527 if (!selection.set()) {
528 selection.start = cursor; // dummy selection
529 selection.end = cursor;
531 Paragraph * endpar = selection.end.par()->next();
532 Paragraph * undoendpar = endpar;
534 if (endpar && endpar->getDepth()) {
535 while (endpar && endpar->getDepth()) {
536 endpar = endpar->next();
540 endpar = endpar->next(); // because of parindents etc.
543 setUndo(bv(), Undo::EDIT,
544 selection.start.par(), undoendpar);
546 LyXCursor tmpcursor = cursor; // store the current cursor
548 // ok we have a selection. This is always between sel_start_cursor
549 // and sel_end cursor
550 cursor = selection.start;
553 if (cursor.par()->params().depth()) {
554 cursor.par()->params()
555 .depth(cursor.par()->params().depth() - 1);
557 if (cursor.par() == selection.end.par()) {
560 cursor.par(cursor.par()->next());
563 redoParagraphs(selection.start, endpar);
565 // we have to reset the visual selection because the
566 // geometry could have changed
567 setCursor(selection.start.par(), selection.start.pos());
568 selection.cursor = cursor;
569 setCursor(selection.end.par(), selection.end.pos());
572 setCursor(tmpcursor.par(), tmpcursor.pos());
576 // set font over selection and make a total rebreak of those paragraphs
577 void LyXText::setFont(LyXFont const & font, bool toggleall)
579 // if there is no selection just set the current_font
580 if (!selection.set()) {
581 // Determine basis font
583 if (cursor.pos() < cursor.par()->beginningOfBody()) {
584 layoutfont = getLabelFont(bv()->buffer(),
587 layoutfont = getLayoutFont(bv()->buffer(),
590 // Update current font
591 real_current_font.update(font,
592 bv()->buffer()->params.language,
595 // Reduce to implicit settings
596 current_font = real_current_font;
597 current_font.reduce(layoutfont);
598 // And resolve it completely
599 real_current_font.realize(layoutfont);
604 LyXCursor tmpcursor = cursor; // store the current cursor
606 // ok we have a selection. This is always between sel_start_cursor
607 // and sel_end cursor
609 setUndo(bv(), Undo::EDIT,
610 selection.start.par(), selection.end.par()->next());
612 cursor = selection.start;
613 while (cursor.par() != selection.end.par() ||
614 cursor.pos() < selection.end.pos())
616 if (cursor.pos() < cursor.par()->size()) {
617 // an open footnote should behave like a closed one
618 setCharFont(cursor.par(), cursor.pos(),
620 cursor.pos(cursor.pos() + 1);
623 cursor.par(cursor.par()->next());
628 redoParagraphs(selection.start, selection.end.par()->next());
630 // we have to reset the selection, because the
631 // geometry could have changed, but we keep
632 // it for user convenience
633 setCursor(selection.start.par(), selection.start.pos());
634 selection.cursor = cursor;
635 setCursor(selection.end.par(), selection.end.pos());
637 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
638 tmpcursor.boundary());
642 void LyXText::redoHeightOfParagraph()
644 RowList::iterator tmprow = cursor.row();
645 int y = cursor.y() - tmprow->baseline();
647 setHeightOfRow(tmprow);
649 while (tmprow != rows().begin()
650 && boost::prior(tmprow)->par() == tmprow->par()) {
652 y -= tmprow->height();
653 setHeightOfRow(tmprow);
658 setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
662 void LyXText::redoDrawingOfParagraph(LyXCursor const & cur)
664 RowList::iterator tmprow = cur.row();
666 int y = cur.y() - tmprow->baseline();
667 setHeightOfRow(tmprow);
669 while (tmprow != rows().begin()
670 && boost::prior(tmprow)->par() == tmprow->par()) {
672 y -= tmprow->height();
676 setCursor(cur.par(), cur.pos());
680 // deletes and inserts again all paragaphs between the cursor
681 // and the specified par
682 // This function is needed after SetLayout and SetFont etc.
683 void LyXText::redoParagraphs(LyXCursor const & cur,
684 Paragraph const * endpar)
686 RowList::iterator tmprit = cur.row();
688 int y = cur.y() - tmprit->baseline();
690 Paragraph * first_phys_par;
691 if (tmprit == rows().begin()) {
692 // A trick/hack for UNDO.
693 // This is needed because in an UNDO/REDO we could have
694 // changed the ownerParagrah() so the paragraph inside
695 // the row is NOT my really first par anymore.
696 // Got it Lars ;) (Jug 20011206)
697 first_phys_par = ownerParagraph();
699 // In here prevrit could be set to rows().end(). (Lgb)
701 first_phys_par = tmprit->par();
702 while (tmprit != rows().begin()
703 && boost::prior(tmprit)->par() == first_phys_par)
706 y -= tmprit->height();
709 // Is it possible to put the prevrit setting in here? (Lgb)
712 RowList::iterator prevrit;
713 bool good_prevrit = false;
715 // It seems to mee that good_prevrit is not needed if we let
716 // a bad prevrit have the value rows().end() (Lgb)
717 if (tmprit != rows().begin()) {
718 prevrit = boost::prior(tmprit);
723 while (tmprit != rows().end() && tmprit->par() != endpar) {
724 RowList::iterator tmprit2 = tmprit++;
728 // Reinsert the paragraphs.
729 Paragraph * tmppar = first_phys_par;
731 // See if this loop can be rewritten as a while loop instead.
732 // That should also make the code a bit easier to read. (Lgb)
735 insertParagraph(tmppar, tmprit);
736 while (tmprit != rows().end()
737 && tmprit->par() == tmppar) {
740 tmppar = tmppar->next();
742 } while (tmppar && tmppar != endpar);
745 // If the above changes are done, then we can compare prevrit
746 // with rows().end() here. (Lgb)
748 setHeightOfRow(prevrit);
749 const_cast<LyXText *>(this)->postPaint(y - prevrit->height());
751 setHeightOfRow(rows().begin());
752 const_cast<LyXText *>(this)->postPaint(0);
754 if (tmprit != rows().end())
755 setHeightOfRow(tmprit);
760 void LyXText::fullRebreak()
762 if (rows().empty()) {
766 if (need_break_row != rows().end()) {
767 breakAgain(need_break_row);
768 need_break_row = rows().end();
774 // important for the screen
777 // the cursor set functions have a special mechanism. When they
778 // realize, that you left an empty paragraph, they will delete it.
779 // They also delete the corresponding row
781 // need the selection cursor:
782 void LyXText::setSelection()
784 bool const lsel = selection.set();
786 if (!selection.set()) {
787 last_sel_cursor = selection.cursor;
788 selection.start = selection.cursor;
789 selection.end = selection.cursor;
794 // first the toggling area
795 if (cursor.y() < last_sel_cursor.y()
796 || (cursor.y() == last_sel_cursor.y()
797 && cursor.x() < last_sel_cursor.x())) {
798 toggle_end_cursor = last_sel_cursor;
799 toggle_cursor = cursor;
801 toggle_end_cursor = cursor;
802 toggle_cursor = last_sel_cursor;
805 last_sel_cursor = cursor;
807 // and now the whole selection
809 if (selection.cursor.par() == cursor.par())
810 if (selection.cursor.pos() < cursor.pos()) {
811 selection.end = cursor;
812 selection.start = selection.cursor;
814 selection.end = selection.cursor;
815 selection.start = cursor;
817 else if (selection.cursor.y() < cursor.y() ||
818 (selection.cursor.y() == cursor.y()
819 && selection.cursor.x() < cursor.x())) {
820 selection.end = cursor;
821 selection.start = selection.cursor;
824 selection.end = selection.cursor;
825 selection.start = cursor;
828 // a selection with no contents is not a selection
829 if (selection.start.par() == selection.end.par() &&
830 selection.start.pos() == selection.end.pos())
831 selection.set(false);
833 if (inset_owner && (selection.set() || lsel))
834 inset_owner->setUpdateStatus(bv(), InsetText::SELECTION);
838 string const LyXText::selectionAsString(Buffer const * buffer,
841 if (!selection.set()) return string();
843 // should be const ...
844 Paragraph * startpar(selection.start.par());
845 Paragraph * endpar(selection.end.par());
846 pos_type const startpos(selection.start.pos());
847 pos_type const endpos(selection.end.pos());
849 if (startpar == endpar) {
850 return startpar->asString(buffer, startpos, endpos, label);
855 // First paragraph in selection
856 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
858 // The paragraphs in between (if any)
859 LyXCursor tmpcur(selection.start);
860 tmpcur.par(tmpcur.par()->next());
861 while (tmpcur.par() != endpar) {
862 result += tmpcur.par()->asString(buffer, 0,
863 tmpcur.par()->size(),
865 tmpcur.par(tmpcur.par()->next());
868 // Last paragraph in selection
869 result += endpar->asString(buffer, 0, endpos, label);
875 void LyXText::clearSelection()
877 selection.set(false);
878 selection.mark(false);
879 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
880 // reset this in the bv_owner!
881 if (bv_owner && bv_owner->text)
882 bv_owner->text->xsel_cache.set(false);
886 void LyXText::cursorHome()
888 setCursor(cursor.par(), cursor.row()->pos());
892 void LyXText::cursorEnd()
894 if (cursor.par()->empty())
897 // There is a lot of unneeded recalculation going on here:
898 // - boost::next(curosr.row())
899 // - lastPost(*this, cursor.row())
901 if (boost::next(cursor.row()) == rows().end()
902 || boost::next(cursor.row())->par() != cursor.row()->par()) {
903 setCursor(cursor.par(), lastPos(*this, cursor.row()) + 1);
905 if (!cursor.par()->empty() &&
906 (cursor.par()->getChar(lastPos(*this, cursor.row())) == ' '
907 || cursor.par()->isNewline(lastPos(*this, cursor.row())))) {
908 setCursor(cursor.par(), lastPos(*this, cursor.row()));
910 setCursor(cursor.par(),
911 lastPos(*this, cursor.row()) + 1);
917 void LyXText::cursorTop()
919 while (cursor.par()->previous())
920 cursor.par(cursor.par()->previous());
921 setCursor(cursor.par(), 0);
925 void LyXText::cursorBottom()
927 while (cursor.par()->next())
928 cursor.par(cursor.par()->next());
929 setCursor(cursor.par(), cursor.par()->size());
933 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
935 // If the mask is completely neutral, tell user
936 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
937 // Could only happen with user style
938 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
942 // Try implicit word selection
943 // If there is a change in the language the implicit word selection
945 LyXCursor resetCursor = cursor;
946 bool implicitSelection = (font.language() == ignore_language
947 && font.number() == LyXFont::IGNORE)
948 ? selectWordWhenUnderCursor(WHOLE_WORD_STRICT) : false;
951 setFont(font, toggleall);
953 // Implicit selections are cleared afterwards
954 //and cursor is set to the original position.
955 if (implicitSelection) {
957 cursor = resetCursor;
958 setCursor(cursor.par(), cursor.pos());
959 selection.cursor = cursor;
962 inset_owner->setUpdateStatus(bv(), InsetText::CURSOR_PAR);
966 string LyXText::getStringToIndex()
968 // Try implicit word selection
969 // If there is a change in the language the implicit word selection
971 LyXCursor const reset_cursor = cursor;
972 bool const implicitSelection = selectWordWhenUnderCursor(PREVIOUS_WORD);
975 if (!selection.set())
976 bv()->owner()->message(_("Nothing to index!"));
977 else if (selection.start.par() != selection.end.par())
978 bv()->owner()->message(_("Cannot index more than one paragraph!"));
980 idxstring = selectionAsString(bv()->buffer(), false);
982 // Reset cursors to their original position.
983 cursor = reset_cursor;
984 setCursor(cursor.par(), cursor.pos());
985 selection.cursor = cursor;
987 // Clear the implicit selection.
988 if (implicitSelection)
995 // the DTP switches for paragraphs. LyX will store them in the first
996 // physicla paragraph. When a paragraph is broken, the top settings rest,
997 // the bottom settings are given to the new one. So I can make shure,
998 // they do not duplicate themself and you cannnot make dirty things with
1001 void LyXText::setParagraph(bool line_top, bool line_bottom,
1002 bool pagebreak_top, bool pagebreak_bottom,
1003 VSpace const & space_top,
1004 VSpace const & space_bottom,
1005 Spacing const & spacing,
1007 string const & labelwidthstring,
1010 LyXCursor tmpcursor = cursor;
1011 if (!selection.set()) {
1012 selection.start = cursor;
1013 selection.end = cursor;
1016 // make sure that the depth behind the selection are restored, too
1017 Paragraph * endpar = selection.end.par()->next();
1018 Paragraph * undoendpar = endpar;
1020 if (endpar && endpar->getDepth()) {
1021 while (endpar && endpar->getDepth()) {
1022 endpar = endpar->next();
1023 undoendpar = endpar;
1027 // because of parindents etc.
1028 endpar = endpar->next();
1031 setUndo(bv(), Undo::EDIT, selection.start.par(), undoendpar);
1034 Paragraph * tmppar = selection.end.par();
1036 while (tmppar != selection.start.par()->previous()) {
1037 setCursor(tmppar, 0);
1038 postPaint(cursor.y() - cursor.row()->baseline());
1039 cursor.par()->params().lineTop(line_top);
1040 cursor.par()->params().lineBottom(line_bottom);
1041 cursor.par()->params().pagebreakTop(pagebreak_top);
1042 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1043 cursor.par()->params().spaceTop(space_top);
1044 cursor.par()->params().spaceBottom(space_bottom);
1045 cursor.par()->params().spacing(spacing);
1046 // does the layout allow the new alignment?
1047 LyXLayout_ptr const & layout = cursor.par()->layout();
1049 if (align == LYX_ALIGN_LAYOUT)
1050 align = layout->align;
1051 if (align & layout->alignpossible) {
1052 if (align == layout->align)
1053 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1055 cursor.par()->params().align(align);
1057 cursor.par()->setLabelWidthString(labelwidthstring);
1058 cursor.par()->params().noindent(noindent);
1059 tmppar = cursor.par()->previous();
1062 redoParagraphs(selection.start, endpar);
1065 setCursor(selection.start.par(), selection.start.pos());
1066 selection.cursor = cursor;
1067 setCursor(selection.end.par(), selection.end.pos());
1069 setCursor(tmpcursor.par(), tmpcursor.pos());
1071 bv()->updateInset(inset_owner);
1075 // set the counter of a paragraph. This includes the labels
1076 void LyXText::setCounter(Buffer const * buf, Paragraph * par)
1078 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1079 LyXLayout_ptr const & layout = par->layout();
1081 if (par->previous()) {
1083 par->params().appendix(par->previous()->params().appendix());
1084 if (!par->params().appendix() && par->params().startOfAppendix()) {
1085 par->params().appendix(true);
1086 textclass.counters().reset();
1088 par->enumdepth = par->previous()->enumdepth;
1089 par->itemdepth = par->previous()->itemdepth;
1091 par->params().appendix(par->params().startOfAppendix());
1096 /* Maybe we have to increment the enumeration depth.
1097 * BUT, enumeration in a footnote is considered in isolation from its
1098 * surrounding paragraph so don't increment if this is the
1099 * first line of the footnote
1100 * AND, bibliographies can't have their depth changed ie. they
1101 * are always of depth 0
1104 && par->previous()->getDepth() < par->getDepth()
1105 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1106 && par->enumdepth < 3
1107 && layout->labeltype != LABEL_BIBLIO) {
1111 // Maybe we have to decrement the enumeration depth, see note above
1113 && par->previous()->getDepth() > par->getDepth()
1114 && layout->labeltype != LABEL_BIBLIO) {
1115 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1118 if (!par->params().labelString().empty()) {
1119 par->params().labelString(string());
1122 if (layout->margintype == MARGIN_MANUAL) {
1123 if (par->params().labelWidthString().empty()) {
1124 par->setLabelWidthString(layout->labelstring());
1127 par->setLabelWidthString(string());
1130 // is it a layout that has an automatic label?
1131 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1132 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1136 if (i >= 0 && i <= buf->params.secnumdepth) {
1140 textclass.counters().step(layout->latexname());
1142 // Is there a label? Useful for Chapter layout
1143 if (!par->params().appendix()) {
1144 s << layout->labelstring();
1146 s << layout->labelstring_appendix();
1149 // Use of an integer is here less than elegant. For now.
1150 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1151 if (!par->params().appendix()) {
1152 numbertype = "sectioning";
1154 numbertype = "appendix";
1155 if (par->isRightToLeftPar(buf->params))
1156 langtype = "hebrew";
1161 s << textclass.counters()
1162 .numberLabel(layout->latexname(),
1163 numbertype, langtype, head);
1165 par->params().labelString(STRCONV(s.str()));
1167 // reset enum counters
1168 textclass.counters().reset("enum");
1169 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1170 textclass.counters().reset("enum");
1171 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1173 // Yes I know this is a really, really! bad solution
1175 string enumcounter("enum");
1177 switch (par->enumdepth) {
1186 enumcounter += "iv";
1189 // not a valid enumdepth...
1193 textclass.counters().step(enumcounter);
1195 s << textclass.counters()
1196 .numberLabel(enumcounter, "enumeration");
1197 par->params().labelString(STRCONV(s.str()));
1199 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1200 textclass.counters().step("bibitem");
1201 int number = textclass.counters().value("bibitem");
1202 if (par->bibitem()) {
1203 par->bibitem()->setCounter(number);
1204 par->params().labelString(layout->labelstring());
1206 // In biblio should't be following counters but...
1208 string s = layout->labelstring();
1210 // the caption hack:
1211 if (layout->labeltype == LABEL_SENSITIVE) {
1212 Paragraph * tmppar = par;
1215 while (tmppar && tmppar->inInset()
1216 // the single '=' is intended below
1217 && (in = tmppar->inInset()->owner())) {
1218 if (in->lyxCode() == Inset::FLOAT_CODE ||
1219 in->lyxCode() == Inset::WRAP_CODE) {
1223 tmppar = in->parOwner();
1229 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1231 textclass.counters().step(fl.type());
1233 // Doesn't work... yet.
1234 #if USE_BOOST_FORMAT
1235 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1236 // s << boost::format(_("%1$s %1$d:")
1238 // % buf->counters().value(fl.name());
1241 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1242 o << fl.name() << " #:";
1243 s = STRCONV(o.str());
1246 // par->SetLayout(0);
1247 // s = layout->labelstring;
1248 s = _("Senseless: ");
1251 par->params().labelString(s);
1253 // reset the enumeration counter. They are always reset
1254 // when there is any other layout between
1255 // Just fall-through between the cases so that all
1256 // enum counters deeper than enumdepth is also reset.
1257 switch (par->enumdepth) {
1259 textclass.counters().reset("enumi");
1261 textclass.counters().reset("enumii");
1263 textclass.counters().reset("enumiii");
1265 textclass.counters().reset("enumiv");
1271 // Updates all counters. Paragraphs with changed label string will be rebroken
1272 void LyXText::updateCounters()
1274 RowList::iterator rowit = rows().begin();
1275 Paragraph * par = rowit->par();
1277 // CHECK if this is really needed. (Lgb)
1278 bv()->buffer()->params.getLyXTextClass().counters().reset();
1281 while (rowit->par() != par)
1284 string const oldLabel = par->params().labelString();
1286 // setCounter can potentially change the labelString.
1287 setCounter(bv()->buffer(), par);
1289 string const & newLabel = par->params().labelString();
1291 if (oldLabel.empty() && !newLabel.empty()) {
1292 removeParagraph(rowit);
1293 appendParagraph(rowit);
1301 void LyXText::insertInset(Inset * inset)
1303 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1305 setUndo(bv(), Undo::FINISH, cursor.par(), cursor.par()->next());
1307 cursor.par()->insertInset(cursor.pos(), inset);
1308 // Just to rebreak and refresh correctly.
1309 // The character will not be inserted a second time
1310 insertChar(Paragraph::META_INSET);
1311 // If we enter a highly editable inset the cursor should be to before
1312 // the inset. This couldn't happen before as Undo was not handled inside
1313 // inset now after the Undo LyX tries to call inset->Edit(...) again
1314 // and cannot do this as the cursor is behind the inset and GetInset
1315 // does not return the inset!
1316 if (isHighlyEditableInset(inset)) {
1323 void LyXText::copyEnvironmentType()
1325 copylayouttype = cursor.par()->layout()->name();
1329 void LyXText::pasteEnvironmentType()
1331 // do nothing if there has been no previous copyEnvironmentType()
1332 if (!copylayouttype.empty())
1333 setLayout(copylayouttype);
1337 void LyXText::cutSelection(bool doclear, bool realcut)
1339 // Stuff what we got on the clipboard. Even if there is no selection.
1341 // There is a problem with having the stuffing here in that the
1342 // larger the selection the slower LyX will get. This can be
1343 // solved by running the line below only when the selection has
1344 // finished. The solution used currently just works, to make it
1345 // faster we need to be more clever and probably also have more
1346 // calls to stuffClipboard. (Lgb)
1347 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1349 // This doesn't make sense, if there is no selection
1350 if (!selection.set())
1353 // OK, we have a selection. This is always between selection.start
1354 // and selection.end
1356 // make sure that the depth behind the selection are restored, too
1357 Paragraph * endpar = selection.end.par()->next();
1358 Paragraph * undoendpar = endpar;
1360 if (endpar && endpar->getDepth()) {
1361 while (endpar && endpar->getDepth()) {
1362 endpar = endpar->next();
1363 undoendpar = endpar;
1365 } else if (endpar) {
1366 endpar = endpar->next(); // because of parindents etc.
1369 setUndo(bv(), Undo::DELETE,
1370 selection.start.par(), undoendpar);
1372 // there are two cases: cut only within one paragraph or
1373 // more than one paragraph
1374 if (selection.start.par() == selection.end.par()) {
1375 // only within one paragraph
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,
1382 selection.end.pos(pos);
1384 endpar = selection.end.par();
1385 int pos = selection.end.pos();
1386 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1387 selection.start.pos(), pos,
1388 bv()->buffer()->params.textclass,
1391 selection.end.par(endpar);
1392 selection.end.pos(pos);
1393 cursor.pos(selection.end.pos());
1395 endpar = endpar->next();
1397 // sometimes necessary
1399 selection.start.par()->stripLeadingSpaces();
1401 redoParagraphs(selection.start, endpar);
1403 // cutSelection can invalidate the cursor so we need to set
1405 // we prefer the end for when tracking changes
1406 cursor = selection.end;
1408 // need a valid cursor. (Lgb)
1411 setCursor(cursor.par(), cursor.pos());
1412 selection.cursor = cursor;
1417 void LyXText::copySelection()
1419 // stuff the selection onto the X clipboard, from an explicit copy request
1420 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1422 // this doesnt make sense, if there is no selection
1423 if (!selection.set())
1426 // ok we have a selection. This is always between selection.start
1427 // and sel_end cursor
1429 // copy behind a space if there is one
1430 while (selection.start.par()->size() > selection.start.pos()
1431 && selection.start.par()->isLineSeparator(selection.start.pos())
1432 && (selection.start.par() != selection.end.par()
1433 || selection.start.pos() < selection.end.pos()))
1434 selection.start.pos(selection.start.pos() + 1);
1436 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1437 selection.start.pos(), selection.end.pos(),
1438 bv()->buffer()->params.textclass);
1442 void LyXText::pasteSelection()
1444 // this does not make sense, if there is nothing to paste
1445 if (!CutAndPaste::checkPastePossible())
1448 setUndo(bv(), Undo::INSERT,
1449 cursor.par(), cursor.par()->next());
1452 Paragraph * actpar = cursor.par();
1453 int pos = cursor.pos();
1455 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1456 bv()->buffer()->params.textclass);
1458 redoParagraphs(cursor, endpar);
1460 setCursor(cursor.par(), cursor.pos());
1463 selection.cursor = cursor;
1464 setCursor(actpar, pos);
1470 void LyXText::setSelectionRange(lyx::pos_type length)
1475 selection.cursor = cursor;
1482 // simple replacing. The font of the first selected character is used
1483 void LyXText::replaceSelectionWithString(string const & str)
1485 setCursorParUndo(bv());
1488 if (!selection.set()) { // create a dummy selection
1489 selection.end = cursor;
1490 selection.start = cursor;
1493 // Get font setting before we cut
1494 pos_type pos = selection.end.pos();
1495 LyXFont const font = selection.start.par()
1496 ->getFontSettings(bv()->buffer()->params,
1497 selection.start.pos());
1499 // Insert the new string
1500 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1501 selection.end.par()->insertChar(pos, (*cit), font);
1505 // Cut the selection
1506 cutSelection(true, false);
1512 // needed to insert the selection
1513 void LyXText::insertStringAsLines(string const & str)
1515 Paragraph * par = cursor.par();
1516 pos_type pos = cursor.pos();
1517 Paragraph * endpar = cursor.par()->next();
1519 setCursorParUndo(bv());
1521 // only to be sure, should not be neccessary
1524 bv()->buffer()->insertStringAsLines(par, pos, current_font, str);
1526 redoParagraphs(cursor, endpar);
1527 setCursor(cursor.par(), cursor.pos());
1528 selection.cursor = cursor;
1529 setCursor(par, pos);
1534 // turns double-CR to single CR, others where converted into one
1535 // blank. Then InsertStringAsLines is called
1536 void LyXText::insertStringAsParagraphs(string const & str)
1538 string linestr(str);
1539 bool newline_inserted = false;
1540 for (string::size_type i = 0; i < linestr.length(); ++i) {
1541 if (linestr[i] == '\n') {
1542 if (newline_inserted) {
1543 // we know that \r will be ignored by
1544 // InsertStringA. Of course, it is a dirty
1545 // trick, but it works...
1546 linestr[i - 1] = '\r';
1550 newline_inserted = true;
1552 } else if (IsPrintable(linestr[i])) {
1553 newline_inserted = false;
1556 insertStringAsLines(linestr);
1560 void LyXText::checkParagraph(Paragraph * par, pos_type pos)
1562 LyXCursor tmpcursor;
1566 RowList::iterator row = getRow(par, pos, y);
1567 RowList::iterator beg = rows().begin();
1569 // is there a break one row above
1571 && boost::prior(row)->par() == row->par()) {
1572 z = rowBreakPoint(*boost::prior(row));
1573 if (z >= row->pos()) {
1574 // set the dimensions of the row above
1575 y -= boost::prior(row)->height();
1578 breakAgain(boost::prior(row));
1580 // set the cursor again. Otherwise
1581 // dangling pointers are possible
1582 setCursor(cursor.par(), cursor.pos(),
1583 false, cursor.boundary());
1584 selection.cursor = cursor;
1589 int const tmpheight = row->height();
1590 pos_type const tmplast = lastPos(*this, row);
1593 if (row->height() == tmpheight && lastPos(*this, row) == tmplast) {
1594 postRowPaint(row, y);
1599 // check the special right address boxes
1600 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1607 redoDrawingOfParagraph(tmpcursor);
1610 // set the cursor again. Otherwise dangling pointers are possible
1611 // also set the selection
1613 if (selection.set()) {
1615 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1616 false, selection.cursor.boundary());
1617 selection.cursor = cursor;
1618 setCursorIntern(selection.start.par(),
1619 selection.start.pos(),
1620 false, selection.start.boundary());
1621 selection.start = cursor;
1622 setCursorIntern(selection.end.par(),
1623 selection.end.pos(),
1624 false, selection.end.boundary());
1625 selection.end = cursor;
1626 setCursorIntern(last_sel_cursor.par(),
1627 last_sel_cursor.pos(),
1628 false, last_sel_cursor.boundary());
1629 last_sel_cursor = cursor;
1632 setCursorIntern(cursor.par(), cursor.pos(),
1633 false, cursor.boundary());
1637 // returns false if inset wasn't found
1638 bool LyXText::updateInset(Inset * inset)
1640 // first check the current paragraph
1641 int pos = cursor.par()->getPositionOfInset(inset);
1643 checkParagraph(cursor.par(), pos);
1647 // check every paragraph
1649 Paragraph * par = ownerParagraph();
1651 pos = par->getPositionOfInset(inset);
1653 checkParagraph(par, pos);
1663 bool LyXText::setCursor(Paragraph * par,
1665 bool setfont, bool boundary)
1667 LyXCursor old_cursor = cursor;
1668 setCursorIntern(par, pos, setfont, boundary);
1669 return deleteEmptyParagraphMechanism(old_cursor);
1673 void LyXText::setCursor(LyXCursor & cur, Paragraph * par,
1674 pos_type pos, bool boundary)
1680 cur.boundary(boundary);
1682 // get the cursor y position in text
1684 RowList::iterator row = getRow(par, pos, y);
1685 RowList::iterator beg = rows().begin();
1687 RowList::iterator old_row = row;
1689 // if we are before the first char of this row and are still in the
1690 // same paragraph and there is a previous row then put the cursor on
1691 // the end of the previous row
1692 cur.iy(y + row->baseline());
1694 if (row != beg && pos &&
1695 boost::prior(row)->par() == row->par() &&
1696 pos < par->size() &&
1697 par->getChar(pos) == Paragraph::META_INSET &&
1698 (ins = par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1705 // y is now the beginning of the cursor row
1706 y += row->baseline();
1707 // y is now the cursor baseline
1710 pos_type last = lastPrintablePos(*this, old_row);
1712 // None of these should happen, but we're scaredy-cats
1713 if (pos > par->size()) {
1714 lyxerr << "dont like 1 please report" << endl;
1717 } else if (pos > last + 1) {
1718 lyxerr << "dont like 2 please report" << endl;
1719 // This shouldn't happen.
1722 } else if (pos < row->pos()) {
1723 lyxerr << "dont like 3 please report" << endl;
1728 // now get the cursors x position
1729 float x = getCursorX(row, pos, last, boundary);
1732 if (old_row != row) {
1733 x = getCursorX(old_row, pos, last, boundary);
1737 //if the cursor is in a visible row, anchor to it
1739 if (topy < y && y < topy + bv()->workHeight())
1744 float LyXText::getCursorX(RowList::iterator rit,
1745 pos_type pos, pos_type last, bool boundary) const
1747 pos_type cursor_vpos = 0;
1749 float fill_separator;
1751 float fill_label_hfill;
1752 // This call HAS to be here because of the BidiTables!!!
1753 prepareToPrint(rit, x, fill_separator, fill_hfill,
1756 if (last < rit->pos())
1757 cursor_vpos = rit->pos();
1758 else if (pos > last && !boundary)
1759 cursor_vpos = (rit->par()->isRightToLeftPar(bv()->buffer()->params))
1760 ? rit->pos() : last + 1;
1761 else if (pos > rit->pos() &&
1762 (pos > last || boundary))
1763 /// Place cursor after char at (logical) position pos - 1
1764 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1765 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1767 /// Place cursor before char at (logical) position pos
1768 cursor_vpos = (bidi_level(pos) % 2 == 0)
1769 ? log2vis(pos) : log2vis(pos) + 1;
1771 pos_type body_pos = rit->par()->beginningOfBody();
1772 if ((body_pos > 0) &&
1773 ((body_pos - 1 > last) ||
1774 !rit->par()->isLineSeparator(body_pos - 1)))
1777 for (pos_type vpos = rit->pos(); vpos < cursor_vpos; ++vpos) {
1778 pos_type pos = vis2log(vpos);
1779 if (body_pos > 0 && pos == body_pos - 1) {
1780 x += fill_label_hfill +
1781 font_metrics::width(
1782 rit->par()->layout()->labelsep,
1783 getLabelFont(bv()->buffer(),
1785 if (rit->par()->isLineSeparator(body_pos - 1))
1786 x -= singleWidth(rit->par(), body_pos - 1);
1789 if (hfillExpansion(*this, rit, pos)) {
1790 x += singleWidth(rit->par(), pos);
1791 if (pos >= body_pos)
1794 x += fill_label_hfill;
1795 } else if (rit->par()->isSeparator(pos)) {
1796 x += singleWidth(rit->par(), pos);
1797 if (pos >= body_pos)
1798 x += fill_separator;
1800 x += singleWidth(rit->par(), pos);
1806 void LyXText::setCursorIntern(Paragraph * par,
1807 pos_type pos, bool setfont, bool boundary)
1809 InsetText * it = static_cast<InsetText *>(par->inInset());
1811 if (it != inset_owner) {
1812 lyxerr[Debug::INSETS] << "InsetText is " << it
1814 << "inset_owner is "
1815 << inset_owner << endl;
1816 #ifdef WITH_WARNINGS
1817 #warning I believe this code is wrong. (Lgb)
1818 #warning Jürgen, have a look at this. (Lgb)
1819 #warning Hmmm, I guess you are right but we
1820 #warning should verify when this is needed
1822 // Jürgen, would you like to have a look?
1823 // I guess we need to move the outer cursor
1824 // and open and lock the inset (bla bla bla)
1825 // stuff I don't know... so can you have a look?
1827 // I moved the lyxerr stuff in here so we can see if
1828 // this is actually really needed and where!
1830 // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1835 setCursor(cursor, par, pos, boundary);
1841 void LyXText::setCurrentFont()
1843 pos_type pos = cursor.pos();
1844 if (cursor.boundary() && pos > 0)
1848 if (pos == cursor.par()->size())
1850 else // potentional bug... BUG (Lgb)
1851 if (cursor.par()->isSeparator(pos)) {
1852 if (pos > cursor.row()->pos() &&
1853 bidi_level(pos) % 2 ==
1854 bidi_level(pos - 1) % 2)
1856 else if (pos + 1 < cursor.par()->size())
1862 cursor.par()->getFontSettings(bv()->buffer()->params, pos);
1863 real_current_font = getFont(bv()->buffer(), cursor.par(), pos);
1865 if (cursor.pos() == cursor.par()->size() &&
1866 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()) &&
1867 !cursor.boundary()) {
1868 Language const * lang =
1869 cursor.par()->getParLanguage(bv()->buffer()->params);
1870 current_font.setLanguage(lang);
1871 current_font.setNumber(LyXFont::OFF);
1872 real_current_font.setLanguage(lang);
1873 real_current_font.setNumber(LyXFont::OFF);
1878 // returns the column near the specified x-coordinate of the row
1879 // x is set to the real beginning of this column
1881 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1884 float fill_separator;
1886 float fill_label_hfill;
1888 prepareToPrint(rit, tmpx, fill_separator,
1889 fill_hfill, fill_label_hfill);
1891 pos_type vc = rit->pos();
1892 pos_type last = lastPrintablePos(*this, rit);
1895 LyXLayout_ptr const & layout = rit->par()->layout();
1897 bool left_side = false;
1899 pos_type body_pos = rit->par()->beginningOfBody();
1900 float last_tmpx = tmpx;
1903 (body_pos - 1 > last ||
1904 !rit->par()->isLineSeparator(body_pos - 1)))
1907 // check for empty row
1908 if (!rit->par()->size()) {
1913 while (vc <= last && tmpx <= x) {
1916 if (body_pos > 0 && c == body_pos - 1) {
1917 tmpx += fill_label_hfill +
1918 font_metrics::width(layout->labelsep,
1919 getLabelFont(bv()->buffer(), rit->par()));
1920 if (rit->par()->isLineSeparator(body_pos - 1))
1921 tmpx -= singleWidth(rit->par(), body_pos - 1);
1924 if (hfillExpansion(*this, rit, c)) {
1925 tmpx += singleWidth(rit->par(), c);
1929 tmpx += fill_label_hfill;
1930 } else if (rit->par()->isSeparator(c)) {
1931 tmpx += singleWidth(rit->par(), c);
1933 tmpx+= fill_separator;
1935 tmpx += singleWidth(rit->par(), c);
1940 if ((tmpx + last_tmpx) / 2 > x) {
1945 if (vc > last + 1) // This shouldn't happen.
1949 // This (rtl_support test) is not needed, but gives
1950 // some speedup if rtl_support=false
1951 bool const lastrow = lyxrc.rtl_support &&
1952 (boost::next(rit) == rowlist_.end() ||
1953 boost::next(rit)->par() != rit->par());
1954 // If lastrow is false, we don't need to compute
1955 // the value of rtl.
1956 bool const rtl = (lastrow)
1957 ? rit->par()->isRightToLeftPar(bv()->buffer()->params)
1960 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1961 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1963 else if (vc == rit->pos()) {
1965 if (bidi_level(c) % 2 == 1)
1968 c = vis2log(vc - 1);
1969 bool const rtl = (bidi_level(c) % 2 == 1);
1970 if (left_side == rtl) {
1972 boundary = isBoundary(bv()->buffer(), rit->par(), c);
1976 if (rit->pos() <= last && c > last
1977 && rit->par()->isNewline(last)) {
1978 if (bidi_level(last) % 2 == 0)
1979 tmpx -= singleWidth(rit->par(), last);
1981 tmpx += singleWidth(rit->par(), last);
1991 void LyXText::setCursorFromCoordinates(int x, int y)
1993 LyXCursor old_cursor = cursor;
1995 setCursorFromCoordinates(cursor, x, y);
1997 deleteEmptyParagraphMechanism(old_cursor);
2004 * return true if the cursor given is at the end of a row,
2005 * and the next row is filled by an inset that spans an entire
2008 bool beforeFullRowInset(LyXText & lt, RowList::iterator row,
2010 if (boost::next(row) == lt.rows().end())
2012 Row const & next = *boost::next(row);
2014 if (next.pos() != cur.pos() || next.par() != cur.par())
2016 if (!cur.par()->isInset(cur.pos()))
2018 Inset const * inset = cur.par()->getInset(cur.pos());
2019 if (inset->needFullRow() || inset->display())
2026 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
2028 // Get the row first.
2030 RowList::iterator row = getRowNearY(y);
2032 pos_type const column = getColumnNearX(row, x, bound);
2033 cur.par(row->par());
2034 cur.pos(row->pos() + column);
2036 cur.y(y + row->baseline());
2039 if (beforeFullRowInset(*this, row, cur)) {
2040 pos_type last = lastPrintablePos(*this, row);
2041 float x = getCursorX(boost::next(row), cur.pos(), last, bound);
2043 cur.iy(y + row->height() + boost::next(row)->baseline());
2044 cur.irow(boost::next(row));
2050 cur.boundary(bound);
2054 void LyXText::cursorLeft(bool internal)
2056 if (cursor.pos() > 0) {
2057 bool boundary = cursor.boundary();
2058 setCursor(cursor.par(), cursor.pos() - 1, true, false);
2059 if (!internal && !boundary &&
2060 isBoundary(bv()->buffer(), cursor.par(), cursor.pos() + 1))
2061 setCursor(cursor.par(), cursor.pos() + 1, true, true);
2062 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2063 Paragraph * par = cursor.par()->previous();
2064 setCursor(par, par->size());
2069 void LyXText::cursorRight(bool internal)
2071 if (!internal && cursor.boundary() &&
2072 !cursor.par()->isNewline(cursor.pos()))
2073 setCursor(cursor.par(), cursor.pos(), true, false);
2074 else if (cursor.pos() < cursor.par()->size()) {
2075 setCursor(cursor.par(), cursor.pos() + 1, true, false);
2077 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()))
2078 setCursor(cursor.par(), cursor.pos(), true, true);
2079 } else if (cursor.par()->next())
2080 setCursor(cursor.par()->next(), 0);
2084 void LyXText::cursorUp(bool selecting)
2087 int x = cursor.x_fix();
2088 int y = cursor.y() - cursor.row()->baseline() - 1;
2089 setCursorFromCoordinates(x, y);
2092 int y1 = cursor.iy() - topy;
2095 Inset * inset_hit = checkInsetHit(x, y1);
2096 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2097 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2101 setCursorFromCoordinates(bv(), cursor.x_fix(),
2102 cursor.y() - cursor.row()->baseline() - 1);
2107 void LyXText::cursorDown(bool selecting)
2110 int x = cursor.x_fix();
2111 int y = cursor.y() - cursor.row()->baseline() +
2112 cursor.row()->height() + 1;
2113 setCursorFromCoordinates(x, y);
2114 if (!selecting && cursor.row() == cursor.irow()) {
2116 int y1 = cursor.iy() - topy;
2119 Inset * inset_hit = checkInsetHit(x, y1);
2120 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2121 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2125 setCursorFromCoordinates(bv(), cursor.x_fix(),
2126 cursor.y() - cursor.row()->baseline()
2127 + cursor.row()->height() + 1);
2132 void LyXText::cursorUpParagraph()
2134 if (cursor.pos() > 0) {
2135 setCursor(cursor.par(), 0);
2137 else if (cursor.par()->previous()) {
2138 setCursor(cursor.par()->previous(), 0);
2143 void LyXText::cursorDownParagraph()
2145 if (cursor.par()->next()) {
2146 setCursor(cursor.par()->next(), 0);
2148 setCursor(cursor.par(), cursor.par()->size());
2152 // fix the cursor `cur' after a characters has been deleted at `where'
2153 // position. Called by deleteEmptyParagraphMechanism
2154 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2155 LyXCursor const & where)
2157 // if cursor is not in the paragraph where the delete occured,
2159 if (cur.par() != where.par())
2162 // if cursor position is after the place where the delete occured,
2164 if (cur.pos() > where.pos())
2165 cur.pos(cur.pos()-1);
2167 // check also if we don't want to set the cursor on a spot behind the
2168 // pagragraph because we erased the last character.
2169 if (cur.pos() > cur.par()->size())
2170 cur.pos(cur.par()->size());
2172 // recompute row et al. for this cursor
2173 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2177 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2179 // Would be wrong to delete anything if we have a selection.
2180 if (selection.set())
2183 // We allow all kinds of "mumbo-jumbo" when freespacing.
2184 if (old_cursor.par()->layout()->free_spacing
2185 || old_cursor.par()->isFreeSpacing()) {
2189 /* Ok I'll put some comments here about what is missing.
2190 I have fixed BackSpace (and thus Delete) to not delete
2191 double-spaces automagically. I have also changed Cut,
2192 Copy and Paste to hopefully do some sensible things.
2193 There are still some small problems that can lead to
2194 double spaces stored in the document file or space at
2195 the beginning of paragraphs. This happens if you have
2196 the cursor betwenn to spaces and then save. Or if you
2197 cut and paste and the selection have a space at the
2198 beginning and then save right after the paste. I am
2199 sure none of these are very hard to fix, but I will
2200 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2201 that I can get some feedback. (Lgb)
2204 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2205 // delete the LineSeparator.
2208 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2209 // delete the LineSeparator.
2212 // If the pos around the old_cursor were spaces, delete one of them.
2213 if (old_cursor.par() != cursor.par()
2214 || old_cursor.pos() != cursor.pos()) {
2215 // Only if the cursor has really moved
2217 if (old_cursor.pos() > 0
2218 && old_cursor.pos() < old_cursor.par()->size()
2219 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2220 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2221 old_cursor.par()->erase(old_cursor.pos() - 1);
2222 redoParagraphs(old_cursor, old_cursor.par()->next());
2224 #ifdef WITH_WARNINGS
2225 #warning This will not work anymore when we have multiple views of the same buffer
2226 // In this case, we will have to correct also the cursors held by
2227 // other bufferviews. It will probably be easier to do that in a more
2228 // automated way in LyXCursor code. (JMarc 26/09/2001)
2230 // correct all cursors held by the LyXText
2231 fixCursorAfterDelete(cursor, old_cursor);
2232 fixCursorAfterDelete(selection.cursor,
2234 fixCursorAfterDelete(selection.start,
2236 fixCursorAfterDelete(selection.end, old_cursor);
2237 fixCursorAfterDelete(last_sel_cursor,
2239 fixCursorAfterDelete(toggle_cursor, old_cursor);
2240 fixCursorAfterDelete(toggle_end_cursor,
2246 // don't delete anything if this is the ONLY paragraph!
2247 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2250 // Do not delete empty paragraphs with keepempty set.
2251 if (old_cursor.par()->layout()->keepempty)
2254 // only do our magic if we changed paragraph
2255 if (old_cursor.par() == cursor.par())
2258 // record if we have deleted a paragraph
2259 // we can't possibly have deleted a paragraph before this point
2260 bool deleted = false;
2262 if ((old_cursor.par()->empty()
2263 || (old_cursor.par()->size() == 1
2264 && old_cursor.par()->isLineSeparator(0)))) {
2265 // ok, we will delete anything
2266 LyXCursor tmpcursor;
2270 if (old_cursor.row() != rows().begin()) {
2271 const_cast<LyXText *>(this)->postPaint(old_cursor.y() - old_cursor.row()->baseline()
2272 - boost::prior(old_cursor.row())->height());
2274 cursor = old_cursor; // that undo can restore the right cursor position
2275 Paragraph * endpar = old_cursor.par()->next();
2276 if (endpar && endpar->getDepth()) {
2277 while (endpar && endpar->getDepth()) {
2278 endpar = endpar->next();
2281 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2285 removeRow(old_cursor.row());
2286 if (ownerParagraph() == old_cursor.par()) {
2287 ownerParagraph(ownerParagraph()->next());
2290 delete old_cursor.par();
2292 /* Breakagain the next par. Needed because of
2293 * the parindent that can occur or dissappear.
2294 * The next row can change its height, if
2295 * there is another layout before */
2296 if (refresh_row != rows().end()) {
2297 if (boost::next(refresh_row) != rows().end()) {
2298 breakAgain(boost::next(refresh_row));
2301 setHeightOfRow(refresh_row);
2304 RowList::iterator nextrow = boost::next(old_cursor.row());
2305 const_cast<LyXText *>(this)->postPaint(
2306 old_cursor.y() - old_cursor.row()->baseline());
2309 cursor = old_cursor; // that undo can restore the right cursor position
2310 Paragraph * endpar = old_cursor.par()->next();
2311 if (endpar && endpar->getDepth()) {
2312 while (endpar && endpar->getDepth()) {
2313 endpar = endpar->next();
2316 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2320 removeRow(old_cursor.row());
2322 if (ownerParagraph() == old_cursor.par()) {
2323 ownerParagraph(ownerParagraph()->next());
2326 delete old_cursor.par();
2328 /* Breakagain the next par. Needed because of
2329 the parindent that can occur or dissappear.
2330 The next row can change its height, if
2331 there is another layout before */
2332 if (nextrow != rows().end()) {
2333 breakAgain(nextrow);
2339 setCursorIntern(cursor.par(), cursor.pos());
2341 if (selection.cursor.par() == old_cursor.par()
2342 && selection.cursor.pos() == old_cursor.pos()) {
2343 // correct selection
2344 selection.cursor = cursor;
2348 if (old_cursor.par()->stripLeadingSpaces()) {
2349 redoParagraphs(old_cursor,
2350 old_cursor.par()->next());
2352 setCursorIntern(cursor.par(), cursor.pos());
2353 selection.cursor = cursor;
2360 Paragraph * LyXText::ownerParagraph() const
2363 return inset_owner->paragraph();
2365 return &*(bv_owner->buffer()->paragraphs.begin());
2369 void LyXText::ownerParagraph(Paragraph * p) const
2372 inset_owner->paragraph(p);
2374 bv_owner->buffer()->paragraphs.set(p);
2379 void LyXText::ownerParagraph(int id, Paragraph * p) const
2381 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2382 if (op && op->inInset()) {
2383 static_cast<InsetText *>(op->inInset())->paragraph(p);
2390 LyXText::refresh_status LyXText::refreshStatus() const
2392 return refresh_status_;
2396 void LyXText::clearPaint()
2398 refresh_status_ = REFRESH_NONE;
2399 refresh_row = rows().end();
2404 void LyXText::postPaint(int start_y)
2406 refresh_status old = refresh_status_;
2408 refresh_status_ = REFRESH_AREA;
2409 refresh_row = rows().end();
2411 if (old != REFRESH_NONE && refresh_y < start_y)
2414 refresh_y = start_y;
2419 // We are an inset's lyxtext. Tell the top-level lyxtext
2420 // it needs to update the row we're in.
2421 LyXText * t = bv()->text;
2422 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2426 // FIXME: we should probably remove this y parameter,
2427 // make refresh_y be 0, and use row->y etc.
2428 void LyXText::postRowPaint(RowList::iterator rit, int start_y)
2430 if (refresh_status_ != REFRESH_NONE && refresh_y < start_y) {
2431 refresh_status_ = REFRESH_AREA;
2434 refresh_y = start_y;
2437 if (refresh_status_ == REFRESH_AREA)
2440 refresh_status_ = REFRESH_ROW;
2446 // We are an inset's lyxtext. Tell the top-level lyxtext
2447 // it needs to update the row we're in.
2448 LyXText * t = bv()->text;
2449 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2453 bool LyXText::isInInset() const
2455 // Sub-level has non-null bv owner and
2456 // non-null inset owner.
2457 return inset_owner != 0 && bv_owner != 0;
2461 int defaultRowHeight()
2463 LyXFont const font(LyXFont::ALL_SANE);
2464 return int(font_metrics::maxAscent(font)
2465 + font_metrics::maxDescent(font) * 1.5);