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 < beginningOfMainBody(buf, par)) {
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 < beginningOfMainBody(buf, par)) {
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 < beginningOfMainBody(buf, par))
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 < beginningOfMainBody(buf, par))
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() < beginningOfMainBody(bview->buffer(),
645 layoutfont = getLabelFont(bview->buffer(),
648 layoutfont = getLayoutFont(bview->buffer(),
651 // Update current font
652 real_current_font.update(font,
653 bview->buffer()->params.language,
656 // Reduce to implicit settings
657 current_font = real_current_font;
658 current_font.reduce(layoutfont);
659 // And resolve it completely
660 real_current_font.realize(layoutfont);
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
670 setUndo(bview, Undo::EDIT,
671 selection.start.par(), selection.end.par()->next());
673 cursor = selection.start;
674 while (cursor.par() != selection.end.par() ||
675 cursor.pos() < selection.end.pos())
677 if (cursor.pos() < cursor.par()->size()) {
678 // an open footnote should behave like a closed one
679 setCharFont(bview, cursor.par(), cursor.pos(),
681 cursor.pos(cursor.pos() + 1);
684 cursor.par(cursor.par()->next());
689 redoParagraphs(bview, selection.start, selection.end.par()->next());
691 // we have to reset the selection, because the
692 // geometry could have changed, but we keep
693 // it for user convenience
694 setCursor(bview, selection.start.par(), selection.start.pos());
695 selection.cursor = cursor;
696 setCursor(bview, selection.end.par(), selection.end.pos());
698 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
699 tmpcursor.boundary());
703 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
705 Row * tmprow = cur.row();
706 int y = cur.y() - tmprow->baseline();
708 setHeightOfRow(bview, tmprow);
710 while (tmprow->previous()
711 && tmprow->previous()->par() == tmprow->par()) {
712 tmprow = tmprow->previous();
713 y -= tmprow->height();
714 setHeightOfRow(bview, tmprow);
717 // we can set the refreshing parameters now
718 status(bview, LyXText::NEED_MORE_REFRESH);
720 refresh_row = tmprow;
721 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
725 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
727 Row * tmprow = cur.row();
729 int y = cur.y() - tmprow->baseline();
730 setHeightOfRow(bview, tmprow);
732 while (tmprow->previous()
733 && tmprow->previous()->par() == tmprow->par()) {
734 tmprow = tmprow->previous();
735 y -= tmprow->height();
738 // we can set the refreshing parameters now
739 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
741 refresh_row = tmprow;
743 status(bview, LyXText::NEED_MORE_REFRESH);
744 setCursor(bview, cur.par(), cur.pos());
748 // deletes and inserts again all paragaphs between the cursor
749 // and the specified par
750 // This function is needed after SetLayout and SetFont etc.
751 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
752 Paragraph const * endpar) const
755 Paragraph * tmppar = 0;
756 Paragraph * first_phys_par = 0;
758 Row * tmprow = cur.row();
760 int y = cur.y() - tmprow->baseline();
762 if (!tmprow->previous()) {
763 // a trick/hack for UNDO
764 // This is needed because in an UNDO/REDO we could have changed
765 // the ownerParagrah() so the paragraph inside the row is NOT
766 // my really first par anymore. Got it Lars ;) (Jug 20011206)
767 first_phys_par = ownerParagraph();
769 first_phys_par = tmprow->par();
770 while (tmprow->previous()
771 && tmprow->previous()->par() == first_phys_par)
773 tmprow = tmprow->previous();
774 y -= tmprow->height();
778 // we can set the refreshing parameters now
779 status(bview, LyXText::NEED_MORE_REFRESH);
781 refresh_row = tmprow->previous(); /* the real refresh row will
782 be deleted, so I store
786 tmppar = tmprow->next()->par();
789 while (tmprow->next() && tmppar != endpar) {
790 removeRow(tmprow->next());
791 if (tmprow->next()) {
792 tmppar = tmprow->next()->par();
798 // remove the first one
799 tmprow2 = tmprow; /* this is because tmprow->previous()
801 tmprow = tmprow->previous();
804 tmppar = first_phys_par;
808 insertParagraph(bview, tmppar, tmprow);
812 while (tmprow->next()
813 && tmprow->next()->par() == tmppar) {
814 tmprow = tmprow->next();
816 tmppar = tmppar->next();
818 } while (tmppar && tmppar != endpar);
820 // this is because of layout changes
822 refresh_y -= refresh_row->height();
823 setHeightOfRow(bview, refresh_row);
825 refresh_row = firstrow;
827 setHeightOfRow(bview, refresh_row);
830 if (tmprow && tmprow->next())
831 setHeightOfRow(bview, tmprow->next());
832 updateCounters(bview);
836 void LyXText::fullRebreak(BufferView * bview)
842 if (need_break_row) {
843 breakAgain(bview, need_break_row);
850 // important for the screen
853 // the cursor set functions have a special mechanism. When they
854 // realize, that you left an empty paragraph, they will delete it.
855 // They also delete the corresponding row
857 // need the selection cursor:
858 void LyXText::setSelection(BufferView * bview)
860 bool const lsel = selection.set();
862 if (!selection.set()) {
863 last_sel_cursor = selection.cursor;
864 selection.start = selection.cursor;
865 selection.end = selection.cursor;
870 // first the toggling area
871 if (cursor.y() < last_sel_cursor.y()
872 || (cursor.y() == last_sel_cursor.y()
873 && cursor.x() < last_sel_cursor.x())) {
874 toggle_end_cursor = last_sel_cursor;
875 toggle_cursor = cursor;
877 toggle_end_cursor = cursor;
878 toggle_cursor = last_sel_cursor;
881 last_sel_cursor = cursor;
883 // and now the whole selection
885 if (selection.cursor.par() == cursor.par())
886 if (selection.cursor.pos() < cursor.pos()) {
887 selection.end = cursor;
888 selection.start = selection.cursor;
890 selection.end = selection.cursor;
891 selection.start = cursor;
893 else if (selection.cursor.y() < cursor.y() ||
894 (selection.cursor.y() == cursor.y()
895 && selection.cursor.x() < cursor.x())) {
896 selection.end = cursor;
897 selection.start = selection.cursor;
900 selection.end = selection.cursor;
901 selection.start = cursor;
904 // a selection with no contents is not a selection
905 if (selection.start.par() == selection.end.par() &&
906 selection.start.pos() == selection.end.pos())
907 selection.set(false);
909 if (inset_owner && (selection.set() || lsel))
910 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
914 string const LyXText::selectionAsString(Buffer const * buffer,
917 if (!selection.set()) return string();
919 // should be const ...
920 Paragraph * startpar(selection.start.par());
921 Paragraph * endpar(selection.end.par());
922 pos_type const startpos(selection.start.pos());
923 pos_type const endpos(selection.end.pos());
925 if (startpar == endpar) {
926 return startpar->asString(buffer, startpos, endpos, label);
931 // First paragraph in selection
932 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
934 // The paragraphs in between (if any)
935 LyXCursor tmpcur(selection.start);
936 tmpcur.par(tmpcur.par()->next());
937 while (tmpcur.par() != endpar) {
938 result += tmpcur.par()->asString(buffer, 0,
939 tmpcur.par()->size(),
941 tmpcur.par(tmpcur.par()->next());
944 // Last paragraph in selection
945 result += endpar->asString(buffer, 0, endpos, label);
951 void LyXText::clearSelection() const
953 selection.set(false);
954 selection.mark(false);
955 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
956 // reset this in the bv_owner!
957 if (bv_owner && bv_owner->text)
958 bv_owner->text->xsel_cache.set(false);
962 void LyXText::cursorHome(BufferView * bview) const
964 setCursor(bview, cursor.par(), cursor.row()->pos());
968 void LyXText::cursorEnd(BufferView * bview) const
970 if (!cursor.row()->next()
971 || cursor.row()->next()->par() != cursor.row()->par()) {
972 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
974 if (!cursor.par()->empty() &&
975 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
976 || cursor.par()->isNewline(rowLast(cursor.row())))) {
977 setCursor(bview, cursor.par(), rowLast(cursor.row()));
979 setCursor(bview,cursor.par(),
980 rowLast(cursor.row()) + 1);
986 void LyXText::cursorTop(BufferView * bview) const
988 while (cursor.par()->previous())
989 cursor.par(cursor.par()->previous());
990 setCursor(bview, cursor.par(), 0);
994 void LyXText::cursorBottom(BufferView * bview) const
996 while (cursor.par()->next())
997 cursor.par(cursor.par()->next());
998 setCursor(bview, cursor.par(), cursor.par()->size());
1002 void LyXText::toggleFree(BufferView * bview,
1003 LyXFont const & font, bool toggleall)
1005 // If the mask is completely neutral, tell user
1006 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1007 // Could only happen with user style
1008 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1012 // Try implicit word selection
1013 // If there is a change in the language the implicit word selection
1015 LyXCursor resetCursor = cursor;
1016 bool implicitSelection = (font.language() == ignore_language
1017 && font.number() == LyXFont::IGNORE)
1018 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1021 setFont(bview, font, toggleall);
1023 // Implicit selections are cleared afterwards
1024 //and cursor is set to the original position.
1025 if (implicitSelection) {
1027 cursor = resetCursor;
1028 setCursor(bview, cursor.par(), cursor.pos());
1029 selection.cursor = cursor;
1032 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1036 string LyXText::getStringToIndex(BufferView * bview)
1038 // Try implicit word selection
1039 // If there is a change in the language the implicit word selection
1041 LyXCursor const reset_cursor = cursor;
1042 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1045 if (!selection.set())
1046 bview->owner()->message(_("Nothing to index!"));
1047 else if (selection.start.par() != selection.end.par())
1048 bview->owner()->message(_("Cannot index more than one paragraph!"));
1050 idxstring = selectionAsString(bview->buffer(), false);
1052 // Reset cursors to their original position.
1053 cursor = reset_cursor;
1054 setCursor(bview, cursor.par(), cursor.pos());
1055 selection.cursor = cursor;
1057 // Clear the implicit selection.
1058 if (implicitSelection)
1065 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1066 Paragraph const * par) const
1068 if (par->layout()->labeltype != LABEL_MANUAL)
1071 return par->beginningOfMainBody();
1075 // the DTP switches for paragraphs. LyX will store them in the first
1076 // physicla paragraph. When a paragraph is broken, the top settings rest,
1077 // the bottom settings are given to the new one. So I can make shure,
1078 // they do not duplicate themself and you cannnot make dirty things with
1081 void LyXText::setParagraph(BufferView * bview,
1082 bool line_top, bool line_bottom,
1083 bool pagebreak_top, bool pagebreak_bottom,
1084 VSpace const & space_top,
1085 VSpace const & space_bottom,
1086 Spacing const & spacing,
1088 string labelwidthstring,
1091 LyXCursor tmpcursor = cursor;
1092 if (!selection.set()) {
1093 selection.start = cursor;
1094 selection.end = cursor;
1097 // make sure that the depth behind the selection are restored, too
1098 Paragraph * endpar = selection.end.par()->next();
1099 Paragraph * undoendpar = endpar;
1101 if (endpar && endpar->getDepth()) {
1102 while (endpar && endpar->getDepth()) {
1103 endpar = endpar->next();
1104 undoendpar = endpar;
1108 // because of parindents etc.
1109 endpar = endpar->next();
1112 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1115 Paragraph * tmppar = selection.end.par();
1117 while (tmppar != selection.start.par()->previous()) {
1118 setCursor(bview, tmppar, 0);
1119 status(bview, LyXText::NEED_MORE_REFRESH);
1120 refresh_row = cursor.row();
1121 refresh_y = cursor.y() - cursor.row()->baseline();
1122 cursor.par()->params().lineTop(line_top);
1123 cursor.par()->params().lineBottom(line_bottom);
1124 cursor.par()->params().pagebreakTop(pagebreak_top);
1125 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1126 cursor.par()->params().spaceTop(space_top);
1127 cursor.par()->params().spaceBottom(space_bottom);
1128 cursor.par()->params().spacing(spacing);
1129 // does the layout allow the new alignment?
1130 LyXLayout_ptr const & layout = cursor.par()->layout();
1132 if (align == LYX_ALIGN_LAYOUT)
1133 align = layout->align;
1134 if (align & layout->alignpossible) {
1135 if (align == layout->align)
1136 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1138 cursor.par()->params().align(align);
1140 cursor.par()->setLabelWidthString(labelwidthstring);
1141 cursor.par()->params().noindent(noindent);
1142 tmppar = cursor.par()->previous();
1145 redoParagraphs(bview, selection.start, endpar);
1148 setCursor(bview, selection.start.par(), selection.start.pos());
1149 selection.cursor = cursor;
1150 setCursor(bview, selection.end.par(), selection.end.pos());
1151 setSelection(bview);
1152 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1154 bview->updateInset(inset_owner, true);
1158 // set the counter of a paragraph. This includes the labels
1159 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1161 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1162 LyXLayout_ptr const & layout = par->layout();
1164 if (par->previous()) {
1166 par->params().appendix(par->previous()->params().appendix());
1167 if (!par->params().appendix() && par->params().startOfAppendix()) {
1168 par->params().appendix(true);
1169 textclass.counters().reset();
1171 par->enumdepth = par->previous()->enumdepth;
1172 par->itemdepth = par->previous()->itemdepth;
1174 par->params().appendix(par->params().startOfAppendix());
1179 /* Maybe we have to increment the enumeration depth.
1180 * BUT, enumeration in a footnote is considered in isolation from its
1181 * surrounding paragraph so don't increment if this is the
1182 * first line of the footnote
1183 * AND, bibliographies can't have their depth changed ie. they
1184 * are always of depth 0
1187 && par->previous()->getDepth() < par->getDepth()
1188 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1189 && par->enumdepth < 3
1190 && layout->labeltype != LABEL_BIBLIO) {
1194 // Maybe we have to decrement the enumeration depth, see note above
1196 && par->previous()->getDepth() > par->getDepth()
1197 && layout->labeltype != LABEL_BIBLIO) {
1198 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1201 if (!par->params().labelString().empty()) {
1202 par->params().labelString(string());
1205 if (layout->margintype == MARGIN_MANUAL) {
1206 if (par->params().labelWidthString().empty()) {
1207 par->setLabelWidthString(layout->labelstring());
1210 par->setLabelWidthString(string());
1213 // is it a layout that has an automatic label?
1214 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1215 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1219 if (i >= 0 && i <= buf->params.secnumdepth) {
1223 textclass.counters().step(layout->latexname());
1225 // Is there a label? Useful for Chapter layout
1226 if (!par->params().appendix()) {
1227 s << layout->labelstring();
1229 s << layout->labelstring_appendix();
1232 // Use of an integer is here less than elegant. For now.
1233 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1234 if (!par->params().appendix()) {
1235 numbertype = "sectioning";
1237 numbertype = "appendix";
1238 if (par->isRightToLeftPar(buf->params))
1239 langtype = "hebrew";
1244 s << textclass.counters()
1245 .numberLabel(layout->latexname(),
1246 numbertype, langtype, head);
1248 par->params().labelString(STRCONV(s.str()));
1250 // reset enum counters
1251 textclass.counters().reset("enum");
1252 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1253 textclass.counters().reset("enum");
1254 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1256 // Yes I know this is a really, really! bad solution
1258 string enumcounter("enum");
1260 switch (par->enumdepth) {
1269 enumcounter += "iv";
1272 // not a valid enumdepth...
1276 textclass.counters().step(enumcounter);
1278 s << textclass.counters()
1279 .numberLabel(enumcounter, "enumeration");
1280 par->params().labelString(STRCONV(s.str()));
1282 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1283 textclass.counters().step("bibitem");
1284 int number = textclass.counters().value("bibitem");
1286 InsetCommandParams p("bibitem");
1287 par->bibkey = new InsetBibKey(p);
1289 par->bibkey->setCounter(number);
1290 par->params().labelString(layout->labelstring());
1292 // In biblio should't be following counters but...
1294 string s = layout->labelstring();
1296 // the caption hack:
1297 if (layout->labeltype == LABEL_SENSITIVE) {
1298 Paragraph * tmppar = par;
1301 while (tmppar && tmppar->inInset()
1302 // the single '=' is intended below
1303 && (in = tmppar->inInset()->owner())) {
1304 if (in->lyxCode() == Inset::FLOAT_CODE ||
1305 in->lyxCode() == Inset::WRAP_CODE) {
1309 tmppar = in->parOwner();
1315 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1317 textclass.counters().step(fl.type());
1319 // Doesn't work... yet.
1320 #warning use boost.format
1321 #if USE_BOOST_FORMAT
1322 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1323 // s << boost::format(_("%1$s %1$d:")
1325 // % buf->counters().value(fl.name());
1328 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1329 o << fl.name() << " #:";
1330 s = STRCONV(o.str());
1333 // par->SetLayout(0);
1334 // s = layout->labelstring;
1335 s = _("Senseless: ");
1338 par->params().labelString(s);
1340 // reset the enumeration counter. They are always reset
1341 // when there is any other layout between
1342 // Just fall-through between the cases so that all
1343 // enum counters deeper than enumdepth is also reset.
1344 switch (par->enumdepth) {
1346 textclass.counters().reset("enumi");
1348 textclass.counters().reset("enumii");
1350 textclass.counters().reset("enumiii");
1352 textclass.counters().reset("enumiv");
1358 // Updates all counters. Paragraphs with changed label string will be rebroken
1359 void LyXText::updateCounters(BufferView * bview) const
1361 Row * row = firstrow;
1362 Paragraph * par = row->par();
1364 // CHECK if this is really needed. (Lgb)
1365 bview->buffer()->params.getLyXTextClass().counters().reset();
1368 while (row->par() != par)
1371 string const oldLabel = par->params().labelString();
1373 // setCounter can potentially change the labelString.
1374 setCounter(bview->buffer(), par);
1376 string const & newLabel = par->params().labelString();
1378 if (oldLabel.empty() && !newLabel.empty()) {
1379 removeParagraph(row);
1380 appendParagraph(bview, row);
1388 void LyXText::insertInset(BufferView * bview, Inset * inset)
1390 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1392 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1394 cursor.par()->insertInset(cursor.pos(), inset);
1395 // Just to rebreak and refresh correctly.
1396 // The character will not be inserted a second time
1397 insertChar(bview, Paragraph::META_INSET);
1398 // If we enter a highly editable inset the cursor should be to before
1399 // the inset. This couldn't happen before as Undo was not handled inside
1400 // inset now after the Undo LyX tries to call inset->Edit(...) again
1401 // and cannot do this as the cursor is behind the inset and GetInset
1402 // does not return the inset!
1403 if (isHighlyEditableInset(inset)) {
1404 cursorLeft(bview, true);
1410 void LyXText::copyEnvironmentType()
1412 copylayouttype = cursor.par()->layout()->name();
1416 void LyXText::pasteEnvironmentType(BufferView * bview)
1418 // do nothing if there has been no previous copyEnvironmentType()
1419 if (!copylayouttype.empty())
1420 setLayout(bview, copylayouttype);
1424 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1426 // Stuff what we got on the clipboard. Even if there is no selection.
1428 // There is a problem with having the stuffing here in that the
1429 // larger the selection the slower LyX will get. This can be
1430 // solved by running the line below only when the selection has
1431 // finished. The solution used currently just works, to make it
1432 // faster we need to be more clever and probably also have more
1433 // calls to stuffClipboard. (Lgb)
1434 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1436 // This doesn't make sense, if there is no selection
1437 if (!selection.set())
1440 // OK, we have a selection. This is always between selection.start
1441 // and selection.end
1443 // make sure that the depth behind the selection are restored, too
1444 Paragraph * endpar = selection.end.par()->next();
1445 Paragraph * undoendpar = endpar;
1447 if (endpar && endpar->getDepth()) {
1448 while (endpar && endpar->getDepth()) {
1449 endpar = endpar->next();
1450 undoendpar = endpar;
1452 } else if (endpar) {
1453 endpar = endpar->next(); // because of parindents etc.
1456 setUndo(bview, Undo::DELETE,
1457 selection.start.par(), undoendpar);
1459 // there are two cases: cut only within one paragraph or
1460 // more than one paragraph
1461 if (selection.start.par() == selection.end.par()) {
1462 // only within one paragraph
1463 endpar = selection.end.par();
1464 int pos = selection.end.pos();
1465 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1466 selection.start.pos(), pos,
1467 bview->buffer()->params.textclass,
1469 selection.end.pos(pos);
1471 endpar = selection.end.par();
1472 int pos = selection.end.pos();
1473 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1474 selection.start.pos(), pos,
1475 bview->buffer()->params.textclass,
1478 selection.end.par(endpar);
1479 selection.end.pos(pos);
1480 cursor.pos(selection.end.pos());
1482 endpar = endpar->next();
1484 // sometimes necessary
1486 selection.start.par()->stripLeadingSpaces();
1488 redoParagraphs(bview, selection.start, endpar);
1490 // cutSelection can invalidate the cursor so we need to set
1492 // we prefer the end for when tracking changes
1493 cursor = selection.end;
1495 // need a valid cursor. (Lgb)
1498 setCursor(bview, cursor.par(), cursor.pos());
1499 selection.cursor = cursor;
1500 updateCounters(bview);
1504 void LyXText::copySelection(BufferView * bview)
1506 // stuff the selection onto the X clipboard, from an explicit copy request
1507 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1509 // this doesnt make sense, if there is no selection
1510 if (!selection.set())
1513 // ok we have a selection. This is always between selection.start
1514 // and sel_end cursor
1516 // copy behind a space if there is one
1517 while (selection.start.par()->size() > selection.start.pos()
1518 && selection.start.par()->isLineSeparator(selection.start.pos())
1519 && (selection.start.par() != selection.end.par()
1520 || selection.start.pos() < selection.end.pos()))
1521 selection.start.pos(selection.start.pos() + 1);
1523 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1524 selection.start.pos(), selection.end.pos(),
1525 bview->buffer()->params.textclass);
1529 void LyXText::pasteSelection(BufferView * bview)
1531 // this does not make sense, if there is nothing to paste
1532 if (!CutAndPaste::checkPastePossible(cursor.par()))
1535 setUndo(bview, Undo::INSERT,
1536 cursor.par(), cursor.par()->next());
1539 Paragraph * actpar = cursor.par();
1540 int pos = cursor.pos();
1542 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1543 bview->buffer()->params.textclass);
1545 redoParagraphs(bview, cursor, endpar);
1547 setCursor(bview, cursor.par(), cursor.pos());
1550 selection.cursor = cursor;
1551 setCursor(bview, actpar, pos);
1552 setSelection(bview);
1553 updateCounters(bview);
1557 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1562 selection.cursor = cursor;
1565 setSelection(bview);
1569 // simple replacing. The font of the first selected character is used
1570 void LyXText::replaceSelectionWithString(BufferView * bview,
1573 setCursorParUndo(bview);
1576 if (!selection.set()) { // create a dummy selection
1577 selection.end = cursor;
1578 selection.start = cursor;
1581 // Get font setting before we cut
1582 pos_type pos = selection.end.pos();
1583 LyXFont const font = selection.start.par()
1584 ->getFontSettings(bview->buffer()->params,
1585 selection.start.pos());
1587 // Insert the new string
1588 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1589 selection.end.par()->insertChar(pos, (*cit), font);
1593 // Cut the selection
1594 cutSelection(bview, true, false);
1600 // needed to insert the selection
1601 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1603 Paragraph * par = cursor.par();
1604 pos_type pos = cursor.pos();
1605 Paragraph * endpar = cursor.par()->next();
1607 setCursorParUndo(bview);
1609 // only to be sure, should not be neccessary
1612 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1614 redoParagraphs(bview, cursor, endpar);
1615 setCursor(bview, cursor.par(), cursor.pos());
1616 selection.cursor = cursor;
1617 setCursor(bview, par, pos);
1618 setSelection(bview);
1622 // turns double-CR to single CR, others where converted into one
1623 // blank. Then InsertStringAsLines is called
1624 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1626 string linestr(str);
1627 bool newline_inserted = false;
1628 for (string::size_type i = 0; i < linestr.length(); ++i) {
1629 if (linestr[i] == '\n') {
1630 if (newline_inserted) {
1631 // we know that \r will be ignored by
1632 // InsertStringA. Of course, it is a dirty
1633 // trick, but it works...
1634 linestr[i - 1] = '\r';
1638 newline_inserted = true;
1640 } else if (IsPrintable(linestr[i])) {
1641 newline_inserted = false;
1644 insertStringAsLines(bview, linestr);
1648 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1651 LyXCursor tmpcursor;
1655 Row * row = getRow(par, pos, y);
1657 // is there a break one row above
1658 if (row->previous() && row->previous()->par() == row->par()) {
1659 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1660 if (z >= row->pos()) {
1661 // set the dimensions of the row above
1662 y -= row->previous()->height();
1664 refresh_row = row->previous();
1665 status(bview, LyXText::NEED_MORE_REFRESH);
1667 breakAgain(bview, row->previous());
1669 // set the cursor again. Otherwise
1670 // dangling pointers are possible
1671 setCursor(bview, cursor.par(), cursor.pos(),
1672 false, cursor.boundary());
1673 selection.cursor = cursor;
1678 int const tmpheight = row->height();
1679 pos_type const tmplast = rowLast(row);
1683 breakAgain(bview, row);
1684 if (row->height() == tmpheight && rowLast(row) == tmplast)
1685 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1687 status(bview, LyXText::NEED_MORE_REFRESH);
1689 // check the special right address boxes
1690 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1697 redoDrawingOfParagraph(bview, tmpcursor);
1700 // set the cursor again. Otherwise dangling pointers are possible
1701 // also set the selection
1703 if (selection.set()) {
1705 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1706 false, selection.cursor.boundary());
1707 selection.cursor = cursor;
1708 setCursorIntern(bview, selection.start.par(),
1709 selection.start.pos(),
1710 false, selection.start.boundary());
1711 selection.start = cursor;
1712 setCursorIntern(bview, selection.end.par(),
1713 selection.end.pos(),
1714 false, selection.end.boundary());
1715 selection.end = cursor;
1716 setCursorIntern(bview, last_sel_cursor.par(),
1717 last_sel_cursor.pos(),
1718 false, last_sel_cursor.boundary());
1719 last_sel_cursor = cursor;
1722 setCursorIntern(bview, cursor.par(), cursor.pos(),
1723 false, cursor.boundary());
1727 // returns false if inset wasn't found
1728 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1730 // first check the current paragraph
1731 int pos = cursor.par()->getPositionOfInset(inset);
1733 checkParagraph(bview, cursor.par(), pos);
1737 // check every paragraph
1739 Paragraph * par = ownerParagraph();
1741 pos = par->getPositionOfInset(inset);
1743 checkParagraph(bview, par, pos);
1753 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1755 bool setfont, bool boundary) const
1757 LyXCursor old_cursor = cursor;
1758 setCursorIntern(bview, par, pos, setfont, boundary);
1759 return deleteEmptyParagraphMechanism(bview, old_cursor);
1763 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1764 pos_type pos, bool boundary) const
1771 cur.boundary(boundary);
1773 // get the cursor y position in text
1775 Row * row = getRow(par, pos, y);
1776 Row * old_row = row;
1778 // if we are before the first char of this row and are still in the
1779 // same paragraph and there is a previous row then put the cursor on
1780 // the end of the previous row
1781 cur.iy(y + row->baseline());
1783 if (row->previous() && pos &&
1784 row->previous()->par() == row->par() &&
1785 par->getChar(pos) == Paragraph::META_INSET &&
1786 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1788 row = row->previous();
1793 // y is now the beginning of the cursor row
1794 y += row->baseline();
1795 // y is now the cursor baseline
1798 pos_type last = rowLastPrintable(old_row);
1800 // None of these should happen, but we're scaredy-cats
1801 if (pos > par->size()) {
1804 } else if (pos > last + 1) {
1805 // This shouldn't happen.
1808 } else if (pos < row->pos()) {
1813 // now get the cursors x position
1814 float x = getCursorX(bview, row, pos, last, boundary);
1817 if (old_row != row) {
1818 x = getCursorX(bview, old_row, pos, last, boundary);
1825 float LyXText::getCursorX(BufferView * bview, Row * row,
1826 pos_type pos, pos_type last, bool boundary) const
1828 pos_type cursor_vpos = 0;
1830 float fill_separator;
1832 float fill_label_hfill;
1833 // This call HAS to be here because of the BidiTables!!!
1834 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1837 if (last < row->pos())
1838 cursor_vpos = row->pos();
1839 else if (pos > last && !boundary)
1840 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1841 ? row->pos() : last + 1;
1842 else if (pos > row->pos() &&
1843 (pos > last || boundary))
1844 /// Place cursor after char at (logical) position pos - 1
1845 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1846 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1848 /// Place cursor before char at (logical) position pos
1849 cursor_vpos = (bidi_level(pos) % 2 == 0)
1850 ? log2vis(pos) : log2vis(pos) + 1;
1852 pos_type main_body =
1853 beginningOfMainBody(bview->buffer(), row->par());
1854 if ((main_body > 0) &&
1855 ((main_body-1 > last) ||
1856 !row->par()->isLineSeparator(main_body-1)))
1859 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1860 pos_type pos = vis2log(vpos);
1861 if (main_body > 0 && pos == main_body - 1) {
1862 x += fill_label_hfill +
1863 font_metrics::width(
1864 row->par()->layout()->labelsep,
1865 getLabelFont(bview->buffer(),
1867 if (row->par()->isLineSeparator(main_body - 1))
1868 x -= singleWidth(bview,
1869 row->par(), main_body - 1);
1871 if (hfillExpansion(bview->buffer(), row, pos)) {
1872 x += singleWidth(bview, row->par(), pos);
1873 if (pos >= main_body)
1876 x += fill_label_hfill;
1877 } else if (row->par()->isSeparator(pos)) {
1878 x += singleWidth(bview, row->par(), pos);
1879 if (pos >= main_body)
1880 x += fill_separator;
1882 x += singleWidth(bview, row->par(), pos);
1888 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1889 pos_type pos, bool setfont, bool boundary) const
1891 InsetText * it = static_cast<InsetText *>(par->inInset());
1893 if (it != inset_owner) {
1894 lyxerr[Debug::INSETS] << "InsetText is " << it
1896 << "inset_owner is "
1897 << inset_owner << endl;
1898 #ifdef WITH_WARNINGS
1899 #warning I believe this code is wrong. (Lgb)
1900 #warning Jürgen, have a look at this. (Lgb)
1901 #warning Hmmm, I guess you are right but we
1902 #warning should verify when this is needed
1904 // Jürgen, would you like to have a look?
1905 // I guess we need to move the outer cursor
1906 // and open and lock the inset (bla bla bla)
1907 // stuff I don't know... so can you have a look?
1909 // I moved the lyxerr stuff in here so we can see if
1910 // this is actually really needed and where!
1912 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1917 setCursor(bview, cursor, par, pos, boundary);
1919 setCurrentFont(bview);
1923 void LyXText::setCurrentFont(BufferView * bview) const
1925 pos_type pos = cursor.pos();
1926 if (cursor.boundary() && pos > 0)
1930 if (pos == cursor.par()->size())
1932 else // potentional bug... BUG (Lgb)
1933 if (cursor.par()->isSeparator(pos)) {
1934 if (pos > cursor.row()->pos() &&
1935 bidi_level(pos) % 2 ==
1936 bidi_level(pos - 1) % 2)
1938 else if (pos + 1 < cursor.par()->size())
1944 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1945 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1947 if (cursor.pos() == cursor.par()->size() &&
1948 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1949 !cursor.boundary()) {
1950 Language const * lang =
1951 cursor.par()->getParLanguage(bview->buffer()->params);
1952 current_font.setLanguage(lang);
1953 current_font.setNumber(LyXFont::OFF);
1954 real_current_font.setLanguage(lang);
1955 real_current_font.setNumber(LyXFont::OFF);
1960 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1962 LyXCursor old_cursor = cursor;
1964 setCursorFromCoordinates(bview, cursor, x, y);
1965 setCurrentFont(bview);
1966 deleteEmptyParagraphMechanism(bview, old_cursor);
1973 * return true if the cursor given is at the end of a row,
1974 * and the next row is filled by an inset that spans an entire
1977 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1980 Row const & next = *row.next();
1982 if (next.pos() != cur.pos() || next.par() != cur.par())
1984 if (!cur.par()->isInset(cur.pos()))
1986 Inset const * inset = cur.par()->getInset(cur.pos());
1987 if (inset->needFullRow() || inset->display())
1994 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1997 // Get the row first.
1999 Row * row = getRowNearY(y);
2001 pos_type const column = getColumnNearX(bview, row, x, bound);
2002 cur.par(row->par());
2003 cur.pos(row->pos() + column);
2005 cur.y(y + row->baseline());
2008 if (beforeFullRowInset(*row, cur)) {
2009 pos_type last = rowLastPrintable(row);
2010 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2012 cur.iy(y + row->height() + row->next()->baseline());
2013 cur.irow(row->next());
2019 cur.boundary(bound);
2023 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2025 if (cursor.pos() > 0) {
2026 bool boundary = cursor.boundary();
2027 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2028 if (!internal && !boundary &&
2029 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2030 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2031 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2032 Paragraph * par = cursor.par()->previous();
2033 setCursor(bview, par, par->size());
2038 void LyXText::cursorRight(BufferView * bview, bool internal) const
2040 if (!internal && cursor.boundary() &&
2041 !cursor.par()->isNewline(cursor.pos()))
2042 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2043 else if (cursor.pos() < cursor.par()->size()) {
2044 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2046 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2047 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2048 } else if (cursor.par()->next())
2049 setCursor(bview, cursor.par()->next(), 0);
2053 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2056 int x = cursor.x_fix();
2057 int y = cursor.y() - cursor.row()->baseline() - 1;
2058 setCursorFromCoordinates(bview, x, y);
2060 int y1 = cursor.iy() - first_y;
2063 Inset * inset_hit = checkInsetHit(bview, x, y1);
2064 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2065 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2069 setCursorFromCoordinates(bview, cursor.x_fix(),
2070 cursor.y() - cursor.row()->baseline() - 1);
2075 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2078 int x = cursor.x_fix();
2079 int y = cursor.y() - cursor.row()->baseline() +
2080 cursor.row()->height() + 1;
2081 setCursorFromCoordinates(bview, x, y);
2082 if (!selecting && cursor.row() == cursor.irow()) {
2083 int y1 = cursor.iy() - first_y;
2086 Inset * inset_hit = checkInsetHit(bview, x, y1);
2087 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2088 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2092 setCursorFromCoordinates(bview, cursor.x_fix(),
2093 cursor.y() - cursor.row()->baseline()
2094 + cursor.row()->height() + 1);
2099 void LyXText::cursorUpParagraph(BufferView * bview) const
2101 if (cursor.pos() > 0) {
2102 setCursor(bview, cursor.par(), 0);
2104 else if (cursor.par()->previous()) {
2105 setCursor(bview, cursor.par()->previous(), 0);
2110 void LyXText::cursorDownParagraph(BufferView * bview) const
2112 if (cursor.par()->next()) {
2113 setCursor(bview, cursor.par()->next(), 0);
2115 setCursor(bview, cursor.par(), cursor.par()->size());
2119 // fix the cursor `cur' after a characters has been deleted at `where'
2120 // position. Called by deleteEmptyParagraphMechanism
2121 void LyXText::fixCursorAfterDelete(BufferView * bview,
2123 LyXCursor const & where) const
2125 // if cursor is not in the paragraph where the delete occured,
2127 if (cur.par() != where.par())
2130 // if cursor position is after the place where the delete occured,
2132 if (cur.pos() > where.pos())
2133 cur.pos(cur.pos()-1);
2135 // check also if we don't want to set the cursor on a spot behind the
2136 // pagragraph because we erased the last character.
2137 if (cur.pos() > cur.par()->size())
2138 cur.pos(cur.par()->size());
2140 // recompute row et al. for this cursor
2141 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2145 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2146 LyXCursor const & old_cursor) const
2148 // Would be wrong to delete anything if we have a selection.
2149 if (selection.set())
2152 // We allow all kinds of "mumbo-jumbo" when freespacing.
2153 if (old_cursor.par()->layout()->free_spacing
2154 || old_cursor.par()->isFreeSpacing()) {
2158 /* Ok I'll put some comments here about what is missing.
2159 I have fixed BackSpace (and thus Delete) to not delete
2160 double-spaces automagically. I have also changed Cut,
2161 Copy and Paste to hopefully do some sensible things.
2162 There are still some small problems that can lead to
2163 double spaces stored in the document file or space at
2164 the beginning of paragraphs. This happens if you have
2165 the cursor betwenn to spaces and then save. Or if you
2166 cut and paste and the selection have a space at the
2167 beginning and then save right after the paste. I am
2168 sure none of these are very hard to fix, but I will
2169 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2170 that I can get some feedback. (Lgb)
2173 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2174 // delete the LineSeparator.
2177 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2178 // delete the LineSeparator.
2181 // If the pos around the old_cursor were spaces, delete one of them.
2182 if (old_cursor.par() != cursor.par()
2183 || old_cursor.pos() != cursor.pos()) {
2184 // Only if the cursor has really moved
2186 if (old_cursor.pos() > 0
2187 && old_cursor.pos() < old_cursor.par()->size()
2188 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2189 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2190 old_cursor.par()->erase(old_cursor.pos() - 1);
2191 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2193 #ifdef WITH_WARNINGS
2194 #warning This will not work anymore when we have multiple views of the same buffer
2195 // In this case, we will have to correct also the cursors held by
2196 // other bufferviews. It will probably be easier to do that in a more
2197 // automated way in LyXCursor code. (JMarc 26/09/2001)
2199 // correct all cursors held by the LyXText
2200 fixCursorAfterDelete(bview, cursor, old_cursor);
2201 fixCursorAfterDelete(bview, selection.cursor,
2203 fixCursorAfterDelete(bview, selection.start,
2205 fixCursorAfterDelete(bview, selection.end, old_cursor);
2206 fixCursorAfterDelete(bview, last_sel_cursor,
2208 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2209 fixCursorAfterDelete(bview, toggle_end_cursor,
2215 // don't delete anything if this is the ONLY paragraph!
2216 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2219 // Do not delete empty paragraphs with keepempty set.
2220 if (old_cursor.par()->layout()->keepempty)
2223 // only do our magic if we changed paragraph
2224 if (old_cursor.par() == cursor.par())
2227 // record if we have deleted a paragraph
2228 // we can't possibly have deleted a paragraph before this point
2229 bool deleted = false;
2231 if ((old_cursor.par()->empty()
2232 || (old_cursor.par()->size() == 1
2233 && old_cursor.par()->isLineSeparator(0)))) {
2234 // ok, we will delete anything
2235 LyXCursor tmpcursor;
2237 // make sure that you do not delete any environments
2238 status(bview, LyXText::NEED_MORE_REFRESH);
2241 if (old_cursor.row()->previous()) {
2242 refresh_row = old_cursor.row()->previous();
2243 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2245 cursor = old_cursor; // that undo can restore the right cursor position
2246 Paragraph * endpar = old_cursor.par()->next();
2247 if (endpar && endpar->getDepth()) {
2248 while (endpar && endpar->getDepth()) {
2249 endpar = endpar->next();
2252 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2256 removeRow(old_cursor.row());
2257 if (ownerParagraph() == old_cursor.par()) {
2258 ownerParagraph(ownerParagraph()->next());
2261 delete old_cursor.par();
2263 /* Breakagain the next par. Needed because of
2264 * the parindent that can occur or dissappear.
2265 * The next row can change its height, if
2266 * there is another layout before */
2267 if (refresh_row->next()) {
2268 breakAgain(bview, refresh_row->next());
2269 updateCounters(bview);
2271 setHeightOfRow(bview, refresh_row);
2273 refresh_row = old_cursor.row()->next();
2274 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
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(bview, Undo::DELETE, old_cursor.par(), endpar);
2288 removeRow(old_cursor.row());
2290 if (ownerParagraph() == old_cursor.par()) {
2291 ownerParagraph(ownerParagraph()->next());
2294 delete old_cursor.par();
2296 /* Breakagain the next par. Needed because of
2297 the parindent that can occur or dissappear.
2298 The next row can change its height, if
2299 there is another layout before */
2301 breakAgain(bview, refresh_row);
2302 updateCounters(bview);
2307 setCursorIntern(bview, cursor.par(), cursor.pos());
2309 if (selection.cursor.par() == old_cursor.par()
2310 && selection.cursor.pos() == old_cursor.pos()) {
2311 // correct selection
2312 selection.cursor = cursor;
2316 if (old_cursor.par()->stripLeadingSpaces()) {
2317 redoParagraphs(bview, old_cursor,
2318 old_cursor.par()->next());
2320 setCursorIntern(bview, cursor.par(), cursor.pos());
2321 selection.cursor = cursor;
2328 Paragraph * LyXText::ownerParagraph() const
2331 return inset_owner->paragraph();
2333 return &*(bv_owner->buffer()->paragraphs.begin());
2337 void LyXText::ownerParagraph(Paragraph * p) const
2340 inset_owner->paragraph(p);
2342 bv_owner->buffer()->paragraphs.set(p);
2347 void LyXText::ownerParagraph(int id, Paragraph * p) const
2349 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2350 if (op && op->inInset()) {
2351 static_cast<InsetText *>(op->inInset())->paragraph(p);
2358 LyXText::text_status LyXText::status() const
2364 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2366 LyXText * t = bview->text;
2368 // We should only go up with refreshing code so this means that if
2369 // we have a MORE refresh we should never set it to LITTLE if we still
2370 // didn't handle it (and then it will be UNCHANGED. Now as long as
2371 // we stay inside one LyXText this may work but we need to tell the
2372 // outermost LyXText that it should REALLY draw us if there is some
2373 // change in a Inset::LyXText. So you see that when we are inside a
2374 // inset's LyXText we give the LITTLE to the outermost LyXText to
2375 // tell'em that it should redraw the actual row (where the inset
2376 // resides! Capito?!
2378 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2380 if (inset_owner && st != UNCHANGED) {
2381 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2382 if (!t->refresh_row) {
2383 t->refresh_row = t->cursor.row();
2384 t->refresh_y = t->cursor.y() -
2385 t->cursor.row()->baseline();