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 ParagraphList::iterator par = ownerParagraphs().begin();
92 ParagraphList::iterator end = ownerParagraphs().end();
94 current_font = getFont(bview->buffer(), &*par, 0);
96 for (; par != end; ++par) {
97 insertParagraph(&*par, rowlist_.end());
99 setCursorIntern(rowlist_.begin()->par(), 0);
100 selection.cursor = cursor;
108 LyXFont const realizeFont(LyXFont const & font,
112 LyXTextClass const & tclass = buf->params.getLyXTextClass();
113 LyXFont tmpfont(font);
114 Paragraph::depth_type par_depth = par->getDepth();
116 // Resolve against environment font information
117 while (par && par_depth && !tmpfont.resolved()) {
118 par = par->outerHook();
120 tmpfont.realize(par->layout()->font);
121 par_depth = par->getDepth();
125 tmpfont.realize(tclass.defaultfont());
133 // Gets the fully instantiated font at a given position in a paragraph
134 // Basically the same routine as Paragraph::getFont() in paragraph.C.
135 // The difference is that this one is used for displaying, and thus we
136 // are allowed to make cosmetic improvements. For instance make footnotes
138 // If position is -1, we get the layout font of the paragraph.
139 // If position is -2, we get the font of the manual label of the paragraph.
140 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
143 lyx::Assert(pos >= 0);
145 LyXLayout_ptr const & layout = par->layout();
147 // We specialize the 95% common case:
148 if (!par->getDepth()) {
149 if (layout->labeltype == LABEL_MANUAL
150 && pos < par->beginningOfBody()) {
152 LyXFont f = par->getFontSettings(buf->params, pos);
154 par->inInset()->getDrawFont(f);
155 return f.realize(layout->reslabelfont);
157 LyXFont f = par->getFontSettings(buf->params, pos);
159 par->inInset()->getDrawFont(f);
160 return f.realize(layout->resfont);
164 // The uncommon case need not be optimized as much
168 if (pos < par->beginningOfBody()) {
170 layoutfont = layout->labelfont;
173 layoutfont = layout->font;
176 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
177 tmpfont.realize(layoutfont);
180 par->inInset()->getDrawFont(tmpfont);
182 return realizeFont(tmpfont, buf, par);
186 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
188 LyXLayout_ptr const & layout = par->layout();
190 if (!par->getDepth()) {
191 return layout->resfont;
194 return realizeFont(layout->font, buf, par);
198 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
200 LyXLayout_ptr const & layout = par->layout();
202 if (!par->getDepth()) {
203 return layout->reslabelfont;
206 return realizeFont(layout->labelfont, buf, par);
210 void LyXText::setCharFont(Paragraph * par,
211 pos_type pos, LyXFont const & fnt,
214 Buffer const * buf = bv()->buffer();
215 LyXFont font = getFont(buf, par, pos);
216 font.update(fnt, buf->params.language, toggleall);
217 // Let the insets convert their font
218 if (par->isInset(pos)) {
219 Inset * inset = par->getInset(pos);
220 if (isEditableInset(inset)) {
221 UpdatableInset * uinset =
222 static_cast<UpdatableInset *>(inset);
223 uinset->setFont(bv(), fnt, toggleall, true);
227 // Plug thru to version below:
228 setCharFont(buf, par, pos, font);
232 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
233 pos_type pos, LyXFont const & fnt)
237 LyXTextClass const & tclass = buf->params.getLyXTextClass();
238 LyXLayout_ptr const & layout = par->layout();
240 // Get concrete layout font to reduce against
243 if (pos < par->beginningOfBody())
244 layoutfont = layout->labelfont;
246 layoutfont = layout->font;
248 // Realize against environment font information
249 if (par->getDepth()) {
250 Paragraph * tp = par;
251 while (!layoutfont.resolved() && tp && tp->getDepth()) {
252 tp = tp->outerHook();
254 layoutfont.realize(tp->layout()->font);
258 layoutfont.realize(tclass.defaultfont());
260 // Now, reduce font against full layout font
261 font.reduce(layoutfont);
263 par->setFont(pos, font);
267 // removes the row and reset the touched counters
268 void LyXText::removeRow(RowList::iterator rit)
270 /* FIXME: when we cache the bview, this should just
271 * become a postPaint(), I think */
272 if (refresh_row == rit) {
273 if (rit == rows().begin())
274 refresh_row = boost::next(rit);
276 refresh_row = boost::prior(rit);
278 // what about refresh_y
281 if (anchor_row_ == rit) {
282 if (rit != rows().begin()) {
283 anchor_row_ = boost::prior(rit);
284 anchor_row_offset_ += boost::prior(rit)->height();
286 anchor_row_ = boost::next(rit);
287 anchor_row_offset_ -= rit->height();
291 // the text becomes smaller
292 height -= rit->height();
298 // remove all following rows of the paragraph of the specified row.
299 void LyXText::removeParagraph(RowList::iterator rit)
301 Paragraph * tmppar = rit->par();
304 while (rit != rows().end() && rit->par() == tmppar) {
305 RowList::iterator tmprit = boost::next(rit);
312 void LyXText::insertParagraph(Paragraph * par, RowList::iterator rowit)
314 // insert a new row, starting at position 0
316 RowList::iterator rit = rowlist_.insert(rowit, newrow);
318 // and now append the whole paragraph before the new row
319 appendParagraph(rit);
323 Inset * LyXText::getInset() const
325 if (cursor.pos() < cursor.par()->size()
326 && cursor.par()->isInset(cursor.pos())) {
327 return cursor.par()->getInset(cursor.pos());
333 void LyXText::toggleInset()
335 Inset * inset = getInset();
336 // is there an editable inset at cursor position?
337 if (!isEditableInset(inset)) {
338 // No, try to see if we are inside a collapsable inset
339 if (inset_owner && inset_owner->owner()
340 && inset_owner->owner()->isOpen()) {
341 bv()->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
342 inset_owner->owner()->close(bv());
343 bv()->getLyXText()->cursorRight(bv());
347 //bv()->owner()->message(inset->editMessage());
349 // do we want to keep this?? (JMarc)
350 if (!isHighlyEditableInset(inset))
351 setCursorParUndo(bv());
353 if (inset->isOpen()) {
359 bv()->updateInset(inset);
363 /* used in setlayout */
364 // Asger is not sure we want to do this...
365 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
368 LyXLayout_ptr const & layout = par.layout();
371 for (pos_type pos = 0; pos < par.size(); ++pos) {
372 if (pos < par.beginningOfBody())
373 layoutfont = layout->labelfont;
375 layoutfont = layout->font;
377 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
378 tmpfont.reduce(layoutfont);
379 par.setFont(pos, tmpfont);
384 Paragraph * LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
385 LyXCursor & send_cur,
386 string const & layout)
388 Paragraph * endpar = send_cur.par()->next();
389 Paragraph * undoendpar = endpar;
391 if (endpar && endpar->getDepth()) {
392 while (endpar && endpar->getDepth()) {
393 endpar = endpar->next();
397 endpar = endpar->next(); // because of parindents etc.
400 setUndo(bv(), Undo::EDIT, sstart_cur.par(), undoendpar);
402 // ok we have a selection. This is always between sstart_cur
403 // and sel_end cursor
405 Paragraph * par = sstart_cur.par();
406 Paragraph * epar = send_cur.par()->next();
408 LyXLayout_ptr const & lyxlayout =
409 bv()->buffer()->params.getLyXTextClass()[layout];
412 par->applyLayout(lyxlayout);
413 makeFontEntriesLayoutSpecific(*bv()->buffer(), *par);
414 Paragraph * fppar = par;
415 fppar->params().spaceTop(lyxlayout->fill_top ?
416 VSpace(VSpace::VFILL)
417 : VSpace(VSpace::NONE));
418 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
419 VSpace(VSpace::VFILL)
420 : VSpace(VSpace::NONE));
421 if (lyxlayout->margintype == MARGIN_MANUAL)
422 par->setLabelWidthString(lyxlayout->labelstring());
425 } while (par != epar);
431 // set layout over selection and make a total rebreak of those paragraphs
432 void LyXText::setLayout(string const & layout)
434 LyXCursor tmpcursor = cursor; /* store the current cursor */
436 // if there is no selection just set the layout
437 // of the current paragraph */
438 if (!selection.set()) {
439 selection.start = cursor; // dummy selection
440 selection.end = cursor;
442 Paragraph * endpar = setLayout(cursor, selection.start,
443 selection.end, layout);
444 redoParagraphs(selection.start, endpar);
446 // we have to reset the selection, because the
447 // geometry could have changed
448 setCursor(selection.start.par(),
449 selection.start.pos(), false);
450 selection.cursor = cursor;
451 setCursor(selection.end.par(), selection.end.pos(), false);
455 setCursor(tmpcursor.par(), tmpcursor.pos(), true);
459 // increment depth over selection and
460 // make a total rebreak of those paragraphs
461 void LyXText::incDepth()
463 // If there is no selection, just use the current paragraph
464 if (!selection.set()) {
465 selection.start = cursor; // dummy selection
466 selection.end = cursor;
469 // We end at the next paragraph with depth 0
470 Paragraph * endpar = selection.end.par()->next();
472 Paragraph * undoendpar = endpar;
474 if (endpar && endpar->getDepth()) {
475 while (endpar && endpar->getDepth()) {
476 endpar = endpar->next();
480 endpar = endpar->next(); // because of parindents etc.
483 setUndo(bv(), Undo::EDIT,
484 selection.start.par(), undoendpar);
486 LyXCursor tmpcursor = cursor; // store the current cursor
488 // ok we have a selection. This is always between sel_start_cursor
489 // and sel_end cursor
490 cursor = selection.start;
493 // NOTE: you can't change the depth of a bibliography entry
494 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
495 Paragraph * prev = cursor.par()->previous();
498 if (cursor.par()->getDepth()
499 < prev->getMaxDepthAfter()) {
500 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
504 if (cursor.par() == selection.end.par())
506 cursor.par(cursor.par()->next());
509 redoParagraphs(selection.start, endpar);
511 // we have to reset visual the selection because the
512 // geometry could have changed
513 setCursor(selection.start.par(), selection.start.pos());
514 selection.cursor = cursor;
515 setCursor(selection.end.par(), selection.end.pos());
518 setCursor(tmpcursor.par(), tmpcursor.pos());
522 // decrement depth over selection and
523 // make a total rebreak of those paragraphs
524 void LyXText::decDepth()
526 // if there is no selection just set the layout
527 // of the current paragraph
528 if (!selection.set()) {
529 selection.start = cursor; // dummy selection
530 selection.end = cursor;
532 Paragraph * endpar = selection.end.par()->next();
533 Paragraph * undoendpar = endpar;
535 if (endpar && endpar->getDepth()) {
536 while (endpar && endpar->getDepth()) {
537 endpar = endpar->next();
541 endpar = endpar->next(); // because of parindents etc.
544 setUndo(bv(), Undo::EDIT,
545 selection.start.par(), undoendpar);
547 LyXCursor tmpcursor = cursor; // store the current cursor
549 // ok we have a selection. This is always between sel_start_cursor
550 // and sel_end cursor
551 cursor = selection.start;
554 if (cursor.par()->params().depth()) {
555 cursor.par()->params()
556 .depth(cursor.par()->params().depth() - 1);
558 if (cursor.par() == selection.end.par()) {
561 cursor.par(cursor.par()->next());
564 redoParagraphs(selection.start, endpar);
566 // we have to reset the visual selection because the
567 // geometry could have changed
568 setCursor(selection.start.par(), selection.start.pos());
569 selection.cursor = cursor;
570 setCursor(selection.end.par(), selection.end.pos());
573 setCursor(tmpcursor.par(), tmpcursor.pos());
577 // set font over selection and make a total rebreak of those paragraphs
578 void LyXText::setFont(LyXFont const & font, bool toggleall)
580 // if there is no selection just set the current_font
581 if (!selection.set()) {
582 // Determine basis font
584 if (cursor.pos() < cursor.par()->beginningOfBody()) {
585 layoutfont = getLabelFont(bv()->buffer(),
588 layoutfont = getLayoutFont(bv()->buffer(),
591 // Update current font
592 real_current_font.update(font,
593 bv()->buffer()->params.language,
596 // Reduce to implicit settings
597 current_font = real_current_font;
598 current_font.reduce(layoutfont);
599 // And resolve it completely
600 real_current_font.realize(layoutfont);
605 LyXCursor tmpcursor = cursor; // store the current cursor
607 // ok we have a selection. This is always between sel_start_cursor
608 // and sel_end cursor
610 setUndo(bv(), Undo::EDIT,
611 selection.start.par(), selection.end.par()->next());
613 cursor = selection.start;
614 while (cursor.par() != selection.end.par() ||
615 cursor.pos() < selection.end.pos())
617 if (cursor.pos() < cursor.par()->size()) {
618 // an open footnote should behave like a closed one
619 setCharFont(cursor.par(), cursor.pos(),
621 cursor.pos(cursor.pos() + 1);
624 cursor.par(cursor.par()->next());
629 redoParagraphs(selection.start, selection.end.par()->next());
631 // we have to reset the selection, because the
632 // geometry could have changed, but we keep
633 // it for user convenience
634 setCursor(selection.start.par(), selection.start.pos());
635 selection.cursor = cursor;
636 setCursor(selection.end.par(), selection.end.pos());
638 setCursor(tmpcursor.par(), tmpcursor.pos(), true,
639 tmpcursor.boundary());
643 void LyXText::redoHeightOfParagraph()
645 RowList::iterator tmprow = cursor.row();
646 int y = cursor.y() - tmprow->baseline();
648 setHeightOfRow(tmprow);
650 while (tmprow != rows().begin()
651 && boost::prior(tmprow)->par() == tmprow->par()) {
653 y -= tmprow->height();
654 setHeightOfRow(tmprow);
659 setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
663 void LyXText::redoDrawingOfParagraph(LyXCursor const & cur)
665 RowList::iterator tmprow = cur.row();
667 int y = cur.y() - tmprow->baseline();
668 setHeightOfRow(tmprow);
670 while (tmprow != rows().begin()
671 && boost::prior(tmprow)->par() == tmprow->par()) {
673 y -= tmprow->height();
677 setCursor(cur.par(), cur.pos());
681 // deletes and inserts again all paragaphs between the cursor
682 // and the specified par
683 // This function is needed after SetLayout and SetFont etc.
684 void LyXText::redoParagraphs(LyXCursor const & cur,
685 Paragraph const * endpar)
687 RowList::iterator tmprit = cur.row();
689 int y = cur.y() - tmprit->baseline();
691 Paragraph * first_phys_par;
692 if (tmprit == rows().begin()) {
693 // A trick/hack for UNDO.
694 // This is needed because in an UNDO/REDO we could have
695 // changed the ownerParagrah() so the paragraph inside
696 // the row is NOT my really first par anymore.
697 // Got it Lars ;) (Jug 20011206)
698 first_phys_par = &*ownerParagraphs().begin();
700 // In here prevrit could be set to rows().end(). (Lgb)
702 first_phys_par = tmprit->par();
703 while (tmprit != rows().begin()
704 && boost::prior(tmprit)->par() == first_phys_par)
707 y -= tmprit->height();
710 // Is it possible to put the prevrit setting in here? (Lgb)
713 RowList::iterator prevrit;
714 bool good_prevrit = false;
716 // It seems to mee that good_prevrit is not needed if we let
717 // a bad prevrit have the value rows().end() (Lgb)
718 if (tmprit != rows().begin()) {
719 prevrit = boost::prior(tmprit);
724 while (tmprit != rows().end() && tmprit->par() != endpar) {
725 RowList::iterator tmprit2 = tmprit++;
729 // Reinsert the paragraphs.
730 Paragraph * tmppar = first_phys_par;
732 // See if this loop can be rewritten as a while loop instead.
733 // That should also make the code a bit easier to read. (Lgb)
736 insertParagraph(tmppar, tmprit);
737 while (tmprit != rows().end()
738 && tmprit->par() == tmppar) {
741 tmppar = tmppar->next();
743 } while (tmppar && tmppar != endpar);
746 // If the above changes are done, then we can compare prevrit
747 // with rows().end() here. (Lgb)
749 setHeightOfRow(prevrit);
750 const_cast<LyXText *>(this)->postPaint(y - prevrit->height());
752 setHeightOfRow(rows().begin());
753 const_cast<LyXText *>(this)->postPaint(0);
755 if (tmprit != rows().end())
756 setHeightOfRow(tmprit);
761 void LyXText::fullRebreak()
763 if (rows().empty()) {
767 if (need_break_row != rows().end()) {
768 breakAgain(need_break_row);
769 need_break_row = rows().end();
775 // important for the screen
778 // the cursor set functions have a special mechanism. When they
779 // realize, that you left an empty paragraph, they will delete it.
780 // They also delete the corresponding row
782 // need the selection cursor:
783 void LyXText::setSelection()
785 bool const lsel = selection.set();
787 if (!selection.set()) {
788 last_sel_cursor = selection.cursor;
789 selection.start = selection.cursor;
790 selection.end = selection.cursor;
795 // first the toggling area
796 if (cursor.y() < last_sel_cursor.y()
797 || (cursor.y() == last_sel_cursor.y()
798 && cursor.x() < last_sel_cursor.x())) {
799 toggle_end_cursor = last_sel_cursor;
800 toggle_cursor = cursor;
802 toggle_end_cursor = cursor;
803 toggle_cursor = last_sel_cursor;
806 last_sel_cursor = cursor;
808 // and now the whole selection
810 if (selection.cursor.par() == cursor.par())
811 if (selection.cursor.pos() < cursor.pos()) {
812 selection.end = cursor;
813 selection.start = selection.cursor;
815 selection.end = selection.cursor;
816 selection.start = cursor;
818 else if (selection.cursor.y() < cursor.y() ||
819 (selection.cursor.y() == cursor.y()
820 && selection.cursor.x() < cursor.x())) {
821 selection.end = cursor;
822 selection.start = selection.cursor;
825 selection.end = selection.cursor;
826 selection.start = cursor;
829 // a selection with no contents is not a selection
830 if (selection.start.par() == selection.end.par() &&
831 selection.start.pos() == selection.end.pos())
832 selection.set(false);
834 if (inset_owner && (selection.set() || lsel))
835 inset_owner->setUpdateStatus(bv(), InsetText::SELECTION);
839 string const LyXText::selectionAsString(Buffer const * buffer,
842 if (!selection.set()) return string();
844 // should be const ...
845 Paragraph * startpar(selection.start.par());
846 Paragraph * endpar(selection.end.par());
847 pos_type const startpos(selection.start.pos());
848 pos_type const endpos(selection.end.pos());
850 if (startpar == endpar) {
851 return startpar->asString(buffer, startpos, endpos, label);
856 // First paragraph in selection
857 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
859 // The paragraphs in between (if any)
860 LyXCursor tmpcur(selection.start);
861 tmpcur.par(tmpcur.par()->next());
862 while (tmpcur.par() != endpar) {
863 result += tmpcur.par()->asString(buffer, 0,
864 tmpcur.par()->size(),
866 tmpcur.par(tmpcur.par()->next());
869 // Last paragraph in selection
870 result += endpar->asString(buffer, 0, endpos, label);
876 void LyXText::clearSelection()
878 selection.set(false);
879 selection.mark(false);
880 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
881 // reset this in the bv_owner!
882 if (bv_owner && bv_owner->text)
883 bv_owner->text->xsel_cache.set(false);
887 void LyXText::cursorHome()
889 setCursor(cursor.par(), cursor.row()->pos());
893 void LyXText::cursorEnd()
895 if (cursor.par()->empty())
898 // There is a lot of unneeded recalculation going on here:
899 // - boost::next(curosr.row())
900 // - lastPost(*this, cursor.row())
902 if (boost::next(cursor.row()) == rows().end()
903 || boost::next(cursor.row())->par() != cursor.row()->par()) {
904 setCursor(cursor.par(), lastPos(*this, cursor.row()) + 1);
906 if (!cursor.par()->empty() &&
907 (cursor.par()->getChar(lastPos(*this, cursor.row())) == ' '
908 || cursor.par()->isNewline(lastPos(*this, cursor.row())))) {
909 setCursor(cursor.par(), lastPos(*this, cursor.row()));
911 setCursor(cursor.par(),
912 lastPos(*this, cursor.row()) + 1);
918 void LyXText::cursorTop()
920 while (cursor.par()->previous())
921 cursor.par(cursor.par()->previous());
922 setCursor(cursor.par(), 0);
926 void LyXText::cursorBottom()
928 while (cursor.par()->next())
929 cursor.par(cursor.par()->next());
930 setCursor(cursor.par(), cursor.par()->size());
934 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
936 // If the mask is completely neutral, tell user
937 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
938 // Could only happen with user style
939 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
943 // Try implicit word selection
944 // If there is a change in the language the implicit word selection
946 LyXCursor resetCursor = cursor;
947 bool implicitSelection = (font.language() == ignore_language
948 && font.number() == LyXFont::IGNORE)
949 ? selectWordWhenUnderCursor(WHOLE_WORD_STRICT) : false;
952 setFont(font, toggleall);
954 // Implicit selections are cleared afterwards
955 //and cursor is set to the original position.
956 if (implicitSelection) {
958 cursor = resetCursor;
959 setCursor(cursor.par(), cursor.pos());
960 selection.cursor = cursor;
963 inset_owner->setUpdateStatus(bv(), InsetText::CURSOR_PAR);
967 string LyXText::getStringToIndex()
969 // Try implicit word selection
970 // If there is a change in the language the implicit word selection
972 LyXCursor const reset_cursor = cursor;
973 bool const implicitSelection = selectWordWhenUnderCursor(PREVIOUS_WORD);
976 if (!selection.set())
977 bv()->owner()->message(_("Nothing to index!"));
978 else if (selection.start.par() != selection.end.par())
979 bv()->owner()->message(_("Cannot index more than one paragraph!"));
981 idxstring = selectionAsString(bv()->buffer(), false);
983 // Reset cursors to their original position.
984 cursor = reset_cursor;
985 setCursor(cursor.par(), cursor.pos());
986 selection.cursor = cursor;
988 // Clear the implicit selection.
989 if (implicitSelection)
996 // the DTP switches for paragraphs. LyX will store them in the first
997 // physicla paragraph. When a paragraph is broken, the top settings rest,
998 // the bottom settings are given to the new one. So I can make shure,
999 // they do not duplicate themself and you cannnot make dirty things with
1002 void LyXText::setParagraph(bool line_top, bool line_bottom,
1003 bool pagebreak_top, bool pagebreak_bottom,
1004 VSpace const & space_top,
1005 VSpace const & space_bottom,
1006 Spacing const & spacing,
1008 string const & labelwidthstring,
1011 LyXCursor tmpcursor = cursor;
1012 if (!selection.set()) {
1013 selection.start = cursor;
1014 selection.end = cursor;
1017 // make sure that the depth behind the selection are restored, too
1018 Paragraph * endpar = selection.end.par()->next();
1019 Paragraph * undoendpar = endpar;
1021 if (endpar && endpar->getDepth()) {
1022 while (endpar && endpar->getDepth()) {
1023 endpar = endpar->next();
1024 undoendpar = endpar;
1028 // because of parindents etc.
1029 endpar = endpar->next();
1032 setUndo(bv(), Undo::EDIT, selection.start.par(), undoendpar);
1035 Paragraph * tmppar = selection.end.par();
1037 while (tmppar != selection.start.par()->previous()) {
1038 setCursor(tmppar, 0);
1039 postPaint(cursor.y() - cursor.row()->baseline());
1040 cursor.par()->params().lineTop(line_top);
1041 cursor.par()->params().lineBottom(line_bottom);
1042 cursor.par()->params().pagebreakTop(pagebreak_top);
1043 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1044 cursor.par()->params().spaceTop(space_top);
1045 cursor.par()->params().spaceBottom(space_bottom);
1046 cursor.par()->params().spacing(spacing);
1047 // does the layout allow the new alignment?
1048 LyXLayout_ptr const & layout = cursor.par()->layout();
1050 if (align == LYX_ALIGN_LAYOUT)
1051 align = layout->align;
1052 if (align & layout->alignpossible) {
1053 if (align == layout->align)
1054 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1056 cursor.par()->params().align(align);
1058 cursor.par()->setLabelWidthString(labelwidthstring);
1059 cursor.par()->params().noindent(noindent);
1060 tmppar = cursor.par()->previous();
1063 redoParagraphs(selection.start, endpar);
1066 setCursor(selection.start.par(), selection.start.pos());
1067 selection.cursor = cursor;
1068 setCursor(selection.end.par(), selection.end.pos());
1070 setCursor(tmpcursor.par(), tmpcursor.pos());
1072 bv()->updateInset(inset_owner);
1076 // set the counter of a paragraph. This includes the labels
1077 void LyXText::setCounter(Buffer const * buf, Paragraph * par)
1079 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1080 LyXLayout_ptr const & layout = par->layout();
1082 if (par->previous()) {
1084 par->params().appendix(par->previous()->params().appendix());
1085 if (!par->params().appendix() && par->params().startOfAppendix()) {
1086 par->params().appendix(true);
1087 textclass.counters().reset();
1089 par->enumdepth = par->previous()->enumdepth;
1090 par->itemdepth = par->previous()->itemdepth;
1092 par->params().appendix(par->params().startOfAppendix());
1097 /* Maybe we have to increment the enumeration depth.
1098 * BUT, enumeration in a footnote is considered in isolation from its
1099 * surrounding paragraph so don't increment if this is the
1100 * first line of the footnote
1101 * AND, bibliographies can't have their depth changed ie. they
1102 * are always of depth 0
1105 && par->previous()->getDepth() < par->getDepth()
1106 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1107 && par->enumdepth < 3
1108 && layout->labeltype != LABEL_BIBLIO) {
1112 // Maybe we have to decrement the enumeration depth, see note above
1114 && par->previous()->getDepth() > par->getDepth()
1115 && layout->labeltype != LABEL_BIBLIO) {
1116 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1119 if (!par->params().labelString().empty()) {
1120 par->params().labelString(string());
1123 if (layout->margintype == MARGIN_MANUAL) {
1124 if (par->params().labelWidthString().empty()) {
1125 par->setLabelWidthString(layout->labelstring());
1128 par->setLabelWidthString(string());
1131 // is it a layout that has an automatic label?
1132 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1133 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1137 if (i >= 0 && i <= buf->params.secnumdepth) {
1141 textclass.counters().step(layout->latexname());
1143 // Is there a label? Useful for Chapter layout
1144 if (!par->params().appendix()) {
1145 s << layout->labelstring();
1147 s << layout->labelstring_appendix();
1150 // Use of an integer is here less than elegant. For now.
1151 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1152 if (!par->params().appendix()) {
1153 numbertype = "sectioning";
1155 numbertype = "appendix";
1156 if (par->isRightToLeftPar(buf->params))
1157 langtype = "hebrew";
1162 s << textclass.counters()
1163 .numberLabel(layout->latexname(),
1164 numbertype, langtype, head);
1166 par->params().labelString(STRCONV(s.str()));
1168 // reset enum counters
1169 textclass.counters().reset("enum");
1170 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1171 textclass.counters().reset("enum");
1172 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1174 // Yes I know this is a really, really! bad solution
1176 string enumcounter("enum");
1178 switch (par->enumdepth) {
1187 enumcounter += "iv";
1190 // not a valid enumdepth...
1194 textclass.counters().step(enumcounter);
1196 s << textclass.counters()
1197 .numberLabel(enumcounter, "enumeration");
1198 par->params().labelString(STRCONV(s.str()));
1200 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1201 textclass.counters().step("bibitem");
1202 int number = textclass.counters().value("bibitem");
1203 if (par->bibitem()) {
1204 par->bibitem()->setCounter(number);
1205 par->params().labelString(layout->labelstring());
1207 // In biblio should't be following counters but...
1209 string s = layout->labelstring();
1211 // the caption hack:
1212 if (layout->labeltype == LABEL_SENSITIVE) {
1213 Paragraph * tmppar = par;
1216 while (tmppar && tmppar->inInset()
1217 // the single '=' is intended below
1218 && (in = tmppar->inInset()->owner())) {
1219 if (in->lyxCode() == Inset::FLOAT_CODE ||
1220 in->lyxCode() == Inset::WRAP_CODE) {
1224 tmppar = in->parOwner();
1230 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1232 textclass.counters().step(fl.type());
1234 // Doesn't work... yet.
1235 #if USE_BOOST_FORMAT
1236 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1237 // s << boost::format(_("%1$s %1$d:")
1239 // % buf->counters().value(fl.name());
1242 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1243 o << fl.name() << " #:";
1244 s = STRCONV(o.str());
1247 // par->SetLayout(0);
1248 // s = layout->labelstring;
1249 s = _("Senseless: ");
1252 par->params().labelString(s);
1254 // reset the enumeration counter. They are always reset
1255 // when there is any other layout between
1256 // Just fall-through between the cases so that all
1257 // enum counters deeper than enumdepth is also reset.
1258 switch (par->enumdepth) {
1260 textclass.counters().reset("enumi");
1262 textclass.counters().reset("enumii");
1264 textclass.counters().reset("enumiii");
1266 textclass.counters().reset("enumiv");
1272 // Updates all counters. Paragraphs with changed label string will be rebroken
1273 void LyXText::updateCounters()
1275 RowList::iterator rowit = rows().begin();
1276 Paragraph * par = rowit->par();
1278 // CHECK if this is really needed. (Lgb)
1279 bv()->buffer()->params.getLyXTextClass().counters().reset();
1282 while (rowit->par() != par)
1285 string const oldLabel = par->params().labelString();
1287 // setCounter can potentially change the labelString.
1288 setCounter(bv()->buffer(), par);
1290 string const & newLabel = par->params().labelString();
1292 if (oldLabel.empty() && !newLabel.empty()) {
1293 removeParagraph(rowit);
1294 appendParagraph(rowit);
1302 void LyXText::insertInset(Inset * inset)
1304 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1306 setUndo(bv(), Undo::FINISH, cursor.par(), cursor.par()->next());
1308 cursor.par()->insertInset(cursor.pos(), inset);
1309 // Just to rebreak and refresh correctly.
1310 // The character will not be inserted a second time
1311 insertChar(Paragraph::META_INSET);
1312 // If we enter a highly editable inset the cursor should be to before
1313 // the inset. This couldn't happen before as Undo was not handled inside
1314 // inset now after the Undo LyX tries to call inset->Edit(...) again
1315 // and cannot do this as the cursor is behind the inset and GetInset
1316 // does not return the inset!
1317 if (isHighlyEditableInset(inset)) {
1324 void LyXText::copyEnvironmentType()
1326 copylayouttype = cursor.par()->layout()->name();
1330 void LyXText::pasteEnvironmentType()
1332 // do nothing if there has been no previous copyEnvironmentType()
1333 if (!copylayouttype.empty())
1334 setLayout(copylayouttype);
1338 void LyXText::cutSelection(bool doclear, bool realcut)
1340 // Stuff what we got on the clipboard. Even if there is no selection.
1342 // There is a problem with having the stuffing here in that the
1343 // larger the selection the slower LyX will get. This can be
1344 // solved by running the line below only when the selection has
1345 // finished. The solution used currently just works, to make it
1346 // faster we need to be more clever and probably also have more
1347 // calls to stuffClipboard. (Lgb)
1348 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1350 // This doesn't make sense, if there is no selection
1351 if (!selection.set())
1354 // OK, we have a selection. This is always between selection.start
1355 // and selection.end
1357 // make sure that the depth behind the selection are restored, too
1358 Paragraph * endpar = selection.end.par()->next();
1359 Paragraph * undoendpar = endpar;
1361 if (endpar && endpar->getDepth()) {
1362 while (endpar && endpar->getDepth()) {
1363 endpar = endpar->next();
1364 undoendpar = endpar;
1366 } else if (endpar) {
1367 endpar = endpar->next(); // because of parindents etc.
1370 setUndo(bv(), Undo::DELETE,
1371 selection.start.par(), undoendpar);
1373 // there are two cases: cut only within one paragraph or
1374 // more than one paragraph
1375 if (selection.start.par() == selection.end.par()) {
1376 // only within one paragraph
1377 endpar = selection.end.par();
1378 int pos = selection.end.pos();
1379 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1380 selection.start.pos(), pos,
1381 bv()->buffer()->params.textclass,
1383 selection.end.pos(pos);
1385 endpar = selection.end.par();
1386 int pos = selection.end.pos();
1387 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1388 selection.start.pos(), pos,
1389 bv()->buffer()->params.textclass,
1392 selection.end.par(endpar);
1393 selection.end.pos(pos);
1394 cursor.pos(selection.end.pos());
1396 endpar = endpar->next();
1398 // sometimes necessary
1400 selection.start.par()->stripLeadingSpaces();
1402 redoParagraphs(selection.start, endpar);
1404 // cutSelection can invalidate the cursor so we need to set
1406 // we prefer the end for when tracking changes
1407 cursor = selection.end;
1409 // need a valid cursor. (Lgb)
1412 setCursor(cursor.par(), cursor.pos());
1413 selection.cursor = cursor;
1418 void LyXText::copySelection()
1420 // stuff the selection onto the X clipboard, from an explicit copy request
1421 bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1423 // this doesnt make sense, if there is no selection
1424 if (!selection.set())
1427 // ok we have a selection. This is always between selection.start
1428 // and sel_end cursor
1430 // copy behind a space if there is one
1431 while (selection.start.par()->size() > selection.start.pos()
1432 && selection.start.par()->isLineSeparator(selection.start.pos())
1433 && (selection.start.par() != selection.end.par()
1434 || selection.start.pos() < selection.end.pos()))
1435 selection.start.pos(selection.start.pos() + 1);
1437 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1438 selection.start.pos(), selection.end.pos(),
1439 bv()->buffer()->params.textclass);
1443 void LyXText::pasteSelection()
1445 // this does not make sense, if there is nothing to paste
1446 if (!CutAndPaste::checkPastePossible())
1449 setUndo(bv(), Undo::INSERT,
1450 cursor.par(), cursor.par()->next());
1453 Paragraph * actpar = cursor.par();
1454 int pos = cursor.pos();
1456 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1457 bv()->buffer()->params.textclass);
1459 redoParagraphs(cursor, endpar);
1461 setCursor(cursor.par(), cursor.pos());
1464 selection.cursor = cursor;
1465 setCursor(actpar, pos);
1471 void LyXText::setSelectionRange(lyx::pos_type length)
1476 selection.cursor = cursor;
1483 // simple replacing. The font of the first selected character is used
1484 void LyXText::replaceSelectionWithString(string const & str)
1486 setCursorParUndo(bv());
1489 if (!selection.set()) { // create a dummy selection
1490 selection.end = cursor;
1491 selection.start = cursor;
1494 // Get font setting before we cut
1495 pos_type pos = selection.end.pos();
1496 LyXFont const font = selection.start.par()
1497 ->getFontSettings(bv()->buffer()->params,
1498 selection.start.pos());
1500 // Insert the new string
1501 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1502 selection.end.par()->insertChar(pos, (*cit), font);
1506 // Cut the selection
1507 cutSelection(true, false);
1513 // needed to insert the selection
1514 void LyXText::insertStringAsLines(string const & str)
1516 Paragraph * par = cursor.par();
1517 pos_type pos = cursor.pos();
1518 Paragraph * endpar = cursor.par()->next();
1520 setCursorParUndo(bv());
1522 // only to be sure, should not be neccessary
1525 bv()->buffer()->insertStringAsLines(par, pos, current_font, str);
1527 redoParagraphs(cursor, endpar);
1528 setCursor(cursor.par(), cursor.pos());
1529 selection.cursor = cursor;
1530 setCursor(par, pos);
1535 // turns double-CR to single CR, others where converted into one
1536 // blank. Then InsertStringAsLines is called
1537 void LyXText::insertStringAsParagraphs(string const & str)
1539 string linestr(str);
1540 bool newline_inserted = false;
1541 for (string::size_type i = 0; i < linestr.length(); ++i) {
1542 if (linestr[i] == '\n') {
1543 if (newline_inserted) {
1544 // we know that \r will be ignored by
1545 // InsertStringA. Of course, it is a dirty
1546 // trick, but it works...
1547 linestr[i - 1] = '\r';
1551 newline_inserted = true;
1553 } else if (IsPrintable(linestr[i])) {
1554 newline_inserted = false;
1557 insertStringAsLines(linestr);
1561 void LyXText::checkParagraph(Paragraph * par, pos_type pos)
1563 LyXCursor tmpcursor;
1567 RowList::iterator row = getRow(par, pos, y);
1568 RowList::iterator beg = rows().begin();
1570 // is there a break one row above
1572 && boost::prior(row)->par() == row->par()) {
1573 z = rowBreakPoint(*boost::prior(row));
1574 if (z >= row->pos()) {
1575 // set the dimensions of the row above
1576 y -= boost::prior(row)->height();
1579 breakAgain(boost::prior(row));
1581 // set the cursor again. Otherwise
1582 // dangling pointers are possible
1583 setCursor(cursor.par(), cursor.pos(),
1584 false, cursor.boundary());
1585 selection.cursor = cursor;
1590 int const tmpheight = row->height();
1591 pos_type const tmplast = lastPos(*this, row);
1594 if (row->height() == tmpheight && lastPos(*this, row) == tmplast) {
1595 postRowPaint(row, y);
1600 // check the special right address boxes
1601 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1608 redoDrawingOfParagraph(tmpcursor);
1611 // set the cursor again. Otherwise dangling pointers are possible
1612 // also set the selection
1614 if (selection.set()) {
1616 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1617 false, selection.cursor.boundary());
1618 selection.cursor = cursor;
1619 setCursorIntern(selection.start.par(),
1620 selection.start.pos(),
1621 false, selection.start.boundary());
1622 selection.start = cursor;
1623 setCursorIntern(selection.end.par(),
1624 selection.end.pos(),
1625 false, selection.end.boundary());
1626 selection.end = cursor;
1627 setCursorIntern(last_sel_cursor.par(),
1628 last_sel_cursor.pos(),
1629 false, last_sel_cursor.boundary());
1630 last_sel_cursor = cursor;
1633 setCursorIntern(cursor.par(), cursor.pos(),
1634 false, cursor.boundary());
1638 // returns false if inset wasn't found
1639 bool LyXText::updateInset(Inset * inset)
1641 // first check the current paragraph
1642 int pos = cursor.par()->getPositionOfInset(inset);
1644 checkParagraph(cursor.par(), pos);
1648 // check every paragraph
1650 ParagraphList::iterator par = ownerParagraphs().begin();
1651 ParagraphList::iterator end = ownerParagraphs().end();
1654 pos = par->getPositionOfInset(inset);
1656 checkParagraph(&*par, pos);
1660 } while (par != end);
1666 bool LyXText::setCursor(Paragraph * par,
1668 bool setfont, bool boundary)
1670 LyXCursor old_cursor = cursor;
1671 setCursorIntern(par, pos, setfont, boundary);
1672 return deleteEmptyParagraphMechanism(old_cursor);
1676 void LyXText::setCursor(LyXCursor & cur, Paragraph * par,
1677 pos_type pos, bool boundary)
1683 cur.boundary(boundary);
1685 // get the cursor y position in text
1687 RowList::iterator row = getRow(par, pos, y);
1688 RowList::iterator beg = rows().begin();
1690 RowList::iterator old_row = row;
1692 // if we are before the first char of this row and are still in the
1693 // same paragraph and there is a previous row then put the cursor on
1694 // the end of the previous row
1695 cur.iy(y + row->baseline());
1697 if (row != beg && pos &&
1698 boost::prior(row)->par() == row->par() &&
1699 pos < par->size() &&
1700 par->getChar(pos) == Paragraph::META_INSET &&
1701 (ins = par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1708 // y is now the beginning of the cursor row
1709 y += row->baseline();
1710 // y is now the cursor baseline
1713 pos_type last = lastPrintablePos(*this, old_row);
1715 // None of these should happen, but we're scaredy-cats
1716 if (pos > par->size()) {
1717 lyxerr << "dont like 1 please report" << endl;
1720 } else if (pos > last + 1) {
1721 lyxerr << "dont like 2 please report" << endl;
1722 // This shouldn't happen.
1725 } else if (pos < row->pos()) {
1726 lyxerr << "dont like 3 please report" << endl;
1731 // now get the cursors x position
1732 float x = getCursorX(row, pos, last, boundary);
1735 if (old_row != row) {
1736 x = getCursorX(old_row, pos, last, boundary);
1740 //if the cursor is in a visible row, anchor to it
1742 if (topy < y && y < topy + bv()->workHeight())
1747 float LyXText::getCursorX(RowList::iterator rit,
1748 pos_type pos, pos_type last, bool boundary) const
1750 pos_type cursor_vpos = 0;
1752 float fill_separator;
1754 float fill_label_hfill;
1755 // This call HAS to be here because of the BidiTables!!!
1756 prepareToPrint(rit, x, fill_separator, fill_hfill,
1759 if (last < rit->pos())
1760 cursor_vpos = rit->pos();
1761 else if (pos > last && !boundary)
1762 cursor_vpos = (rit->par()->isRightToLeftPar(bv()->buffer()->params))
1763 ? rit->pos() : last + 1;
1764 else if (pos > rit->pos() &&
1765 (pos > last || boundary))
1766 /// Place cursor after char at (logical) position pos - 1
1767 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1768 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1770 /// Place cursor before char at (logical) position pos
1771 cursor_vpos = (bidi_level(pos) % 2 == 0)
1772 ? log2vis(pos) : log2vis(pos) + 1;
1774 pos_type body_pos = rit->par()->beginningOfBody();
1775 if ((body_pos > 0) &&
1776 ((body_pos - 1 > last) ||
1777 !rit->par()->isLineSeparator(body_pos - 1)))
1780 for (pos_type vpos = rit->pos(); vpos < cursor_vpos; ++vpos) {
1781 pos_type pos = vis2log(vpos);
1782 if (body_pos > 0 && pos == body_pos - 1) {
1783 x += fill_label_hfill +
1784 font_metrics::width(
1785 rit->par()->layout()->labelsep,
1786 getLabelFont(bv()->buffer(),
1788 if (rit->par()->isLineSeparator(body_pos - 1))
1789 x -= singleWidth(rit->par(), body_pos - 1);
1792 if (hfillExpansion(*this, rit, pos)) {
1793 x += singleWidth(rit->par(), pos);
1794 if (pos >= body_pos)
1797 x += fill_label_hfill;
1798 } else if (rit->par()->isSeparator(pos)) {
1799 x += singleWidth(rit->par(), pos);
1800 if (pos >= body_pos)
1801 x += fill_separator;
1803 x += singleWidth(rit->par(), pos);
1809 void LyXText::setCursorIntern(Paragraph * par,
1810 pos_type pos, bool setfont, bool boundary)
1812 InsetText * it = static_cast<InsetText *>(par->inInset());
1814 if (it != inset_owner) {
1815 lyxerr[Debug::INSETS] << "InsetText is " << it
1817 << "inset_owner is "
1818 << inset_owner << endl;
1819 #ifdef WITH_WARNINGS
1820 #warning I believe this code is wrong. (Lgb)
1821 #warning Jürgen, have a look at this. (Lgb)
1822 #warning Hmmm, I guess you are right but we
1823 #warning should verify when this is needed
1825 // Jürgen, would you like to have a look?
1826 // I guess we need to move the outer cursor
1827 // and open and lock the inset (bla bla bla)
1828 // stuff I don't know... so can you have a look?
1830 // I moved the lyxerr stuff in here so we can see if
1831 // this is actually really needed and where!
1833 // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1838 setCursor(cursor, par, pos, boundary);
1844 void LyXText::setCurrentFont()
1846 pos_type pos = cursor.pos();
1847 if (cursor.boundary() && pos > 0)
1851 if (pos == cursor.par()->size())
1853 else // potentional bug... BUG (Lgb)
1854 if (cursor.par()->isSeparator(pos)) {
1855 if (pos > cursor.row()->pos() &&
1856 bidi_level(pos) % 2 ==
1857 bidi_level(pos - 1) % 2)
1859 else if (pos + 1 < cursor.par()->size())
1865 cursor.par()->getFontSettings(bv()->buffer()->params, pos);
1866 real_current_font = getFont(bv()->buffer(), cursor.par(), pos);
1868 if (cursor.pos() == cursor.par()->size() &&
1869 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()) &&
1870 !cursor.boundary()) {
1871 Language const * lang =
1872 cursor.par()->getParLanguage(bv()->buffer()->params);
1873 current_font.setLanguage(lang);
1874 current_font.setNumber(LyXFont::OFF);
1875 real_current_font.setLanguage(lang);
1876 real_current_font.setNumber(LyXFont::OFF);
1881 // returns the column near the specified x-coordinate of the row
1882 // x is set to the real beginning of this column
1884 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1887 float fill_separator;
1889 float fill_label_hfill;
1891 prepareToPrint(rit, tmpx, fill_separator,
1892 fill_hfill, fill_label_hfill);
1894 pos_type vc = rit->pos();
1895 pos_type last = lastPrintablePos(*this, rit);
1898 LyXLayout_ptr const & layout = rit->par()->layout();
1900 bool left_side = false;
1902 pos_type body_pos = rit->par()->beginningOfBody();
1903 float last_tmpx = tmpx;
1906 (body_pos - 1 > last ||
1907 !rit->par()->isLineSeparator(body_pos - 1)))
1910 // check for empty row
1911 if (!rit->par()->size()) {
1916 while (vc <= last && tmpx <= x) {
1919 if (body_pos > 0 && c == body_pos - 1) {
1920 tmpx += fill_label_hfill +
1921 font_metrics::width(layout->labelsep,
1922 getLabelFont(bv()->buffer(), rit->par()));
1923 if (rit->par()->isLineSeparator(body_pos - 1))
1924 tmpx -= singleWidth(rit->par(), body_pos - 1);
1927 if (hfillExpansion(*this, rit, c)) {
1928 tmpx += singleWidth(rit->par(), c);
1932 tmpx += fill_label_hfill;
1933 } else if (rit->par()->isSeparator(c)) {
1934 tmpx += singleWidth(rit->par(), c);
1936 tmpx+= fill_separator;
1938 tmpx += singleWidth(rit->par(), c);
1943 if ((tmpx + last_tmpx) / 2 > x) {
1948 if (vc > last + 1) // This shouldn't happen.
1952 // This (rtl_support test) is not needed, but gives
1953 // some speedup if rtl_support=false
1954 bool const lastrow = lyxrc.rtl_support &&
1955 (boost::next(rit) == rowlist_.end() ||
1956 boost::next(rit)->par() != rit->par());
1957 // If lastrow is false, we don't need to compute
1958 // the value of rtl.
1959 bool const rtl = (lastrow)
1960 ? rit->par()->isRightToLeftPar(bv()->buffer()->params)
1963 ((rtl && left_side && vc == rit->pos() && x < tmpx - 5) ||
1964 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
1966 else if (vc == rit->pos()) {
1968 if (bidi_level(c) % 2 == 1)
1971 c = vis2log(vc - 1);
1972 bool const rtl = (bidi_level(c) % 2 == 1);
1973 if (left_side == rtl) {
1975 boundary = isBoundary(bv()->buffer(), rit->par(), c);
1979 if (rit->pos() <= last && c > last
1980 && rit->par()->isNewline(last)) {
1981 if (bidi_level(last) % 2 == 0)
1982 tmpx -= singleWidth(rit->par(), last);
1984 tmpx += singleWidth(rit->par(), last);
1994 void LyXText::setCursorFromCoordinates(int x, int y)
1996 LyXCursor old_cursor = cursor;
1998 setCursorFromCoordinates(cursor, x, y);
2000 deleteEmptyParagraphMechanism(old_cursor);
2007 * return true if the cursor given is at the end of a row,
2008 * and the next row is filled by an inset that spans an entire
2011 bool beforeFullRowInset(LyXText & lt, RowList::iterator row,
2013 if (boost::next(row) == lt.rows().end())
2015 Row const & next = *boost::next(row);
2017 if (next.pos() != cur.pos() || next.par() != cur.par())
2019 if (!cur.par()->isInset(cur.pos()))
2021 Inset const * inset = cur.par()->getInset(cur.pos());
2022 if (inset->needFullRow() || inset->display())
2029 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
2031 // Get the row first.
2033 RowList::iterator row = getRowNearY(y);
2035 pos_type const column = getColumnNearX(row, x, bound);
2036 cur.par(row->par());
2037 cur.pos(row->pos() + column);
2039 cur.y(y + row->baseline());
2042 if (beforeFullRowInset(*this, row, cur)) {
2043 pos_type last = lastPrintablePos(*this, row);
2044 float x = getCursorX(boost::next(row), cur.pos(), last, bound);
2046 cur.iy(y + row->height() + boost::next(row)->baseline());
2047 cur.irow(boost::next(row));
2053 cur.boundary(bound);
2057 void LyXText::cursorLeft(bool internal)
2059 if (cursor.pos() > 0) {
2060 bool boundary = cursor.boundary();
2061 setCursor(cursor.par(), cursor.pos() - 1, true, false);
2062 if (!internal && !boundary &&
2063 isBoundary(bv()->buffer(), cursor.par(), cursor.pos() + 1))
2064 setCursor(cursor.par(), cursor.pos() + 1, true, true);
2065 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2066 Paragraph * par = cursor.par()->previous();
2067 setCursor(par, par->size());
2072 void LyXText::cursorRight(bool internal)
2074 if (!internal && cursor.boundary() &&
2075 !cursor.par()->isNewline(cursor.pos()))
2076 setCursor(cursor.par(), cursor.pos(), true, false);
2077 else if (cursor.pos() < cursor.par()->size()) {
2078 setCursor(cursor.par(), cursor.pos() + 1, true, false);
2080 isBoundary(bv()->buffer(), cursor.par(), cursor.pos()))
2081 setCursor(cursor.par(), cursor.pos(), true, true);
2082 } else if (cursor.par()->next())
2083 setCursor(cursor.par()->next(), 0);
2087 void LyXText::cursorUp(bool selecting)
2090 int x = cursor.x_fix();
2091 int y = cursor.y() - cursor.row()->baseline() - 1;
2092 setCursorFromCoordinates(x, y);
2095 int y1 = cursor.iy() - topy;
2098 Inset * inset_hit = checkInsetHit(x, y1);
2099 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2100 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2104 setCursorFromCoordinates(bv(), cursor.x_fix(),
2105 cursor.y() - cursor.row()->baseline() - 1);
2110 void LyXText::cursorDown(bool selecting)
2113 int x = cursor.x_fix();
2114 int y = cursor.y() - cursor.row()->baseline() +
2115 cursor.row()->height() + 1;
2116 setCursorFromCoordinates(x, y);
2117 if (!selecting && cursor.row() == cursor.irow()) {
2119 int y1 = cursor.iy() - topy;
2122 Inset * inset_hit = checkInsetHit(x, y1);
2123 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2124 inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2128 setCursorFromCoordinates(bv(), cursor.x_fix(),
2129 cursor.y() - cursor.row()->baseline()
2130 + cursor.row()->height() + 1);
2135 void LyXText::cursorUpParagraph()
2137 if (cursor.pos() > 0) {
2138 setCursor(cursor.par(), 0);
2140 else if (cursor.par()->previous()) {
2141 setCursor(cursor.par()->previous(), 0);
2146 void LyXText::cursorDownParagraph()
2148 if (cursor.par()->next()) {
2149 setCursor(cursor.par()->next(), 0);
2151 setCursor(cursor.par(), cursor.par()->size());
2155 // fix the cursor `cur' after a characters has been deleted at `where'
2156 // position. Called by deleteEmptyParagraphMechanism
2157 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2158 LyXCursor const & where)
2160 // if cursor is not in the paragraph where the delete occured,
2162 if (cur.par() != where.par())
2165 // if cursor position is after the place where the delete occured,
2167 if (cur.pos() > where.pos())
2168 cur.pos(cur.pos()-1);
2170 // check also if we don't want to set the cursor on a spot behind the
2171 // pagragraph because we erased the last character.
2172 if (cur.pos() > cur.par()->size())
2173 cur.pos(cur.par()->size());
2175 // recompute row et al. for this cursor
2176 setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2180 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2182 // Would be wrong to delete anything if we have a selection.
2183 if (selection.set())
2186 // We allow all kinds of "mumbo-jumbo" when freespacing.
2187 if (old_cursor.par()->layout()->free_spacing
2188 || old_cursor.par()->isFreeSpacing()) {
2192 /* Ok I'll put some comments here about what is missing.
2193 I have fixed BackSpace (and thus Delete) to not delete
2194 double-spaces automagically. I have also changed Cut,
2195 Copy and Paste to hopefully do some sensible things.
2196 There are still some small problems that can lead to
2197 double spaces stored in the document file or space at
2198 the beginning of paragraphs. This happens if you have
2199 the cursor betwenn to spaces and then save. Or if you
2200 cut and paste and the selection have a space at the
2201 beginning and then save right after the paste. I am
2202 sure none of these are very hard to fix, but I will
2203 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2204 that I can get some feedback. (Lgb)
2207 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2208 // delete the LineSeparator.
2211 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2212 // delete the LineSeparator.
2215 // If the pos around the old_cursor were spaces, delete one of them.
2216 if (old_cursor.par() != cursor.par()
2217 || old_cursor.pos() != cursor.pos()) {
2218 // Only if the cursor has really moved
2220 if (old_cursor.pos() > 0
2221 && old_cursor.pos() < old_cursor.par()->size()
2222 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2223 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2224 old_cursor.par()->erase(old_cursor.pos() - 1);
2225 redoParagraphs(old_cursor, old_cursor.par()->next());
2227 #ifdef WITH_WARNINGS
2228 #warning This will not work anymore when we have multiple views of the same buffer
2229 // In this case, we will have to correct also the cursors held by
2230 // other bufferviews. It will probably be easier to do that in a more
2231 // automated way in LyXCursor code. (JMarc 26/09/2001)
2233 // correct all cursors held by the LyXText
2234 fixCursorAfterDelete(cursor, old_cursor);
2235 fixCursorAfterDelete(selection.cursor,
2237 fixCursorAfterDelete(selection.start,
2239 fixCursorAfterDelete(selection.end, old_cursor);
2240 fixCursorAfterDelete(last_sel_cursor,
2242 fixCursorAfterDelete(toggle_cursor, old_cursor);
2243 fixCursorAfterDelete(toggle_end_cursor,
2249 // don't delete anything if this is the ONLY paragraph!
2250 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2253 // Do not delete empty paragraphs with keepempty set.
2254 if (old_cursor.par()->layout()->keepempty)
2257 // only do our magic if we changed paragraph
2258 if (old_cursor.par() == cursor.par())
2261 // record if we have deleted a paragraph
2262 // we can't possibly have deleted a paragraph before this point
2263 bool deleted = false;
2265 if ((old_cursor.par()->empty()
2266 || (old_cursor.par()->size() == 1
2267 && old_cursor.par()->isLineSeparator(0)))) {
2268 // ok, we will delete anything
2269 LyXCursor tmpcursor;
2273 if (old_cursor.row() != rows().begin()) {
2274 const_cast<LyXText *>(this)->postPaint(old_cursor.y() - old_cursor.row()->baseline()
2275 - boost::prior(old_cursor.row())->height());
2277 cursor = old_cursor; // that undo can restore the right cursor position
2278 Paragraph * endpar = old_cursor.par()->next();
2279 if (endpar && endpar->getDepth()) {
2280 while (endpar && endpar->getDepth()) {
2281 endpar = endpar->next();
2284 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2288 removeRow(old_cursor.row());
2289 if (ownerParagraphs().begin() == old_cursor.par()) {
2290 ownerParagraph(&*boost::next(ownerParagraphs().begin()));
2293 delete old_cursor.par();
2295 /* Breakagain the next par. Needed because of
2296 * the parindent that can occur or dissappear.
2297 * The next row can change its height, if
2298 * there is another layout before */
2299 if (refresh_row != rows().end()) {
2300 if (boost::next(refresh_row) != rows().end()) {
2301 breakAgain(boost::next(refresh_row));
2304 setHeightOfRow(refresh_row);
2307 RowList::iterator nextrow = boost::next(old_cursor.row());
2308 const_cast<LyXText *>(this)->postPaint(
2309 old_cursor.y() - old_cursor.row()->baseline());
2312 cursor = old_cursor; // that undo can restore the right cursor position
2313 Paragraph * endpar = old_cursor.par()->next();
2314 if (endpar && endpar->getDepth()) {
2315 while (endpar && endpar->getDepth()) {
2316 endpar = endpar->next();
2319 setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2323 removeRow(old_cursor.row());
2325 if (ownerParagraphs().begin() == old_cursor.par()) {
2326 ownerParagraph(&*boost::next(ownerParagraphs().begin()));
2329 delete old_cursor.par();
2331 /* Breakagain the next par. Needed because of
2332 the parindent that can occur or dissappear.
2333 The next row can change its height, if
2334 there is another layout before */
2335 if (nextrow != rows().end()) {
2336 breakAgain(nextrow);
2342 setCursorIntern(cursor.par(), cursor.pos());
2344 if (selection.cursor.par() == old_cursor.par()
2345 && selection.cursor.pos() == old_cursor.pos()) {
2346 // correct selection
2347 selection.cursor = cursor;
2351 if (old_cursor.par()->stripLeadingSpaces()) {
2352 redoParagraphs(old_cursor,
2353 old_cursor.par()->next());
2355 setCursorIntern(cursor.par(), cursor.pos());
2356 selection.cursor = cursor;
2363 ParagraphList & LyXText::ownerParagraphs() const
2366 return inset_owner->paragraphs;
2368 return bv_owner->buffer()->paragraphs;
2372 void LyXText::ownerParagraph(Paragraph * p) const
2375 inset_owner->paragraph(p);
2377 bv_owner->buffer()->paragraphs.set(p);
2382 void LyXText::ownerParagraph(int id, Paragraph * p) const
2384 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2385 if (op && op->inInset()) {
2386 static_cast<InsetText *>(op->inInset())->paragraph(p);
2393 LyXText::refresh_status LyXText::refreshStatus() const
2395 return refresh_status_;
2399 void LyXText::clearPaint()
2401 refresh_status_ = REFRESH_NONE;
2402 refresh_row = rows().end();
2407 void LyXText::postPaint(int start_y)
2409 refresh_status old = refresh_status_;
2411 refresh_status_ = REFRESH_AREA;
2412 refresh_row = rows().end();
2414 if (old != REFRESH_NONE && refresh_y < start_y)
2417 refresh_y = start_y;
2422 // We are an inset's lyxtext. Tell the top-level lyxtext
2423 // it needs to update the row we're in.
2424 LyXText * t = bv()->text;
2425 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2429 // FIXME: we should probably remove this y parameter,
2430 // make refresh_y be 0, and use row->y etc.
2431 void LyXText::postRowPaint(RowList::iterator rit, int start_y)
2433 if (refresh_status_ != REFRESH_NONE && refresh_y < start_y) {
2434 refresh_status_ = REFRESH_AREA;
2437 refresh_y = start_y;
2440 if (refresh_status_ == REFRESH_AREA)
2443 refresh_status_ = REFRESH_ROW;
2449 // We are an inset's lyxtext. Tell the top-level lyxtext
2450 // it needs to update the row we're in.
2451 LyXText * t = bv()->text;
2452 t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2456 bool LyXText::isInInset() const
2458 // Sub-level has non-null bv owner and
2459 // non-null inset owner.
2460 return inset_owner != 0 && bv_owner != 0;
2464 int defaultRowHeight()
2466 LyXFont const font(LyXFont::ALL_SANE);
2467 return int(font_metrics::maxAscent(font)
2468 + font_metrics::maxDescent(font) * 1.5);