1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
15 #include "paragraph.h"
16 #include "frontends/LyXView.h"
17 #include "undo_funcs.h"
19 #include "bufferparams.h"
21 #include "BufferView.h"
22 #include "CutAndPaste.h"
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
28 #include "FloatList.h"
30 #include "ParagraphParameters.h"
33 #include "insets/inseterror.h"
34 #include "insets/insetbib.h"
35 #include "insets/insetspecialchar.h"
36 #include "insets/insettext.h"
37 #include "insets/insetfloat.h"
38 #include "insets/insetwrap.h"
40 #include "support/LAssert.h"
41 #include "support/textutils.h"
42 #include "support/lstrings.h"
44 #include "BoostFormat.h"
54 LyXText::LyXText(BufferView * bv)
55 : height(0), width(0), first_y(0),
56 bv_owner(bv), inset_owner(0), the_locking_inset(0),
57 need_break_row(0), refresh_y(0), refresh_row(0),
58 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
62 LyXText::LyXText(InsetText * inset)
63 : height(0), width(0), first_y(0),
64 bv_owner(0), inset_owner(inset), the_locking_inset(0),
65 need_break_row(0), refresh_y(0), refresh_row(0),
66 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
70 void LyXText::init(BufferView * bview, bool reinit)
73 // Delete all rows, this does not touch the paragraphs!
74 Row * tmprow = firstrow;
76 tmprow = firstrow->next();
85 copylayouttype.erase();
86 first_y = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(bview, par, lastrow);
98 setCursorIntern(bview, firstrow->par(), 0);
99 selection.cursor = cursor;
101 updateCounters(bview);
107 // Delete all rows, this does not touch the paragraphs!
108 Row * tmprow = firstrow;
110 tmprow = firstrow->next();
119 LyXFont const realizeFont(LyXFont const & font,
123 LyXTextClass const & tclass = buf->params.getLyXTextClass();
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 tmpfont.realize(par->layout()->font);
132 par_depth = par->getDepth();
136 tmpfont.realize(tclass.defaultfont());
144 // Gets the fully instantiated font at a given position in a paragraph
145 // Basically the same routine as Paragraph::getFont() in paragraph.C.
146 // The difference is that this one is used for displaying, and thus we
147 // are allowed to make cosmetic improvements. For instance make footnotes
149 // If position is -1, we get the layout font of the paragraph.
150 // If position is -2, we get the font of the manual label of the paragraph.
151 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
154 lyx::Assert(pos >= 0);
156 LyXLayout_ptr const & layout = par->layout();
158 // We specialize the 95% common case:
159 if (!par->getDepth()) {
160 if (layout->labeltype == LABEL_MANUAL
161 && pos < par->beginningOfMainBody()) {
163 LyXFont f = par->getFontSettings(buf->params, pos);
165 par->inInset()->getDrawFont(f);
166 return f.realize(layout->reslabelfont);
168 LyXFont f = par->getFontSettings(buf->params, pos);
170 par->inInset()->getDrawFont(f);
171 return f.realize(layout->resfont);
175 // The uncommon case need not be optimized as much
179 if (pos < par->beginningOfMainBody()) {
181 layoutfont = layout->labelfont;
184 layoutfont = layout->font;
187 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
188 tmpfont.realize(layoutfont);
191 par->inInset()->getDrawFont(tmpfont);
193 return realizeFont(tmpfont, buf, par);
197 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
199 LyXLayout_ptr const & layout = par->layout();
201 if (!par->getDepth()) {
202 return layout->resfont;
205 return realizeFont(layout->font, buf, par);
209 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
211 LyXLayout_ptr const & layout = par->layout();
213 if (!par->getDepth()) {
214 return layout->reslabelfont;
217 return realizeFont(layout->labelfont, buf, par);
221 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
222 pos_type pos, LyXFont const & fnt,
225 Buffer const * buf = bv->buffer();
226 LyXFont font = getFont(buf, par, pos);
227 font.update(fnt, buf->params.language, toggleall);
228 // Let the insets convert their font
229 if (par->isInset(pos)) {
230 Inset * inset = par->getInset(pos);
231 if (isEditableInset(inset)) {
232 UpdatableInset * uinset =
233 static_cast<UpdatableInset *>(inset);
234 uinset->setFont(bv, fnt, toggleall, true);
238 // Plug thru to version below:
239 setCharFont(buf, par, pos, font);
243 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
244 pos_type pos, LyXFont const & fnt)
248 LyXTextClass const & tclass = buf->params.getLyXTextClass();
249 LyXLayout_ptr const & layout = par->layout();
251 // Get concrete layout font to reduce against
254 if (pos < par->beginningOfMainBody())
255 layoutfont = layout->labelfont;
257 layoutfont = layout->font;
259 // Realize against environment font information
260 if (par->getDepth()) {
261 Paragraph * tp = par;
262 while (!layoutfont.resolved() && tp && tp->getDepth()) {
263 tp = tp->outerHook();
265 layoutfont.realize(tp->layout()->font);
269 layoutfont.realize(tclass.defaultfont());
271 // Now, reduce font against full layout font
272 font.reduce(layoutfont);
274 par->setFont(pos, font);
278 // inserts a new row behind the specified row, increments
279 // the touched counters
280 void LyXText::insertRow(Row * row, Paragraph * par,
283 Row * tmprow = new Row;
286 tmprow->next(firstrow);
289 tmprow->previous(row);
290 tmprow->next(row->next());
295 tmprow->next()->previous(tmprow);
297 if (tmprow->previous())
298 tmprow->previous()->next(tmprow);
309 // removes the row and reset the touched counters
310 void LyXText::removeRow(Row * row) const
312 Row * row_prev = row->previous();
314 row->next()->previous(row_prev);
316 firstrow = row->next();
317 // lyx::Assert(firstrow);
319 row_prev->next(row->next());
321 if (row == lastrow) {
322 lyx::Assert(!row->next());
325 if (refresh_row == row) {
326 refresh_row = row_prev ? row_prev : row->next();
327 // what about refresh_y, refresh_height
330 height -= row->height(); // the text becomes smaller
336 // remove all following rows of the paragraph of the specified row.
337 void LyXText::removeParagraph(Row * row) const
339 Paragraph * tmppar = row->par();
343 while (row && row->par() == tmppar) {
344 tmprow = row->next();
351 // insert the specified paragraph behind the specified row
352 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
355 // insert a new row, starting at position 0
356 insertRow(row, par, 0);
358 // and now append the whole paragraph behind the new row
361 appendParagraph(bview, firstrow);
363 row->next()->height(0);
364 appendParagraph(bview, row->next());
369 Inset * LyXText::getInset() const
372 if (cursor.pos() == 0 && cursor.par()->bibkey) {
373 inset = cursor.par()->bibkey;
374 } else if (cursor.pos() < cursor.par()->size()
375 && cursor.par()->isInset(cursor.pos())) {
376 inset = cursor.par()->getInset(cursor.pos());
382 void LyXText::toggleInset(BufferView * bview)
384 Inset * inset = getInset();
385 // is there an editable inset at cursor position?
386 if (!isEditableInset(inset)) {
387 // No, try to see if we are inside a collapsable inset
388 if (inset_owner && inset_owner->owner()
389 && inset_owner->owner()->isOpen()) {
390 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
391 inset_owner->owner()->close(bview);
392 bview->getLyXText()->cursorRight(bview);
396 //bview->owner()->message(inset->editMessage());
398 // do we want to keep this?? (JMarc)
399 if (!isHighlyEditableInset(inset))
400 setCursorParUndo(bview);
402 if (inset->isOpen()) {
408 inset->open(bview, !inset->isOpen());
413 /* used in setlayout */
414 // Asger is not sure we want to do this...
415 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
418 LyXLayout_ptr const & layout = par->layout();
421 for (pos_type pos = 0; pos < par->size(); ++pos) {
422 if (pos < par->beginningOfMainBody())
423 layoutfont = layout->labelfont;
425 layoutfont = layout->font;
427 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
428 tmpfont.reduce(layoutfont);
429 par->setFont(pos, tmpfont);
434 Paragraph * LyXText::setLayout(BufferView * bview,
435 LyXCursor & cur, LyXCursor & sstart_cur,
436 LyXCursor & send_cur,
437 string const & layout)
439 Paragraph * endpar = send_cur.par()->next();
440 Paragraph * undoendpar = endpar;
442 if (endpar && endpar->getDepth()) {
443 while (endpar && endpar->getDepth()) {
444 endpar = endpar->next();
448 endpar = endpar->next(); // because of parindents etc.
451 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
453 // ok we have a selection. This is always between sstart_cur
454 // and sel_end cursor
456 Paragraph * par = sstart_cur.par();
457 Paragraph * epar = send_cur.par()->next();
459 LyXLayout_ptr const & lyxlayout =
460 bview->buffer()->params.getLyXTextClass()[layout];
463 par->applyLayout(lyxlayout);
464 makeFontEntriesLayoutSpecific(bview->buffer(), par);
465 Paragraph * fppar = par;
466 fppar->params().spaceTop(lyxlayout->fill_top ?
467 VSpace(VSpace::VFILL)
468 : VSpace(VSpace::NONE));
469 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
470 VSpace(VSpace::VFILL)
471 : VSpace(VSpace::NONE));
472 if (lyxlayout->margintype == MARGIN_MANUAL)
473 par->setLabelWidthString(lyxlayout->labelstring());
474 if (lyxlayout->labeltype != LABEL_BIBLIO
476 delete fppar->bibkey;
481 } while (par != epar);
487 // set layout over selection and make a total rebreak of those paragraphs
488 void LyXText::setLayout(BufferView * bview, string const & layout)
490 LyXCursor tmpcursor = cursor; /* store the current cursor */
492 // if there is no selection just set the layout
493 // of the current paragraph */
494 if (!selection.set()) {
495 selection.start = cursor; // dummy selection
496 selection.end = cursor;
498 Paragraph * endpar = setLayout(bview, cursor, selection.start,
499 selection.end, layout);
500 redoParagraphs(bview, selection.start, endpar);
502 // we have to reset the selection, because the
503 // geometry could have changed
504 setCursor(bview, selection.start.par(),
505 selection.start.pos(), false);
506 selection.cursor = cursor;
507 setCursor(bview, selection.end.par(), selection.end.pos(), false);
508 updateCounters(bview);
511 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
515 // increment depth over selection and
516 // make a total rebreak of those paragraphs
517 void LyXText::incDepth(BufferView * bview)
519 // If there is no selection, just use the current paragraph
520 if (!selection.set()) {
521 selection.start = cursor; // dummy selection
522 selection.end = cursor;
525 // We end at the next paragraph with depth 0
526 Paragraph * endpar = selection.end.par()->next();
528 Paragraph * undoendpar = endpar;
530 if (endpar && endpar->getDepth()) {
531 while (endpar && endpar->getDepth()) {
532 endpar = endpar->next();
536 endpar = endpar->next(); // because of parindents etc.
539 setUndo(bview, Undo::EDIT,
540 selection.start.par(), undoendpar);
542 LyXCursor tmpcursor = cursor; // store the current cursor
544 // ok we have a selection. This is always between sel_start_cursor
545 // and sel_end cursor
546 cursor = selection.start;
549 // NOTE: you can't change the depth of a bibliography entry
550 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
551 Paragraph * prev = cursor.par()->previous();
554 if (cursor.par()->getDepth()
555 < prev->getMaxDepthAfter()) {
556 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
560 if (cursor.par() == selection.end.par())
562 cursor.par(cursor.par()->next());
565 redoParagraphs(bview, selection.start, endpar);
567 // we have to reset the selection, because the
568 // geometry could have changed
569 setCursor(bview, selection.start.par(), selection.start.pos());
570 selection.cursor = cursor;
571 setCursor(bview, selection.end.par(), selection.end.pos());
572 updateCounters(bview);
575 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
579 // decrement depth over selection and
580 // make a total rebreak of those paragraphs
581 void LyXText::decDepth(BufferView * bview)
583 // if there is no selection just set the layout
584 // of the current paragraph
585 if (!selection.set()) {
586 selection.start = cursor; // dummy selection
587 selection.end = cursor;
589 Paragraph * endpar = selection.end.par()->next();
590 Paragraph * undoendpar = endpar;
592 if (endpar && endpar->getDepth()) {
593 while (endpar && endpar->getDepth()) {
594 endpar = endpar->next();
598 endpar = endpar->next(); // because of parindents etc.
601 setUndo(bview, Undo::EDIT,
602 selection.start.par(), undoendpar);
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
608 cursor = selection.start;
611 if (cursor.par()->params().depth()) {
612 cursor.par()->params()
613 .depth(cursor.par()->params().depth() - 1);
615 if (cursor.par() == selection.end.par()) {
618 cursor.par(cursor.par()->next());
621 redoParagraphs(bview, selection.start, endpar);
623 // we have to reset the selection, because the
624 // geometry could have changed
625 setCursor(bview, selection.start.par(),
626 selection.start.pos());
627 selection.cursor = cursor;
628 setCursor(bview, selection.end.par(), selection.end.pos());
629 updateCounters(bview);
632 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
636 // set font over selection and make a total rebreak of those paragraphs
637 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
639 // if there is no selection just set the current_font
640 if (!selection.set()) {
641 // Determine basis font
643 if (cursor.pos() < cursor.par()->beginningOfMainBody()) {
644 layoutfont = getLabelFont(bview->buffer(),
647 layoutfont = getLayoutFont(bview->buffer(),
650 // Update current font
651 real_current_font.update(font,
652 bview->buffer()->params.language,
655 // Reduce to implicit settings
656 current_font = real_current_font;
657 current_font.reduce(layoutfont);
658 // And resolve it completely
659 real_current_font.realize(layoutfont);
664 LyXCursor tmpcursor = cursor; // store the current cursor
666 // ok we have a selection. This is always between sel_start_cursor
667 // and sel_end cursor
669 setUndo(bview, Undo::EDIT,
670 selection.start.par(), selection.end.par()->next());
672 cursor = selection.start;
673 while (cursor.par() != selection.end.par() ||
674 cursor.pos() < selection.end.pos())
676 if (cursor.pos() < cursor.par()->size()) {
677 // an open footnote should behave like a closed one
678 setCharFont(bview, cursor.par(), cursor.pos(),
680 cursor.pos(cursor.pos() + 1);
683 cursor.par(cursor.par()->next());
688 redoParagraphs(bview, selection.start, selection.end.par()->next());
690 // we have to reset the selection, because the
691 // geometry could have changed, but we keep
692 // it for user convenience
693 setCursor(bview, selection.start.par(), selection.start.pos());
694 selection.cursor = cursor;
695 setCursor(bview, selection.end.par(), selection.end.pos());
697 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
698 tmpcursor.boundary());
702 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
704 Row * tmprow = cur.row();
705 int y = cur.y() - tmprow->baseline();
707 setHeightOfRow(bview, tmprow);
709 while (tmprow->previous()
710 && tmprow->previous()->par() == tmprow->par()) {
711 tmprow = tmprow->previous();
712 y -= tmprow->height();
713 setHeightOfRow(bview, tmprow);
716 // we can set the refreshing parameters now
717 status(bview, LyXText::NEED_MORE_REFRESH);
719 refresh_row = tmprow;
720 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
724 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
726 Row * tmprow = cur.row();
728 int y = cur.y() - tmprow->baseline();
729 setHeightOfRow(bview, tmprow);
731 while (tmprow->previous()
732 && tmprow->previous()->par() == tmprow->par()) {
733 tmprow = tmprow->previous();
734 y -= tmprow->height();
737 // we can set the refreshing parameters now
738 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
740 refresh_row = tmprow;
742 status(bview, LyXText::NEED_MORE_REFRESH);
743 setCursor(bview, cur.par(), cur.pos());
747 // deletes and inserts again all paragaphs between the cursor
748 // and the specified par
749 // This function is needed after SetLayout and SetFont etc.
750 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
751 Paragraph const * endpar) const
754 Paragraph * tmppar = 0;
755 Paragraph * first_phys_par = 0;
757 Row * tmprow = cur.row();
759 int y = cur.y() - tmprow->baseline();
761 if (!tmprow->previous()) {
762 // a trick/hack for UNDO
763 // This is needed because in an UNDO/REDO we could have changed
764 // the ownerParagrah() so the paragraph inside the row is NOT
765 // my really first par anymore. Got it Lars ;) (Jug 20011206)
766 first_phys_par = ownerParagraph();
768 first_phys_par = tmprow->par();
769 while (tmprow->previous()
770 && tmprow->previous()->par() == first_phys_par)
772 tmprow = tmprow->previous();
773 y -= tmprow->height();
777 // we can set the refreshing parameters now
778 status(bview, LyXText::NEED_MORE_REFRESH);
780 refresh_row = tmprow->previous(); /* the real refresh row will
781 be deleted, so I store
785 tmppar = tmprow->next()->par();
788 while (tmprow->next() && tmppar != endpar) {
789 removeRow(tmprow->next());
790 if (tmprow->next()) {
791 tmppar = tmprow->next()->par();
797 // remove the first one
798 tmprow2 = tmprow; /* this is because tmprow->previous()
800 tmprow = tmprow->previous();
803 tmppar = first_phys_par;
807 insertParagraph(bview, tmppar, tmprow);
811 while (tmprow->next()
812 && tmprow->next()->par() == tmppar) {
813 tmprow = tmprow->next();
815 tmppar = tmppar->next();
817 } while (tmppar && tmppar != endpar);
819 // this is because of layout changes
821 refresh_y -= refresh_row->height();
822 setHeightOfRow(bview, refresh_row);
824 refresh_row = firstrow;
826 setHeightOfRow(bview, refresh_row);
829 if (tmprow && tmprow->next())
830 setHeightOfRow(bview, tmprow->next());
831 updateCounters(bview);
835 void LyXText::fullRebreak(BufferView * bview)
841 if (need_break_row) {
842 breakAgain(bview, need_break_row);
849 // important for the screen
852 // the cursor set functions have a special mechanism. When they
853 // realize, that you left an empty paragraph, they will delete it.
854 // They also delete the corresponding row
856 // need the selection cursor:
857 void LyXText::setSelection(BufferView * bview)
859 bool const lsel = selection.set();
861 if (!selection.set()) {
862 last_sel_cursor = selection.cursor;
863 selection.start = selection.cursor;
864 selection.end = selection.cursor;
869 // first the toggling area
870 if (cursor.y() < last_sel_cursor.y()
871 || (cursor.y() == last_sel_cursor.y()
872 && cursor.x() < last_sel_cursor.x())) {
873 toggle_end_cursor = last_sel_cursor;
874 toggle_cursor = cursor;
876 toggle_end_cursor = cursor;
877 toggle_cursor = last_sel_cursor;
880 last_sel_cursor = cursor;
882 // and now the whole selection
884 if (selection.cursor.par() == cursor.par())
885 if (selection.cursor.pos() < cursor.pos()) {
886 selection.end = cursor;
887 selection.start = selection.cursor;
889 selection.end = selection.cursor;
890 selection.start = cursor;
892 else if (selection.cursor.y() < cursor.y() ||
893 (selection.cursor.y() == cursor.y()
894 && selection.cursor.x() < cursor.x())) {
895 selection.end = cursor;
896 selection.start = selection.cursor;
899 selection.end = selection.cursor;
900 selection.start = cursor;
903 // a selection with no contents is not a selection
904 if (selection.start.par() == selection.end.par() &&
905 selection.start.pos() == selection.end.pos())
906 selection.set(false);
908 if (inset_owner && (selection.set() || lsel))
909 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
913 string const LyXText::selectionAsString(Buffer const * buffer,
916 if (!selection.set()) return string();
918 // should be const ...
919 Paragraph * startpar(selection.start.par());
920 Paragraph * endpar(selection.end.par());
921 pos_type const startpos(selection.start.pos());
922 pos_type const endpos(selection.end.pos());
924 if (startpar == endpar) {
925 return startpar->asString(buffer, startpos, endpos, label);
930 // First paragraph in selection
931 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
933 // The paragraphs in between (if any)
934 LyXCursor tmpcur(selection.start);
935 tmpcur.par(tmpcur.par()->next());
936 while (tmpcur.par() != endpar) {
937 result += tmpcur.par()->asString(buffer, 0,
938 tmpcur.par()->size(),
940 tmpcur.par(tmpcur.par()->next());
943 // Last paragraph in selection
944 result += endpar->asString(buffer, 0, endpos, label);
950 void LyXText::clearSelection() const
952 selection.set(false);
953 selection.mark(false);
954 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
955 // reset this in the bv_owner!
956 if (bv_owner && bv_owner->text)
957 bv_owner->text->xsel_cache.set(false);
961 void LyXText::cursorHome(BufferView * bview) const
963 setCursor(bview, cursor.par(), cursor.row()->pos());
967 void LyXText::cursorEnd(BufferView * bview) const
969 if (!cursor.row()->next()
970 || cursor.row()->next()->par() != cursor.row()->par()) {
971 setCursor(bview, cursor.par(), cursor.row()->lastPos() + 1);
973 if (!cursor.par()->empty() &&
974 (cursor.par()->getChar(cursor.row()->lastPos()) == ' '
975 || cursor.par()->isNewline(cursor.row()->lastPos()))) {
976 setCursor(bview, cursor.par(), cursor.row()->lastPos());
978 setCursor(bview,cursor.par(),
979 cursor.row()->lastPos() + 1);
985 void LyXText::cursorTop(BufferView * bview) const
987 while (cursor.par()->previous())
988 cursor.par(cursor.par()->previous());
989 setCursor(bview, cursor.par(), 0);
993 void LyXText::cursorBottom(BufferView * bview) const
995 while (cursor.par()->next())
996 cursor.par(cursor.par()->next());
997 setCursor(bview, cursor.par(), cursor.par()->size());
1001 void LyXText::toggleFree(BufferView * bview,
1002 LyXFont const & font, bool toggleall)
1004 // If the mask is completely neutral, tell user
1005 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1006 // Could only happen with user style
1007 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1011 // Try implicit word selection
1012 // If there is a change in the language the implicit word selection
1014 LyXCursor resetCursor = cursor;
1015 bool implicitSelection = (font.language() == ignore_language
1016 && font.number() == LyXFont::IGNORE)
1017 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1020 setFont(bview, font, toggleall);
1022 // Implicit selections are cleared afterwards
1023 //and cursor is set to the original position.
1024 if (implicitSelection) {
1026 cursor = resetCursor;
1027 setCursor(bview, cursor.par(), cursor.pos());
1028 selection.cursor = cursor;
1031 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1035 string LyXText::getStringToIndex(BufferView * bview)
1037 // Try implicit word selection
1038 // If there is a change in the language the implicit word selection
1040 LyXCursor const reset_cursor = cursor;
1041 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1044 if (!selection.set())
1045 bview->owner()->message(_("Nothing to index!"));
1046 else if (selection.start.par() != selection.end.par())
1047 bview->owner()->message(_("Cannot index more than one paragraph!"));
1049 idxstring = selectionAsString(bview->buffer(), false);
1051 // Reset cursors to their original position.
1052 cursor = reset_cursor;
1053 setCursor(bview, cursor.par(), cursor.pos());
1054 selection.cursor = cursor;
1056 // Clear the implicit selection.
1057 if (implicitSelection)
1064 // the DTP switches for paragraphs. LyX will store them in the first
1065 // physicla paragraph. When a paragraph is broken, the top settings rest,
1066 // the bottom settings are given to the new one. So I can make shure,
1067 // they do not duplicate themself and you cannnot make dirty things with
1070 void LyXText::setParagraph(BufferView * bview,
1071 bool line_top, bool line_bottom,
1072 bool pagebreak_top, bool pagebreak_bottom,
1073 VSpace const & space_top,
1074 VSpace const & space_bottom,
1075 Spacing const & spacing,
1077 string labelwidthstring,
1080 LyXCursor tmpcursor = cursor;
1081 if (!selection.set()) {
1082 selection.start = cursor;
1083 selection.end = cursor;
1086 // make sure that the depth behind the selection are restored, too
1087 Paragraph * endpar = selection.end.par()->next();
1088 Paragraph * undoendpar = endpar;
1090 if (endpar && endpar->getDepth()) {
1091 while (endpar && endpar->getDepth()) {
1092 endpar = endpar->next();
1093 undoendpar = endpar;
1097 // because of parindents etc.
1098 endpar = endpar->next();
1101 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1104 Paragraph * tmppar = selection.end.par();
1106 while (tmppar != selection.start.par()->previous()) {
1107 setCursor(bview, tmppar, 0);
1108 status(bview, LyXText::NEED_MORE_REFRESH);
1109 refresh_row = cursor.row();
1110 refresh_y = cursor.y() - cursor.row()->baseline();
1111 cursor.par()->params().lineTop(line_top);
1112 cursor.par()->params().lineBottom(line_bottom);
1113 cursor.par()->params().pagebreakTop(pagebreak_top);
1114 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1115 cursor.par()->params().spaceTop(space_top);
1116 cursor.par()->params().spaceBottom(space_bottom);
1117 cursor.par()->params().spacing(spacing);
1118 // does the layout allow the new alignment?
1119 LyXLayout_ptr const & layout = cursor.par()->layout();
1121 if (align == LYX_ALIGN_LAYOUT)
1122 align = layout->align;
1123 if (align & layout->alignpossible) {
1124 if (align == layout->align)
1125 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1127 cursor.par()->params().align(align);
1129 cursor.par()->setLabelWidthString(labelwidthstring);
1130 cursor.par()->params().noindent(noindent);
1131 tmppar = cursor.par()->previous();
1134 redoParagraphs(bview, selection.start, endpar);
1137 setCursor(bview, selection.start.par(), selection.start.pos());
1138 selection.cursor = cursor;
1139 setCursor(bview, selection.end.par(), selection.end.pos());
1140 setSelection(bview);
1141 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1143 bview->updateInset(inset_owner, true);
1147 // set the counter of a paragraph. This includes the labels
1148 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1150 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1151 LyXLayout_ptr const & layout = par->layout();
1153 if (par->previous()) {
1155 par->params().appendix(par->previous()->params().appendix());
1156 if (!par->params().appendix() && par->params().startOfAppendix()) {
1157 par->params().appendix(true);
1158 textclass.counters().reset();
1160 par->enumdepth = par->previous()->enumdepth;
1161 par->itemdepth = par->previous()->itemdepth;
1163 par->params().appendix(par->params().startOfAppendix());
1168 /* Maybe we have to increment the enumeration depth.
1169 * BUT, enumeration in a footnote is considered in isolation from its
1170 * surrounding paragraph so don't increment if this is the
1171 * first line of the footnote
1172 * AND, bibliographies can't have their depth changed ie. they
1173 * are always of depth 0
1176 && par->previous()->getDepth() < par->getDepth()
1177 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1178 && par->enumdepth < 3
1179 && layout->labeltype != LABEL_BIBLIO) {
1183 // Maybe we have to decrement the enumeration depth, see note above
1185 && par->previous()->getDepth() > par->getDepth()
1186 && layout->labeltype != LABEL_BIBLIO) {
1187 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1190 if (!par->params().labelString().empty()) {
1191 par->params().labelString(string());
1194 if (layout->margintype == MARGIN_MANUAL) {
1195 if (par->params().labelWidthString().empty()) {
1196 par->setLabelWidthString(layout->labelstring());
1199 par->setLabelWidthString(string());
1202 // is it a layout that has an automatic label?
1203 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1204 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1208 if (i >= 0 && i <= buf->params.secnumdepth) {
1212 textclass.counters().step(layout->latexname());
1214 // Is there a label? Useful for Chapter layout
1215 if (!par->params().appendix()) {
1216 s << layout->labelstring();
1218 s << layout->labelstring_appendix();
1221 // Use of an integer is here less than elegant. For now.
1222 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1223 if (!par->params().appendix()) {
1224 numbertype = "sectioning";
1226 numbertype = "appendix";
1227 if (par->isRightToLeftPar(buf->params))
1228 langtype = "hebrew";
1233 s << textclass.counters()
1234 .numberLabel(layout->latexname(),
1235 numbertype, langtype, head);
1237 par->params().labelString(STRCONV(s.str()));
1239 // reset enum counters
1240 textclass.counters().reset("enum");
1241 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1242 textclass.counters().reset("enum");
1243 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1245 // Yes I know this is a really, really! bad solution
1247 string enumcounter("enum");
1249 switch (par->enumdepth) {
1258 enumcounter += "iv";
1261 // not a valid enumdepth...
1265 textclass.counters().step(enumcounter);
1267 s << textclass.counters()
1268 .numberLabel(enumcounter, "enumeration");
1269 par->params().labelString(STRCONV(s.str()));
1271 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1272 textclass.counters().step("bibitem");
1273 int number = textclass.counters().value("bibitem");
1275 InsetCommandParams p("bibitem");
1276 par->bibkey = new InsetBibKey(p);
1278 par->bibkey->setCounter(number);
1279 par->params().labelString(layout->labelstring());
1281 // In biblio should't be following counters but...
1283 string s = layout->labelstring();
1285 // the caption hack:
1286 if (layout->labeltype == LABEL_SENSITIVE) {
1287 Paragraph * tmppar = par;
1290 while (tmppar && tmppar->inInset()
1291 // the single '=' is intended below
1292 && (in = tmppar->inInset()->owner())) {
1293 if (in->lyxCode() == Inset::FLOAT_CODE ||
1294 in->lyxCode() == Inset::WRAP_CODE) {
1298 tmppar = in->parOwner();
1304 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1306 textclass.counters().step(fl.type());
1308 // Doesn't work... yet.
1309 #warning use boost.format
1310 #if USE_BOOST_FORMAT
1311 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1312 // s << boost::format(_("%1$s %1$d:")
1314 // % buf->counters().value(fl.name());
1317 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1318 o << fl.name() << " #:";
1319 s = STRCONV(o.str());
1322 // par->SetLayout(0);
1323 // s = layout->labelstring;
1324 s = _("Senseless: ");
1327 par->params().labelString(s);
1329 // reset the enumeration counter. They are always reset
1330 // when there is any other layout between
1331 // Just fall-through between the cases so that all
1332 // enum counters deeper than enumdepth is also reset.
1333 switch (par->enumdepth) {
1335 textclass.counters().reset("enumi");
1337 textclass.counters().reset("enumii");
1339 textclass.counters().reset("enumiii");
1341 textclass.counters().reset("enumiv");
1347 // Updates all counters. Paragraphs with changed label string will be rebroken
1348 void LyXText::updateCounters(BufferView * bview) const
1350 Row * row = firstrow;
1351 Paragraph * par = row->par();
1353 // CHECK if this is really needed. (Lgb)
1354 bview->buffer()->params.getLyXTextClass().counters().reset();
1357 while (row->par() != par)
1360 string const oldLabel = par->params().labelString();
1362 // setCounter can potentially change the labelString.
1363 setCounter(bview->buffer(), par);
1365 string const & newLabel = par->params().labelString();
1367 if (oldLabel.empty() && !newLabel.empty()) {
1368 removeParagraph(row);
1369 appendParagraph(bview, row);
1377 void LyXText::insertInset(BufferView * bview, Inset * inset)
1379 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1381 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1383 cursor.par()->insertInset(cursor.pos(), inset);
1384 // Just to rebreak and refresh correctly.
1385 // The character will not be inserted a second time
1386 insertChar(bview, Paragraph::META_INSET);
1387 // If we enter a highly editable inset the cursor should be to before
1388 // the inset. This couldn't happen before as Undo was not handled inside
1389 // inset now after the Undo LyX tries to call inset->Edit(...) again
1390 // and cannot do this as the cursor is behind the inset and GetInset
1391 // does not return the inset!
1392 if (isHighlyEditableInset(inset)) {
1393 cursorLeft(bview, true);
1399 void LyXText::copyEnvironmentType()
1401 copylayouttype = cursor.par()->layout()->name();
1405 void LyXText::pasteEnvironmentType(BufferView * bview)
1407 // do nothing if there has been no previous copyEnvironmentType()
1408 if (!copylayouttype.empty())
1409 setLayout(bview, copylayouttype);
1413 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1415 // Stuff what we got on the clipboard. Even if there is no selection.
1417 // There is a problem with having the stuffing here in that the
1418 // larger the selection the slower LyX will get. This can be
1419 // solved by running the line below only when the selection has
1420 // finished. The solution used currently just works, to make it
1421 // faster we need to be more clever and probably also have more
1422 // calls to stuffClipboard. (Lgb)
1423 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1425 // This doesn't make sense, if there is no selection
1426 if (!selection.set())
1429 // OK, we have a selection. This is always between selection.start
1430 // and selection.end
1432 // make sure that the depth behind the selection are restored, too
1433 Paragraph * endpar = selection.end.par()->next();
1434 Paragraph * undoendpar = endpar;
1436 if (endpar && endpar->getDepth()) {
1437 while (endpar && endpar->getDepth()) {
1438 endpar = endpar->next();
1439 undoendpar = endpar;
1441 } else if (endpar) {
1442 endpar = endpar->next(); // because of parindents etc.
1445 setUndo(bview, Undo::DELETE,
1446 selection.start.par(), undoendpar);
1448 // there are two cases: cut only within one paragraph or
1449 // more than one paragraph
1450 if (selection.start.par() == selection.end.par()) {
1451 // only within one paragraph
1452 endpar = selection.end.par();
1453 int pos = selection.end.pos();
1454 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1455 selection.start.pos(), pos,
1456 bview->buffer()->params.textclass,
1458 selection.end.pos(pos);
1460 endpar = selection.end.par();
1461 int pos = selection.end.pos();
1462 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1463 selection.start.pos(), pos,
1464 bview->buffer()->params.textclass,
1467 selection.end.par(endpar);
1468 selection.end.pos(pos);
1469 cursor.pos(selection.end.pos());
1471 endpar = endpar->next();
1473 // sometimes necessary
1475 selection.start.par()->stripLeadingSpaces();
1477 redoParagraphs(bview, selection.start, endpar);
1479 // cutSelection can invalidate the cursor so we need to set
1481 // we prefer the end for when tracking changes
1482 cursor = selection.end;
1484 // need a valid cursor. (Lgb)
1487 setCursor(bview, cursor.par(), cursor.pos());
1488 selection.cursor = cursor;
1489 updateCounters(bview);
1493 void LyXText::copySelection(BufferView * bview)
1495 // stuff the selection onto the X clipboard, from an explicit copy request
1496 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1498 // this doesnt make sense, if there is no selection
1499 if (!selection.set())
1502 // ok we have a selection. This is always between selection.start
1503 // and sel_end cursor
1505 // copy behind a space if there is one
1506 while (selection.start.par()->size() > selection.start.pos()
1507 && selection.start.par()->isLineSeparator(selection.start.pos())
1508 && (selection.start.par() != selection.end.par()
1509 || selection.start.pos() < selection.end.pos()))
1510 selection.start.pos(selection.start.pos() + 1);
1512 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1513 selection.start.pos(), selection.end.pos(),
1514 bview->buffer()->params.textclass);
1518 void LyXText::pasteSelection(BufferView * bview)
1520 // this does not make sense, if there is nothing to paste
1521 if (!CutAndPaste::checkPastePossible(cursor.par()))
1524 setUndo(bview, Undo::INSERT,
1525 cursor.par(), cursor.par()->next());
1528 Paragraph * actpar = cursor.par();
1529 int pos = cursor.pos();
1531 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1532 bview->buffer()->params.textclass);
1534 redoParagraphs(bview, cursor, endpar);
1536 setCursor(bview, cursor.par(), cursor.pos());
1539 selection.cursor = cursor;
1540 setCursor(bview, actpar, pos);
1541 setSelection(bview);
1542 updateCounters(bview);
1546 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1551 selection.cursor = cursor;
1554 setSelection(bview);
1558 // simple replacing. The font of the first selected character is used
1559 void LyXText::replaceSelectionWithString(BufferView * bview,
1562 setCursorParUndo(bview);
1565 if (!selection.set()) { // create a dummy selection
1566 selection.end = cursor;
1567 selection.start = cursor;
1570 // Get font setting before we cut
1571 pos_type pos = selection.end.pos();
1572 LyXFont const font = selection.start.par()
1573 ->getFontSettings(bview->buffer()->params,
1574 selection.start.pos());
1576 // Insert the new string
1577 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1578 selection.end.par()->insertChar(pos, (*cit), font);
1582 // Cut the selection
1583 cutSelection(bview, true, false);
1589 // needed to insert the selection
1590 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1592 Paragraph * par = cursor.par();
1593 pos_type pos = cursor.pos();
1594 Paragraph * endpar = cursor.par()->next();
1596 setCursorParUndo(bview);
1598 // only to be sure, should not be neccessary
1601 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1603 redoParagraphs(bview, cursor, endpar);
1604 setCursor(bview, cursor.par(), cursor.pos());
1605 selection.cursor = cursor;
1606 setCursor(bview, par, pos);
1607 setSelection(bview);
1611 // turns double-CR to single CR, others where converted into one
1612 // blank. Then InsertStringAsLines is called
1613 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1615 string linestr(str);
1616 bool newline_inserted = false;
1617 for (string::size_type i = 0; i < linestr.length(); ++i) {
1618 if (linestr[i] == '\n') {
1619 if (newline_inserted) {
1620 // we know that \r will be ignored by
1621 // InsertStringA. Of course, it is a dirty
1622 // trick, but it works...
1623 linestr[i - 1] = '\r';
1627 newline_inserted = true;
1629 } else if (IsPrintable(linestr[i])) {
1630 newline_inserted = false;
1633 insertStringAsLines(bview, linestr);
1637 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1640 LyXCursor tmpcursor;
1644 Row * row = getRow(par, pos, y);
1646 // is there a break one row above
1647 if (row->previous() && row->previous()->par() == row->par()) {
1648 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1649 if (z >= row->pos()) {
1650 // set the dimensions of the row above
1651 y -= row->previous()->height();
1653 refresh_row = row->previous();
1654 status(bview, LyXText::NEED_MORE_REFRESH);
1656 breakAgain(bview, row->previous());
1658 // set the cursor again. Otherwise
1659 // dangling pointers are possible
1660 setCursor(bview, cursor.par(), cursor.pos(),
1661 false, cursor.boundary());
1662 selection.cursor = cursor;
1667 int const tmpheight = row->height();
1668 pos_type const tmplast = row->lastPos();
1672 breakAgain(bview, row);
1673 if (row->height() == tmpheight && row->lastPos() == tmplast)
1674 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1676 status(bview, LyXText::NEED_MORE_REFRESH);
1678 // check the special right address boxes
1679 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1686 redoDrawingOfParagraph(bview, tmpcursor);
1689 // set the cursor again. Otherwise dangling pointers are possible
1690 // also set the selection
1692 if (selection.set()) {
1694 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1695 false, selection.cursor.boundary());
1696 selection.cursor = cursor;
1697 setCursorIntern(bview, selection.start.par(),
1698 selection.start.pos(),
1699 false, selection.start.boundary());
1700 selection.start = cursor;
1701 setCursorIntern(bview, selection.end.par(),
1702 selection.end.pos(),
1703 false, selection.end.boundary());
1704 selection.end = cursor;
1705 setCursorIntern(bview, last_sel_cursor.par(),
1706 last_sel_cursor.pos(),
1707 false, last_sel_cursor.boundary());
1708 last_sel_cursor = cursor;
1711 setCursorIntern(bview, cursor.par(), cursor.pos(),
1712 false, cursor.boundary());
1716 // returns false if inset wasn't found
1717 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1719 // first check the current paragraph
1720 int pos = cursor.par()->getPositionOfInset(inset);
1722 checkParagraph(bview, cursor.par(), pos);
1726 // check every paragraph
1728 Paragraph * par = ownerParagraph();
1730 pos = par->getPositionOfInset(inset);
1732 checkParagraph(bview, par, pos);
1742 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1744 bool setfont, bool boundary) const
1746 LyXCursor old_cursor = cursor;
1747 setCursorIntern(bview, par, pos, setfont, boundary);
1748 return deleteEmptyParagraphMechanism(bview, old_cursor);
1752 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1753 pos_type pos, bool boundary) const
1760 cur.boundary(boundary);
1762 // get the cursor y position in text
1764 Row * row = getRow(par, pos, y);
1765 Row * old_row = row;
1767 // if we are before the first char of this row and are still in the
1768 // same paragraph and there is a previous row then put the cursor on
1769 // the end of the previous row
1770 cur.iy(y + row->baseline());
1772 if (row->previous() && pos &&
1773 row->previous()->par() == row->par() &&
1774 par->getChar(pos) == Paragraph::META_INSET &&
1775 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1777 row = row->previous();
1782 // y is now the beginning of the cursor row
1783 y += row->baseline();
1784 // y is now the cursor baseline
1787 pos_type last = old_row->lastPrintablePos();
1789 // None of these should happen, but we're scaredy-cats
1790 if (pos > par->size()) {
1793 } else if (pos > last + 1) {
1794 // This shouldn't happen.
1797 } else if (pos < row->pos()) {
1802 // now get the cursors x position
1803 float x = getCursorX(bview, row, pos, last, boundary);
1806 if (old_row != row) {
1807 x = getCursorX(bview, old_row, pos, last, boundary);
1814 float LyXText::getCursorX(BufferView * bview, Row * row,
1815 pos_type pos, pos_type last, bool boundary) const
1817 pos_type cursor_vpos = 0;
1819 float fill_separator;
1821 float fill_label_hfill;
1822 // This call HAS to be here because of the BidiTables!!!
1823 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1826 if (last < row->pos())
1827 cursor_vpos = row->pos();
1828 else if (pos > last && !boundary)
1829 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1830 ? row->pos() : last + 1;
1831 else if (pos > row->pos() &&
1832 (pos > last || boundary))
1833 /// Place cursor after char at (logical) position pos - 1
1834 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1835 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1837 /// Place cursor before char at (logical) position pos
1838 cursor_vpos = (bidi_level(pos) % 2 == 0)
1839 ? log2vis(pos) : log2vis(pos) + 1;
1841 pos_type main_body = row->par()->beginningOfMainBody();
1842 if ((main_body > 0) &&
1843 ((main_body-1 > last) ||
1844 !row->par()->isLineSeparator(main_body-1)))
1847 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1848 pos_type pos = vis2log(vpos);
1849 if (main_body > 0 && pos == main_body - 1) {
1850 x += fill_label_hfill +
1851 font_metrics::width(
1852 row->par()->layout()->labelsep,
1853 getLabelFont(bview->buffer(),
1855 if (row->par()->isLineSeparator(main_body - 1))
1856 x -= singleWidth(bview,
1857 row->par(), main_body - 1);
1859 if (row->hfillExpansion(pos)) {
1860 x += singleWidth(bview, row->par(), pos);
1861 if (pos >= main_body)
1864 x += fill_label_hfill;
1865 } else if (row->par()->isSeparator(pos)) {
1866 x += singleWidth(bview, row->par(), pos);
1867 if (pos >= main_body)
1868 x += fill_separator;
1870 x += singleWidth(bview, row->par(), pos);
1876 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1877 pos_type pos, bool setfont, bool boundary) const
1879 InsetText * it = static_cast<InsetText *>(par->inInset());
1881 if (it != inset_owner) {
1882 lyxerr[Debug::INSETS] << "InsetText is " << it
1884 << "inset_owner is "
1885 << inset_owner << endl;
1886 #ifdef WITH_WARNINGS
1887 #warning I believe this code is wrong. (Lgb)
1888 #warning Jürgen, have a look at this. (Lgb)
1889 #warning Hmmm, I guess you are right but we
1890 #warning should verify when this is needed
1892 // Jürgen, would you like to have a look?
1893 // I guess we need to move the outer cursor
1894 // and open and lock the inset (bla bla bla)
1895 // stuff I don't know... so can you have a look?
1897 // I moved the lyxerr stuff in here so we can see if
1898 // this is actually really needed and where!
1900 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1905 setCursor(bview, cursor, par, pos, boundary);
1907 setCurrentFont(bview);
1911 void LyXText::setCurrentFont(BufferView * bview) const
1913 pos_type pos = cursor.pos();
1914 if (cursor.boundary() && pos > 0)
1918 if (pos == cursor.par()->size())
1920 else // potentional bug... BUG (Lgb)
1921 if (cursor.par()->isSeparator(pos)) {
1922 if (pos > cursor.row()->pos() &&
1923 bidi_level(pos) % 2 ==
1924 bidi_level(pos - 1) % 2)
1926 else if (pos + 1 < cursor.par()->size())
1932 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1933 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1935 if (cursor.pos() == cursor.par()->size() &&
1936 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1937 !cursor.boundary()) {
1938 Language const * lang =
1939 cursor.par()->getParLanguage(bview->buffer()->params);
1940 current_font.setLanguage(lang);
1941 current_font.setNumber(LyXFont::OFF);
1942 real_current_font.setLanguage(lang);
1943 real_current_font.setNumber(LyXFont::OFF);
1948 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1950 LyXCursor old_cursor = cursor;
1952 setCursorFromCoordinates(bview, cursor, x, y);
1953 setCurrentFont(bview);
1954 deleteEmptyParagraphMechanism(bview, old_cursor);
1961 * return true if the cursor given is at the end of a row,
1962 * and the next row is filled by an inset that spans an entire
1965 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1968 Row const & next = *row.next();
1970 if (next.pos() != cur.pos() || next.par() != cur.par())
1972 if (!cur.par()->isInset(cur.pos()))
1974 Inset const * inset = cur.par()->getInset(cur.pos());
1975 if (inset->needFullRow() || inset->display())
1982 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1985 // Get the row first.
1987 Row * row = getRowNearY(y);
1989 pos_type const column = getColumnNearX(bview, row, x, bound);
1990 cur.par(row->par());
1991 cur.pos(row->pos() + column);
1993 cur.y(y + row->baseline());
1996 if (beforeFullRowInset(*row, cur)) {
1997 pos_type last = row->lastPrintablePos();
1998 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2000 cur.iy(y + row->height() + row->next()->baseline());
2001 cur.irow(row->next());
2007 cur.boundary(bound);
2011 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2013 if (cursor.pos() > 0) {
2014 bool boundary = cursor.boundary();
2015 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2016 if (!internal && !boundary &&
2017 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2018 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2019 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2020 Paragraph * par = cursor.par()->previous();
2021 setCursor(bview, par, par->size());
2026 void LyXText::cursorRight(BufferView * bview, bool internal) const
2028 if (!internal && cursor.boundary() &&
2029 !cursor.par()->isNewline(cursor.pos()))
2030 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2031 else if (cursor.pos() < cursor.par()->size()) {
2032 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2034 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2035 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2036 } else if (cursor.par()->next())
2037 setCursor(bview, cursor.par()->next(), 0);
2041 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2044 int x = cursor.x_fix();
2045 int y = cursor.y() - cursor.row()->baseline() - 1;
2046 setCursorFromCoordinates(bview, x, y);
2048 int y1 = cursor.iy() - first_y;
2051 Inset * inset_hit = checkInsetHit(bview, x, y1);
2052 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2053 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2057 setCursorFromCoordinates(bview, cursor.x_fix(),
2058 cursor.y() - cursor.row()->baseline() - 1);
2063 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2066 int x = cursor.x_fix();
2067 int y = cursor.y() - cursor.row()->baseline() +
2068 cursor.row()->height() + 1;
2069 setCursorFromCoordinates(bview, x, y);
2070 if (!selecting && cursor.row() == cursor.irow()) {
2071 int y1 = cursor.iy() - first_y;
2074 Inset * inset_hit = checkInsetHit(bview, x, y1);
2075 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2076 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2080 setCursorFromCoordinates(bview, cursor.x_fix(),
2081 cursor.y() - cursor.row()->baseline()
2082 + cursor.row()->height() + 1);
2087 void LyXText::cursorUpParagraph(BufferView * bview) const
2089 if (cursor.pos() > 0) {
2090 setCursor(bview, cursor.par(), 0);
2092 else if (cursor.par()->previous()) {
2093 setCursor(bview, cursor.par()->previous(), 0);
2098 void LyXText::cursorDownParagraph(BufferView * bview) const
2100 if (cursor.par()->next()) {
2101 setCursor(bview, cursor.par()->next(), 0);
2103 setCursor(bview, cursor.par(), cursor.par()->size());
2107 // fix the cursor `cur' after a characters has been deleted at `where'
2108 // position. Called by deleteEmptyParagraphMechanism
2109 void LyXText::fixCursorAfterDelete(BufferView * bview,
2111 LyXCursor const & where) const
2113 // if cursor is not in the paragraph where the delete occured,
2115 if (cur.par() != where.par())
2118 // if cursor position is after the place where the delete occured,
2120 if (cur.pos() > where.pos())
2121 cur.pos(cur.pos()-1);
2123 // check also if we don't want to set the cursor on a spot behind the
2124 // pagragraph because we erased the last character.
2125 if (cur.pos() > cur.par()->size())
2126 cur.pos(cur.par()->size());
2128 // recompute row et al. for this cursor
2129 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2133 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2134 LyXCursor const & old_cursor) const
2136 // Would be wrong to delete anything if we have a selection.
2137 if (selection.set())
2140 // We allow all kinds of "mumbo-jumbo" when freespacing.
2141 if (old_cursor.par()->layout()->free_spacing
2142 || old_cursor.par()->isFreeSpacing()) {
2146 /* Ok I'll put some comments here about what is missing.
2147 I have fixed BackSpace (and thus Delete) to not delete
2148 double-spaces automagically. I have also changed Cut,
2149 Copy and Paste to hopefully do some sensible things.
2150 There are still some small problems that can lead to
2151 double spaces stored in the document file or space at
2152 the beginning of paragraphs. This happens if you have
2153 the cursor betwenn to spaces and then save. Or if you
2154 cut and paste and the selection have a space at the
2155 beginning and then save right after the paste. I am
2156 sure none of these are very hard to fix, but I will
2157 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2158 that I can get some feedback. (Lgb)
2161 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2162 // delete the LineSeparator.
2165 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2166 // delete the LineSeparator.
2169 // If the pos around the old_cursor were spaces, delete one of them.
2170 if (old_cursor.par() != cursor.par()
2171 || old_cursor.pos() != cursor.pos()) {
2172 // Only if the cursor has really moved
2174 if (old_cursor.pos() > 0
2175 && old_cursor.pos() < old_cursor.par()->size()
2176 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2177 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2178 old_cursor.par()->erase(old_cursor.pos() - 1);
2179 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2181 #ifdef WITH_WARNINGS
2182 #warning This will not work anymore when we have multiple views of the same buffer
2183 // In this case, we will have to correct also the cursors held by
2184 // other bufferviews. It will probably be easier to do that in a more
2185 // automated way in LyXCursor code. (JMarc 26/09/2001)
2187 // correct all cursors held by the LyXText
2188 fixCursorAfterDelete(bview, cursor, old_cursor);
2189 fixCursorAfterDelete(bview, selection.cursor,
2191 fixCursorAfterDelete(bview, selection.start,
2193 fixCursorAfterDelete(bview, selection.end, old_cursor);
2194 fixCursorAfterDelete(bview, last_sel_cursor,
2196 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2197 fixCursorAfterDelete(bview, toggle_end_cursor,
2203 // don't delete anything if this is the ONLY paragraph!
2204 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2207 // Do not delete empty paragraphs with keepempty set.
2208 if (old_cursor.par()->layout()->keepempty)
2211 // only do our magic if we changed paragraph
2212 if (old_cursor.par() == cursor.par())
2215 // record if we have deleted a paragraph
2216 // we can't possibly have deleted a paragraph before this point
2217 bool deleted = false;
2219 if ((old_cursor.par()->empty()
2220 || (old_cursor.par()->size() == 1
2221 && old_cursor.par()->isLineSeparator(0)))) {
2222 // ok, we will delete anything
2223 LyXCursor tmpcursor;
2225 // make sure that you do not delete any environments
2226 status(bview, LyXText::NEED_MORE_REFRESH);
2229 if (old_cursor.row()->previous()) {
2230 refresh_row = old_cursor.row()->previous();
2231 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2233 cursor = old_cursor; // that undo can restore the right cursor position
2234 Paragraph * endpar = old_cursor.par()->next();
2235 if (endpar && endpar->getDepth()) {
2236 while (endpar && endpar->getDepth()) {
2237 endpar = endpar->next();
2240 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2244 removeRow(old_cursor.row());
2245 if (ownerParagraph() == old_cursor.par()) {
2246 ownerParagraph(ownerParagraph()->next());
2249 delete old_cursor.par();
2251 /* Breakagain the next par. Needed because of
2252 * the parindent that can occur or dissappear.
2253 * The next row can change its height, if
2254 * there is another layout before */
2255 if (refresh_row->next()) {
2256 breakAgain(bview, refresh_row->next());
2257 updateCounters(bview);
2259 setHeightOfRow(bview, refresh_row);
2261 refresh_row = old_cursor.row()->next();
2262 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2265 cursor = old_cursor; // that undo can restore the right cursor position
2266 Paragraph * endpar = old_cursor.par()->next();
2267 if (endpar && endpar->getDepth()) {
2268 while (endpar && endpar->getDepth()) {
2269 endpar = endpar->next();
2272 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2276 removeRow(old_cursor.row());
2278 if (ownerParagraph() == old_cursor.par()) {
2279 ownerParagraph(ownerParagraph()->next());
2282 delete old_cursor.par();
2284 /* Breakagain the next par. Needed because of
2285 the parindent that can occur or dissappear.
2286 The next row can change its height, if
2287 there is another layout before */
2289 breakAgain(bview, refresh_row);
2290 updateCounters(bview);
2295 setCursorIntern(bview, cursor.par(), cursor.pos());
2297 if (selection.cursor.par() == old_cursor.par()
2298 && selection.cursor.pos() == old_cursor.pos()) {
2299 // correct selection
2300 selection.cursor = cursor;
2304 if (old_cursor.par()->stripLeadingSpaces()) {
2305 redoParagraphs(bview, old_cursor,
2306 old_cursor.par()->next());
2308 setCursorIntern(bview, cursor.par(), cursor.pos());
2309 selection.cursor = cursor;
2316 Paragraph * LyXText::ownerParagraph() const
2319 return inset_owner->paragraph();
2321 return &*(bv_owner->buffer()->paragraphs.begin());
2325 void LyXText::ownerParagraph(Paragraph * p) const
2328 inset_owner->paragraph(p);
2330 bv_owner->buffer()->paragraphs.set(p);
2335 void LyXText::ownerParagraph(int id, Paragraph * p) const
2337 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2338 if (op && op->inInset()) {
2339 static_cast<InsetText *>(op->inInset())->paragraph(p);
2346 LyXText::text_status LyXText::status() const
2352 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2354 LyXText * t = bview->text;
2356 // We should only go up with refreshing code so this means that if
2357 // we have a MORE refresh we should never set it to LITTLE if we still
2358 // didn't handle it (and then it will be UNCHANGED. Now as long as
2359 // we stay inside one LyXText this may work but we need to tell the
2360 // outermost LyXText that it should REALLY draw us if there is some
2361 // change in a Inset::LyXText. So you see that when we are inside a
2362 // inset's LyXText we give the LITTLE to the outermost LyXText to
2363 // tell'em that it should redraw the actual row (where the inset
2364 // resides! Capito?!
2366 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2368 if (inset_owner && st != UNCHANGED) {
2369 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2370 if (!t->refresh_row) {
2371 t->refresh_row = t->cursor.row();
2372 t->refresh_y = t->cursor.y() -
2373 t->cursor.row()->baseline();
2380 bool LyXText::isTopLevel() const
2382 /// only the top-level lyxtext has a non-null bv owner
2387 int defaultRowHeight()
2389 LyXFont const font(LyXFont::ALL_SANE);
2390 return int(font_metrics::maxAscent(font)
2391 + font_metrics::maxDescent(font) * 1.5);