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 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "frontends/LyXView.h"
21 #include "undo_funcs.h"
23 #include "bufferparams.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
37 #include "insets/inseterror.h"
38 #include "insets/insetbib.h"
39 #include "insets/insetspecialchar.h"
40 #include "insets/insettext.h"
41 #include "insets/insetfloat.h"
42 #include "insets/insetwrap.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
56 LyXText::LyXText(BufferView * bv)
57 : height(0), width(0), first_y(0),
58 bv_owner(bv), inset_owner(0), the_locking_inset(0),
59 need_break_row(0), refresh_y(0), refresh_row(0),
60 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
64 LyXText::LyXText(InsetText * inset)
65 : height(0), width(0), first_y(0),
66 bv_owner(0), inset_owner(inset), the_locking_inset(0),
67 need_break_row(0), refresh_y(0), refresh_row(0),
68 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
72 void LyXText::init(BufferView * bview, bool reinit)
75 // Delete all rows, this does not touch the paragraphs!
76 Row * tmprow = firstrow;
78 tmprow = firstrow->next();
87 copylayouttype.erase();
88 first_y = refresh_y = 0;
89 status_ = LyXText::UNCHANGED;
93 Paragraph * par = ownerParagraph();
94 current_font = getFont(bview->buffer(), par, 0);
97 insertParagraph(bview, par, lastrow);
100 setCursorIntern(bview, firstrow->par(), 0);
101 selection.cursor = cursor;
103 updateCounters(bview);
109 // Delete all rows, this does not touch the paragraphs!
110 Row * tmprow = firstrow;
112 tmprow = firstrow->next();
121 LyXFont const realizeFont(LyXFont const & font,
125 LyXTextClass const & tclass = buf->params.getLyXTextClass();
126 LyXFont tmpfont(font);
127 Paragraph::depth_type par_depth = par->getDepth();
129 // Resolve against environment font information
130 while (par && par_depth && !tmpfont.resolved()) {
131 par = par->outerHook();
133 tmpfont.realize(par->layout()->font);
134 par_depth = par->getDepth();
138 tmpfont.realize(tclass.defaultfont());
146 // Gets the fully instantiated font at a given position in a paragraph
147 // Basically the same routine as Paragraph::getFont() in paragraph.C.
148 // The difference is that this one is used for displaying, and thus we
149 // are allowed to make cosmetic improvements. For instance make footnotes
151 // If position is -1, we get the layout font of the paragraph.
152 // If position is -2, we get the font of the manual label of the paragraph.
153 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
156 lyx::Assert(pos >= 0);
158 LyXLayout_ptr const & layout = par->layout();
160 // We specialize the 95% common case:
161 if (!par->getDepth()) {
162 if (layout->labeltype == LABEL_MANUAL
163 && pos < beginningOfMainBody(buf, par)) {
165 LyXFont f = par->getFontSettings(buf->params, pos);
167 par->inInset()->getDrawFont(f);
168 return f.realize(layout->reslabelfont);
170 LyXFont f = par->getFontSettings(buf->params, pos);
172 par->inInset()->getDrawFont(f);
173 return f.realize(layout->resfont);
177 // The uncommon case need not be optimized as much
181 if (pos < beginningOfMainBody(buf, par)) {
183 layoutfont = layout->labelfont;
186 layoutfont = layout->font;
189 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
190 tmpfont.realize(layoutfont);
193 par->inInset()->getDrawFont(tmpfont);
195 return realizeFont(tmpfont, buf, par);
199 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
201 LyXLayout_ptr const & layout = par->layout();
203 if (!par->getDepth()) {
204 return layout->resfont;
207 return realizeFont(layout->font, buf, par);
211 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
213 LyXLayout_ptr const & layout = par->layout();
215 if (!par->getDepth()) {
216 return layout->reslabelfont;
219 return realizeFont(layout->labelfont, buf, par);
223 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
224 pos_type pos, LyXFont const & fnt,
227 Buffer const * buf = bv->buffer();
228 LyXFont font = getFont(buf, par, pos);
229 font.update(fnt, buf->params.language, toggleall);
230 // Let the insets convert their font
231 if (par->isInset(pos)) {
232 Inset * inset = par->getInset(pos);
233 if (isEditableInset(inset)) {
234 UpdatableInset * uinset =
235 static_cast<UpdatableInset *>(inset);
236 uinset->setFont(bv, fnt, toggleall, true);
240 // Plug thru to version below:
241 setCharFont(buf, par, pos, font);
245 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
246 pos_type pos, LyXFont const & fnt)
250 LyXTextClass const & tclass = buf->params.getLyXTextClass();
251 LyXLayout_ptr const & layout = par->layout();
253 // Get concrete layout font to reduce against
256 if (pos < beginningOfMainBody(buf, par))
257 layoutfont = layout->labelfont;
259 layoutfont = layout->font;
261 // Realize against environment font information
262 if (par->getDepth()) {
263 Paragraph * tp = par;
264 while (!layoutfont.resolved() && tp && tp->getDepth()) {
265 tp = tp->outerHook();
267 layoutfont.realize(tp->layout()->font);
271 layoutfont.realize(tclass.defaultfont());
273 // Now, reduce font against full layout font
274 font.reduce(layoutfont);
276 par->setFont(pos, font);
280 // inserts a new row behind the specified row, increments
281 // the touched counters
282 void LyXText::insertRow(Row * row, Paragraph * par,
285 Row * tmprow = new Row;
288 tmprow->next(firstrow);
291 tmprow->previous(row);
292 tmprow->next(row->next());
297 tmprow->next()->previous(tmprow);
299 if (tmprow->previous())
300 tmprow->previous()->next(tmprow);
311 // removes the row and reset the touched counters
312 void LyXText::removeRow(Row * row) const
314 Row * row_prev = row->previous();
316 row->next()->previous(row_prev);
318 firstrow = row->next();
319 // lyx::Assert(firstrow);
321 row_prev->next(row->next());
323 if (row == lastrow) {
324 lyx::Assert(!row->next());
327 if (refresh_row == row) {
328 refresh_row = row_prev ? row_prev : row->next();
329 // what about refresh_y, refresh_height
332 height -= row->height(); // the text becomes smaller
338 // remove all following rows of the paragraph of the specified row.
339 void LyXText::removeParagraph(Row * row) const
341 Paragraph * tmppar = row->par();
345 while (row && row->par() == tmppar) {
346 tmprow = row->next();
353 // insert the specified paragraph behind the specified row
354 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
357 // insert a new row, starting at position 0
358 insertRow(row, par, 0);
360 // and now append the whole paragraph behind the new row
363 appendParagraph(bview, firstrow);
365 row->next()->height(0);
366 appendParagraph(bview, row->next());
371 Inset * LyXText::getInset() const
374 if (cursor.pos() == 0 && cursor.par()->bibkey) {
375 inset = cursor.par()->bibkey;
376 } else if (cursor.pos() < cursor.par()->size()
377 && cursor.par()->isInset(cursor.pos())) {
378 inset = cursor.par()->getInset(cursor.pos());
384 void LyXText::toggleInset(BufferView * bview)
386 Inset * inset = getInset();
387 // is there an editable inset at cursor position?
388 if (!isEditableInset(inset)) {
389 // No, try to see if we are inside a collapsable inset
390 if (inset_owner && inset_owner->owner()
391 && inset_owner->owner()->isOpen()) {
392 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
393 inset_owner->owner()->close(bview);
394 bview->getLyXText()->cursorRight(bview);
398 //bview->owner()->message(inset->editMessage());
400 // do we want to keep this?? (JMarc)
401 if (!isHighlyEditableInset(inset))
402 setCursorParUndo(bview);
404 if (inset->isOpen()) {
410 inset->open(bview, !inset->isOpen());
415 /* used in setlayout */
416 // Asger is not sure we want to do this...
417 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
420 LyXLayout_ptr const & layout = par->layout();
423 for (pos_type pos = 0; pos < par->size(); ++pos) {
424 if (pos < beginningOfMainBody(buf, par))
425 layoutfont = layout->labelfont;
427 layoutfont = layout->font;
429 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
430 tmpfont.reduce(layoutfont);
431 par->setFont(pos, tmpfont);
436 Paragraph * LyXText::setLayout(BufferView * bview,
437 LyXCursor & cur, LyXCursor & sstart_cur,
438 LyXCursor & send_cur,
439 string const & layout)
441 Paragraph * endpar = send_cur.par()->next();
442 Paragraph * undoendpar = endpar;
444 if (endpar && endpar->getDepth()) {
445 while (endpar && endpar->getDepth()) {
446 endpar = endpar->next();
450 endpar = endpar->next(); // because of parindents etc.
453 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
455 // ok we have a selection. This is always between sstart_cur
456 // and sel_end cursor
458 Paragraph * par = sstart_cur.par();
459 Paragraph * epar = send_cur.par()->next();
461 LyXLayout_ptr const & lyxlayout =
462 bview->buffer()->params.getLyXTextClass()[layout];
465 par->applyLayout(lyxlayout);
466 makeFontEntriesLayoutSpecific(bview->buffer(), par);
467 Paragraph * fppar = par;
468 fppar->params().spaceTop(lyxlayout->fill_top ?
469 VSpace(VSpace::VFILL)
470 : VSpace(VSpace::NONE));
471 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
472 VSpace(VSpace::VFILL)
473 : VSpace(VSpace::NONE));
474 if (lyxlayout->margintype == MARGIN_MANUAL)
475 par->setLabelWidthString(lyxlayout->labelstring());
476 if (lyxlayout->labeltype != LABEL_BIBLIO
478 delete fppar->bibkey;
483 } while (par != epar);
489 // set layout over selection and make a total rebreak of those paragraphs
490 void LyXText::setLayout(BufferView * bview, string const & layout)
492 LyXCursor tmpcursor = cursor; /* store the current cursor */
494 // if there is no selection just set the layout
495 // of the current paragraph */
496 if (!selection.set()) {
497 selection.start = cursor; // dummy selection
498 selection.end = cursor;
500 Paragraph * endpar = setLayout(bview, cursor, selection.start,
501 selection.end, layout);
502 redoParagraphs(bview, selection.start, endpar);
504 // we have to reset the selection, because the
505 // geometry could have changed
506 setCursor(bview, selection.start.par(),
507 selection.start.pos(), false);
508 selection.cursor = cursor;
509 setCursor(bview, selection.end.par(), selection.end.pos(), false);
510 updateCounters(bview);
513 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
517 // increment depth over selection and
518 // make a total rebreak of those paragraphs
519 void LyXText::incDepth(BufferView * bview)
521 // If there is no selection, just use the current paragraph
522 if (!selection.set()) {
523 selection.start = cursor; // dummy selection
524 selection.end = cursor;
527 // We end at the next paragraph with depth 0
528 Paragraph * endpar = selection.end.par()->next();
530 Paragraph * undoendpar = endpar;
532 if (endpar && endpar->getDepth()) {
533 while (endpar && endpar->getDepth()) {
534 endpar = endpar->next();
538 endpar = endpar->next(); // because of parindents etc.
541 setUndo(bview, Undo::EDIT,
542 selection.start.par(), undoendpar);
544 LyXCursor tmpcursor = cursor; // store the current cursor
546 // ok we have a selection. This is always between sel_start_cursor
547 // and sel_end cursor
548 cursor = selection.start;
551 // NOTE: you can't change the depth of a bibliography entry
552 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
553 Paragraph * prev = cursor.par()->previous();
556 if (cursor.par()->getDepth()
557 < prev->getMaxDepthAfter()) {
558 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
562 if (cursor.par() == selection.end.par())
564 cursor.par(cursor.par()->next());
567 redoParagraphs(bview, selection.start, endpar);
569 // we have to reset the selection, because the
570 // geometry could have changed
571 setCursor(bview, selection.start.par(), selection.start.pos());
572 selection.cursor = cursor;
573 setCursor(bview, selection.end.par(), selection.end.pos());
574 updateCounters(bview);
577 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
581 // decrement depth over selection and
582 // make a total rebreak of those paragraphs
583 void LyXText::decDepth(BufferView * bview)
585 // if there is no selection just set the layout
586 // of the current paragraph
587 if (!selection.set()) {
588 selection.start = cursor; // dummy selection
589 selection.end = cursor;
591 Paragraph * endpar = selection.end.par()->next();
592 Paragraph * undoendpar = endpar;
594 if (endpar && endpar->getDepth()) {
595 while (endpar && endpar->getDepth()) {
596 endpar = endpar->next();
600 endpar = endpar->next(); // because of parindents etc.
603 setUndo(bview, Undo::EDIT,
604 selection.start.par(), undoendpar);
606 LyXCursor tmpcursor = cursor; // store the current cursor
608 // ok we have a selection. This is always between sel_start_cursor
609 // and sel_end cursor
610 cursor = selection.start;
613 if (cursor.par()->params().depth()) {
614 cursor.par()->params()
615 .depth(cursor.par()->params().depth() - 1);
617 if (cursor.par() == selection.end.par()) {
620 cursor.par(cursor.par()->next());
623 redoParagraphs(bview, selection.start, endpar);
625 // we have to reset the selection, because the
626 // geometry could have changed
627 setCursor(bview, selection.start.par(),
628 selection.start.pos());
629 selection.cursor = cursor;
630 setCursor(bview, selection.end.par(), selection.end.pos());
631 updateCounters(bview);
634 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
638 // set font over selection and make a total rebreak of those paragraphs
639 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
641 // if there is no selection just set the current_font
642 if (!selection.set()) {
643 // Determine basis font
645 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
647 layoutfont = getLabelFont(bview->buffer(),
650 layoutfont = getLayoutFont(bview->buffer(),
653 // Update current font
654 real_current_font.update(font,
655 bview->buffer()->params.language,
658 // Reduce to implicit settings
659 current_font = real_current_font;
660 current_font.reduce(layoutfont);
661 // And resolve it completely
662 real_current_font.realize(layoutfont);
667 LyXCursor tmpcursor = cursor; // store the current cursor
669 // ok we have a selection. This is always between sel_start_cursor
670 // and sel_end cursor
672 setUndo(bview, Undo::EDIT,
673 selection.start.par(), selection.end.par()->next());
675 cursor = selection.start;
676 while (cursor.par() != selection.end.par() ||
677 cursor.pos() < selection.end.pos())
679 if (cursor.pos() < cursor.par()->size()) {
680 // an open footnote should behave like a closed one
681 setCharFont(bview, cursor.par(), cursor.pos(),
683 cursor.pos(cursor.pos() + 1);
686 cursor.par(cursor.par()->next());
691 redoParagraphs(bview, selection.start, selection.end.par()->next());
693 // we have to reset the selection, because the
694 // geometry could have changed, but we keep
695 // it for user convenience
696 setCursor(bview, selection.start.par(), selection.start.pos());
697 selection.cursor = cursor;
698 setCursor(bview, selection.end.par(), selection.end.pos());
700 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
701 tmpcursor.boundary());
705 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
707 Row * tmprow = cur.row();
708 int y = cur.y() - tmprow->baseline();
710 setHeightOfRow(bview, tmprow);
712 while (tmprow->previous()
713 && tmprow->previous()->par() == tmprow->par()) {
714 tmprow = tmprow->previous();
715 y -= tmprow->height();
716 setHeightOfRow(bview, tmprow);
719 // we can set the refreshing parameters now
720 status(bview, LyXText::NEED_MORE_REFRESH);
722 refresh_row = tmprow;
723 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
727 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
729 Row * tmprow = cur.row();
731 int y = cur.y() - tmprow->baseline();
732 setHeightOfRow(bview, tmprow);
734 while (tmprow->previous()
735 && tmprow->previous()->par() == tmprow->par()) {
736 tmprow = tmprow->previous();
737 y -= tmprow->height();
740 // we can set the refreshing parameters now
741 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
743 refresh_row = tmprow;
745 status(bview, LyXText::NEED_MORE_REFRESH);
746 setCursor(bview, cur.par(), cur.pos());
750 // deletes and inserts again all paragaphs between the cursor
751 // and the specified par
752 // This function is needed after SetLayout and SetFont etc.
753 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
754 Paragraph const * endpar) const
757 Paragraph * tmppar = 0;
758 Paragraph * first_phys_par = 0;
760 Row * tmprow = cur.row();
762 int y = cur.y() - tmprow->baseline();
764 if (!tmprow->previous()) {
765 // a trick/hack for UNDO
766 // This is needed because in an UNDO/REDO we could have changed
767 // the ownerParagrah() so the paragraph inside the row is NOT
768 // my really first par anymore. Got it Lars ;) (Jug 20011206)
769 first_phys_par = ownerParagraph();
771 first_phys_par = tmprow->par();
772 while (tmprow->previous()
773 && tmprow->previous()->par() == first_phys_par)
775 tmprow = tmprow->previous();
776 y -= tmprow->height();
780 // we can set the refreshing parameters now
781 status(bview, LyXText::NEED_MORE_REFRESH);
783 refresh_row = tmprow->previous(); /* the real refresh row will
784 be deleted, so I store
788 tmppar = tmprow->next()->par();
791 while (tmprow->next() && tmppar != endpar) {
792 removeRow(tmprow->next());
793 if (tmprow->next()) {
794 tmppar = tmprow->next()->par();
800 // remove the first one
801 tmprow2 = tmprow; /* this is because tmprow->previous()
803 tmprow = tmprow->previous();
806 tmppar = first_phys_par;
810 insertParagraph(bview, tmppar, tmprow);
814 while (tmprow->next()
815 && tmprow->next()->par() == tmppar) {
816 tmprow = tmprow->next();
818 tmppar = tmppar->next();
820 } while (tmppar && tmppar != endpar);
822 // this is because of layout changes
824 refresh_y -= refresh_row->height();
825 setHeightOfRow(bview, refresh_row);
827 refresh_row = firstrow;
829 setHeightOfRow(bview, refresh_row);
832 if (tmprow && tmprow->next())
833 setHeightOfRow(bview, tmprow->next());
834 updateCounters(bview);
838 void LyXText::fullRebreak(BufferView * bview)
844 if (need_break_row) {
845 breakAgain(bview, need_break_row);
852 // important for the screen
855 // the cursor set functions have a special mechanism. When they
856 // realize, that you left an empty paragraph, they will delete it.
857 // They also delete the corresponding row
859 // need the selection cursor:
860 void LyXText::setSelection(BufferView * bview)
862 bool const lsel = selection.set();
864 if (!selection.set()) {
865 last_sel_cursor = selection.cursor;
866 selection.start = selection.cursor;
867 selection.end = selection.cursor;
872 // first the toggling area
873 if (cursor.y() < last_sel_cursor.y()
874 || (cursor.y() == last_sel_cursor.y()
875 && cursor.x() < last_sel_cursor.x())) {
876 toggle_end_cursor = last_sel_cursor;
877 toggle_cursor = cursor;
879 toggle_end_cursor = cursor;
880 toggle_cursor = last_sel_cursor;
883 last_sel_cursor = cursor;
885 // and now the whole selection
887 if (selection.cursor.par() == cursor.par())
888 if (selection.cursor.pos() < cursor.pos()) {
889 selection.end = cursor;
890 selection.start = selection.cursor;
892 selection.end = selection.cursor;
893 selection.start = cursor;
895 else if (selection.cursor.y() < cursor.y() ||
896 (selection.cursor.y() == cursor.y()
897 && selection.cursor.x() < cursor.x())) {
898 selection.end = cursor;
899 selection.start = selection.cursor;
902 selection.end = selection.cursor;
903 selection.start = cursor;
906 // a selection with no contents is not a selection
907 if (selection.start.par() == selection.end.par() &&
908 selection.start.pos() == selection.end.pos())
909 selection.set(false);
911 if (inset_owner && (selection.set() || lsel))
912 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
916 string const LyXText::selectionAsString(Buffer const * buffer,
919 if (!selection.set()) return string();
921 // should be const ...
922 Paragraph * startpar(selection.start.par());
923 Paragraph * endpar(selection.end.par());
924 pos_type const startpos(selection.start.pos());
925 pos_type const endpos(selection.end.pos());
927 if (startpar == endpar) {
928 return startpar->asString(buffer, startpos, endpos, label);
933 // First paragraph in selection
934 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
936 // The paragraphs in between (if any)
937 LyXCursor tmpcur(selection.start);
938 tmpcur.par(tmpcur.par()->next());
939 while (tmpcur.par() != endpar) {
940 result += tmpcur.par()->asString(buffer, 0,
941 tmpcur.par()->size(),
943 tmpcur.par(tmpcur.par()->next());
946 // Last paragraph in selection
947 result += endpar->asString(buffer, 0, endpos, label);
953 void LyXText::clearSelection() const
955 selection.set(false);
956 selection.mark(false);
957 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
958 // reset this in the bv_owner!
959 if (bv_owner && bv_owner->text)
960 bv_owner->text->xsel_cache.set(false);
964 void LyXText::cursorHome(BufferView * bview) const
966 setCursor(bview, cursor.par(), cursor.row()->pos());
970 void LyXText::cursorEnd(BufferView * bview) const
972 if (!cursor.row()->next()
973 || cursor.row()->next()->par() != cursor.row()->par()) {
974 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
976 if (!cursor.par()->empty() &&
977 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
978 || cursor.par()->isNewline(rowLast(cursor.row())))) {
979 setCursor(bview, cursor.par(), rowLast(cursor.row()));
981 setCursor(bview,cursor.par(),
982 rowLast(cursor.row()) + 1);
988 void LyXText::cursorTop(BufferView * bview) const
990 while (cursor.par()->previous())
991 cursor.par(cursor.par()->previous());
992 setCursor(bview, cursor.par(), 0);
996 void LyXText::cursorBottom(BufferView * bview) const
998 while (cursor.par()->next())
999 cursor.par(cursor.par()->next());
1000 setCursor(bview, cursor.par(), cursor.par()->size());
1004 void LyXText::toggleFree(BufferView * bview,
1005 LyXFont const & font, bool toggleall)
1007 // If the mask is completely neutral, tell user
1008 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1009 // Could only happen with user style
1010 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1014 // Try implicit word selection
1015 // If there is a change in the language the implicit word selection
1017 LyXCursor resetCursor = cursor;
1018 bool implicitSelection = (font.language() == ignore_language
1019 && font.number() == LyXFont::IGNORE)
1020 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1023 setFont(bview, font, toggleall);
1025 // Implicit selections are cleared afterwards
1026 //and cursor is set to the original position.
1027 if (implicitSelection) {
1029 cursor = resetCursor;
1030 setCursor(bview, cursor.par(), cursor.pos());
1031 selection.cursor = cursor;
1034 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1038 string LyXText::getStringToIndex(BufferView * bview)
1042 // Try implicit word selection
1043 // If there is a change in the language the implicit word selection
1045 LyXCursor const reset_cursor = cursor;
1046 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1048 if (!selection.set()) {
1049 bview->owner()->message(_("Nothing to index!"));
1052 if (selection.start.par() != selection.end.par()) {
1053 bview->owner()->message(_("Cannot index more than one paragraph!"));
1057 idxstring = selectionAsString(bview->buffer(), false);
1059 // Implicit selections are cleared afterwards
1060 //and cursor is set to the original position.
1061 if (implicitSelection) {
1063 cursor = reset_cursor;
1064 setCursor(bview, cursor.par(), cursor.pos());
1065 selection.cursor = cursor;
1071 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1072 Paragraph const * par) const
1074 if (par->layout()->labeltype != LABEL_MANUAL)
1077 return par->beginningOfMainBody();
1081 // the DTP switches for paragraphs. LyX will store them in the first
1082 // physicla paragraph. When a paragraph is broken, the top settings rest,
1083 // the bottom settings are given to the new one. So I can make shure,
1084 // they do not duplicate themself and you cannnot make dirty things with
1087 void LyXText::setParagraph(BufferView * bview,
1088 bool line_top, bool line_bottom,
1089 bool pagebreak_top, bool pagebreak_bottom,
1090 VSpace const & space_top,
1091 VSpace const & space_bottom,
1092 Spacing const & spacing,
1094 string labelwidthstring,
1097 LyXCursor tmpcursor = cursor;
1098 if (!selection.set()) {
1099 selection.start = cursor;
1100 selection.end = cursor;
1103 // make sure that the depth behind the selection are restored, too
1104 Paragraph * endpar = selection.end.par()->next();
1105 Paragraph * undoendpar = endpar;
1107 if (endpar && endpar->getDepth()) {
1108 while (endpar && endpar->getDepth()) {
1109 endpar = endpar->next();
1110 undoendpar = endpar;
1114 // because of parindents etc.
1115 endpar = endpar->next();
1118 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1121 Paragraph * tmppar = selection.end.par();
1123 while (tmppar != selection.start.par()->previous()) {
1124 setCursor(bview, tmppar, 0);
1125 status(bview, LyXText::NEED_MORE_REFRESH);
1126 refresh_row = cursor.row();
1127 refresh_y = cursor.y() - cursor.row()->baseline();
1128 cursor.par()->params().lineTop(line_top);
1129 cursor.par()->params().lineBottom(line_bottom);
1130 cursor.par()->params().pagebreakTop(pagebreak_top);
1131 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1132 cursor.par()->params().spaceTop(space_top);
1133 cursor.par()->params().spaceBottom(space_bottom);
1134 cursor.par()->params().spacing(spacing);
1135 // does the layout allow the new alignment?
1136 LyXLayout_ptr const & layout = cursor.par()->layout();
1138 if (align == LYX_ALIGN_LAYOUT)
1139 align = layout->align;
1140 if (align & layout->alignpossible) {
1141 if (align == layout->align)
1142 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1144 cursor.par()->params().align(align);
1146 cursor.par()->setLabelWidthString(labelwidthstring);
1147 cursor.par()->params().noindent(noindent);
1148 tmppar = cursor.par()->previous();
1151 redoParagraphs(bview, selection.start, endpar);
1154 setCursor(bview, selection.start.par(), selection.start.pos());
1155 selection.cursor = cursor;
1156 setCursor(bview, selection.end.par(), selection.end.pos());
1157 setSelection(bview);
1158 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1160 bview->updateInset(inset_owner, true);
1164 // set the counter of a paragraph. This includes the labels
1165 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1167 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1168 LyXLayout_ptr const & layout = par->layout();
1170 if (par->previous()) {
1172 par->params().appendix(par->previous()->params().appendix());
1173 if (!par->params().appendix() && par->params().startOfAppendix()) {
1174 par->params().appendix(true);
1175 textclass.counters().reset();
1177 par->enumdepth = par->previous()->enumdepth;
1178 par->itemdepth = par->previous()->itemdepth;
1180 par->params().appendix(par->params().startOfAppendix());
1185 /* Maybe we have to increment the enumeration depth.
1186 * BUT, enumeration in a footnote is considered in isolation from its
1187 * surrounding paragraph so don't increment if this is the
1188 * first line of the footnote
1189 * AND, bibliographies can't have their depth changed ie. they
1190 * are always of depth 0
1193 && par->previous()->getDepth() < par->getDepth()
1194 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1195 && par->enumdepth < 3
1196 && layout->labeltype != LABEL_BIBLIO) {
1200 // Maybe we have to decrement the enumeration depth, see note above
1202 && par->previous()->getDepth() > par->getDepth()
1203 && layout->labeltype != LABEL_BIBLIO) {
1204 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1207 if (!par->params().labelString().empty()) {
1208 par->params().labelString(string());
1211 if (layout->margintype == MARGIN_MANUAL) {
1212 if (par->params().labelWidthString().empty()) {
1213 par->setLabelWidthString(layout->labelstring());
1216 par->setLabelWidthString(string());
1219 // is it a layout that has an automatic label?
1220 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1222 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1227 if (i >= 0 && i <= buf->params.secnumdepth) {
1229 textclass.counters().step(layout->latexname());
1231 // Is there a label? Useful for Chapter layout
1232 if (!par->params().appendix()) {
1233 if (!layout->labelstring().empty())
1234 par->params().labelString(layout->labelstring());
1236 par->params().labelString(string());
1238 if (!layout->labelstring_appendix().empty())
1239 par->params().labelString(layout->labelstring_appendix());
1241 par->params().labelString(string());
1244 // Use if an integer is here less than elegant. For now.
1245 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1246 if (!par->params().appendix()) {
1247 numbertype = "sectioning";
1249 numbertype = "appendix";
1250 if (par->isRightToLeftPar(buf->params))
1251 langtype = "hebrew";
1256 s << textclass.counters()
1257 .numberLabel(layout->latexname(),
1258 numbertype, langtype, head);
1260 par->params().labelString(par->params().labelString() + s.str().c_str());
1261 // We really want to remove the c_str as soon as
1264 // reset enum counters
1265 textclass.counters().reset("enum");
1266 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1267 textclass.counters().reset("enum");
1268 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1270 // Yes I know this is a really, really! bad solution
1272 string enumcounter("enum");
1274 switch (par->enumdepth) {
1283 enumcounter += "iv";
1286 // not a valid enumdepth...
1290 textclass.counters().step(enumcounter);
1292 s << textclass.counters()
1293 .numberLabel(enumcounter,
1294 "enumeration", langtype);
1295 par->params().labelString(s.str().c_str());
1297 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1298 textclass.counters().step("bibitem");
1299 int number = textclass.counters().value("bibitem");
1301 InsetCommandParams p("bibitem");
1302 par->bibkey = new InsetBibKey(p);
1304 par->bibkey->setCounter(number);
1305 par->params().labelString(layout->labelstring());
1307 // In biblio should't be following counters but...
1309 string s = layout->labelstring();
1311 // the caption hack:
1312 if (layout->labeltype == LABEL_SENSITIVE) {
1313 Paragraph * tmppar = par;
1316 while (tmppar && tmppar->inInset()
1317 // the single '=' is intended below
1318 && (in = tmppar->inInset()->owner())) {
1319 if (in->lyxCode() == Inset::FLOAT_CODE ||
1320 in->lyxCode() == Inset::WRAP_CODE) {
1324 tmppar = in->parOwner();
1330 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1332 textclass.counters().step(fl.type());
1334 // Doesn't work... yet.
1336 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1337 o << fl.name() << " #:";
1340 // par->SetLayout(0);
1341 // s = layout->labelstring;
1342 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1343 ? " :úåòîùî øñç" : "Senseless: ";
1346 par->params().labelString(s);
1348 // reset the enumeration counter. They are always reset
1349 // when there is any other layout between
1350 // Just fall-through between the cases so that all
1351 // enum counters deeper than enumdepth is also reset.
1352 switch (par->enumdepth) {
1354 textclass.counters().reset("enumi");
1356 textclass.counters().reset("enumii");
1358 textclass.counters().reset("enumiii");
1360 textclass.counters().reset("enumiv");
1366 // Updates all counters BEHIND the row. Changed paragraphs
1367 // with a dynamic left margin will be rebroken.
1368 void LyXText::updateCounters(BufferView * bview) const
1372 Row * row = firstrow;
1375 // CHECK if this is really needed. (Lgb)
1376 bview->buffer()->params.getLyXTextClass().counters().reset();
1379 while (row->par() != par)
1382 setCounter(bview->buffer(), par);
1384 // now check for the headline layouts. remember that they
1385 // have a dynamic left margin
1386 LyXLayout_ptr const & layout = par->layout();
1388 if (layout->margintype == MARGIN_DYNAMIC
1389 || layout->labeltype == LABEL_SENSITIVE) {
1390 // Rebreak the paragraph
1391 removeParagraph(row);
1392 appendParagraph(bview, row);
1399 void LyXText::insertInset(BufferView * bview, Inset * inset)
1401 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1403 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1405 cursor.par()->insertInset(cursor.pos(), inset);
1406 // Just to rebreak and refresh correctly.
1407 // The character will not be inserted a second time
1408 insertChar(bview, Paragraph::META_INSET);
1409 // If we enter a highly editable inset the cursor should be to before
1410 // the inset. This couldn't happen before as Undo was not handled inside
1411 // inset now after the Undo LyX tries to call inset->Edit(...) again
1412 // and cannot do this as the cursor is behind the inset and GetInset
1413 // does not return the inset!
1414 if (isHighlyEditableInset(inset)) {
1415 cursorLeft(bview, true);
1421 void LyXText::copyEnvironmentType()
1423 copylayouttype = cursor.par()->layout()->name();
1427 void LyXText::pasteEnvironmentType(BufferView * bview)
1429 setLayout(bview, copylayouttype);
1433 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1435 // Stuff what we got on the clipboard. Even if there is no selection.
1437 // There is a problem with having the stuffing here in that the
1438 // larger the selection the slower LyX will get. This can be
1439 // solved by running the line below only when the selection has
1440 // finished. The solution used currently just works, to make it
1441 // faster we need to be more clever and probably also have more
1442 // calls to stuffClipboard. (Lgb)
1443 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1445 // This doesn't make sense, if there is no selection
1446 if (!selection.set())
1449 // OK, we have a selection. This is always between selection.start
1450 // and selection.end
1452 // make sure that the depth behind the selection are restored, too
1453 Paragraph * endpar = selection.end.par()->next();
1454 Paragraph * undoendpar = endpar;
1456 if (endpar && endpar->getDepth()) {
1457 while (endpar && endpar->getDepth()) {
1458 endpar = endpar->next();
1459 undoendpar = endpar;
1461 } else if (endpar) {
1462 endpar = endpar->next(); // because of parindents etc.
1465 setUndo(bview, Undo::DELETE,
1466 selection.start.par(), undoendpar);
1468 // there are two cases: cut only within one paragraph or
1469 // more than one paragraph
1470 if (selection.start.par() == selection.end.par()) {
1471 // only within one paragraph
1472 endpar = selection.end.par();
1473 int pos = selection.end.pos();
1474 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1475 selection.start.pos(), pos,
1476 bview->buffer()->params.textclass,
1478 selection.end.pos(pos);
1480 endpar = selection.end.par();
1481 int pos = selection.end.pos();
1482 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1483 selection.start.pos(), pos,
1484 bview->buffer()->params.textclass,
1487 selection.end.par(endpar);
1488 selection.end.pos(pos);
1489 cursor.pos(selection.end.pos());
1491 endpar = endpar->next();
1493 // sometimes necessary
1495 selection.start.par()->stripLeadingSpaces();
1497 redoParagraphs(bview, selection.start, endpar);
1499 // cutSelection can invalidate the cursor so we need to set
1501 cursor = selection.start;
1503 // need a valid cursor. (Lgb)
1506 setCursor(bview, cursor.par(), cursor.pos());
1507 selection.cursor = cursor;
1508 updateCounters(bview);
1512 void LyXText::copySelection(BufferView * bview)
1514 // stuff the selection onto the X clipboard, from an explicit copy request
1515 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1517 // this doesnt make sense, if there is no selection
1518 if (!selection.set())
1521 // ok we have a selection. This is always between selection.start
1522 // and sel_end cursor
1524 // copy behind a space if there is one
1525 while (selection.start.par()->size() > selection.start.pos()
1526 && selection.start.par()->isLineSeparator(selection.start.pos())
1527 && (selection.start.par() != selection.end.par()
1528 || selection.start.pos() < selection.end.pos()))
1529 selection.start.pos(selection.start.pos() + 1);
1531 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1532 selection.start.pos(), selection.end.pos(),
1533 bview->buffer()->params.textclass);
1537 void LyXText::pasteSelection(BufferView * bview)
1539 // this does not make sense, if there is nothing to paste
1540 if (!CutAndPaste::checkPastePossible(cursor.par()))
1543 setUndo(bview, Undo::INSERT,
1544 cursor.par(), cursor.par()->next());
1547 Paragraph * actpar = cursor.par();
1548 int pos = cursor.pos();
1550 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1551 bview->buffer()->params.textclass);
1553 redoParagraphs(bview, cursor, endpar);
1555 setCursor(bview, cursor.par(), cursor.pos());
1558 selection.cursor = cursor;
1559 setCursor(bview, actpar, pos);
1560 setSelection(bview);
1561 updateCounters(bview);
1565 // sets the selection over the number of characters of string, no check!!
1566 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1571 selection.cursor = cursor;
1572 for (string::size_type i = 0; i < str.length(); ++i)
1574 setSelection(bview);
1578 // simple replacing. The font of the first selected character is used
1579 void LyXText::replaceSelectionWithString(BufferView * bview,
1582 setCursorParUndo(bview);
1585 if (!selection.set()) { // create a dummy selection
1586 selection.end = cursor;
1587 selection.start = cursor;
1590 // Get font setting before we cut
1591 pos_type pos = selection.end.pos();
1592 LyXFont const font = selection.start.par()
1593 ->getFontSettings(bview->buffer()->params,
1594 selection.start.pos());
1596 // Insert the new string
1597 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1598 selection.end.par()->insertChar(pos, (*cit), font);
1602 // Cut the selection
1603 cutSelection(bview, true, false);
1609 // needed to insert the selection
1610 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1612 Paragraph * par = cursor.par();
1613 pos_type pos = cursor.pos();
1614 Paragraph * endpar = cursor.par()->next();
1616 setCursorParUndo(bview);
1618 // only to be sure, should not be neccessary
1621 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1623 redoParagraphs(bview, cursor, endpar);
1624 setCursor(bview, cursor.par(), cursor.pos());
1625 selection.cursor = cursor;
1626 setCursor(bview, par, pos);
1627 setSelection(bview);
1631 // turns double-CR to single CR, others where converted into one
1632 // blank. Then InsertStringAsLines is called
1633 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1635 string linestr(str);
1636 bool newline_inserted = false;
1637 for (string::size_type i = 0; i < linestr.length(); ++i) {
1638 if (linestr[i] == '\n') {
1639 if (newline_inserted) {
1640 // we know that \r will be ignored by
1641 // InsertStringA. Of course, it is a dirty
1642 // trick, but it works...
1643 linestr[i - 1] = '\r';
1647 newline_inserted = true;
1649 } else if (IsPrintable(linestr[i])) {
1650 newline_inserted = false;
1653 insertStringAsLines(bview, linestr);
1657 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1660 LyXCursor tmpcursor;
1664 Row * row = getRow(par, pos, y);
1666 // is there a break one row above
1667 if (row->previous() && row->previous()->par() == row->par()) {
1668 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1669 if (z >= row->pos()) {
1670 // set the dimensions of the row above
1671 y -= row->previous()->height();
1673 refresh_row = row->previous();
1674 status(bview, LyXText::NEED_MORE_REFRESH);
1676 breakAgain(bview, row->previous());
1678 // set the cursor again. Otherwise
1679 // dangling pointers are possible
1680 setCursor(bview, cursor.par(), cursor.pos(),
1681 false, cursor.boundary());
1682 selection.cursor = cursor;
1687 int const tmpheight = row->height();
1688 pos_type const tmplast = rowLast(row);
1692 breakAgain(bview, row);
1693 if (row->height() == tmpheight && rowLast(row) == tmplast)
1694 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1696 status(bview, LyXText::NEED_MORE_REFRESH);
1698 // check the special right address boxes
1699 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1706 redoDrawingOfParagraph(bview, tmpcursor);
1709 // set the cursor again. Otherwise dangling pointers are possible
1710 // also set the selection
1712 if (selection.set()) {
1714 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1715 false, selection.cursor.boundary());
1716 selection.cursor = cursor;
1717 setCursorIntern(bview, selection.start.par(),
1718 selection.start.pos(),
1719 false, selection.start.boundary());
1720 selection.start = cursor;
1721 setCursorIntern(bview, selection.end.par(),
1722 selection.end.pos(),
1723 false, selection.end.boundary());
1724 selection.end = cursor;
1725 setCursorIntern(bview, last_sel_cursor.par(),
1726 last_sel_cursor.pos(),
1727 false, last_sel_cursor.boundary());
1728 last_sel_cursor = cursor;
1731 setCursorIntern(bview, cursor.par(), cursor.pos(),
1732 false, cursor.boundary());
1736 // returns false if inset wasn't found
1737 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1739 // first check the current paragraph
1740 int pos = cursor.par()->getPositionOfInset(inset);
1742 checkParagraph(bview, cursor.par(), pos);
1746 // check every paragraph
1748 Paragraph * par = ownerParagraph();
1750 pos = par->getPositionOfInset(inset);
1752 checkParagraph(bview, par, pos);
1762 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1764 bool setfont, bool boundary) const
1766 LyXCursor old_cursor = cursor;
1767 setCursorIntern(bview, par, pos, setfont, boundary);
1768 return deleteEmptyParagraphMechanism(bview, old_cursor);
1772 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1773 pos_type pos, bool boundary) const
1780 cur.boundary(boundary);
1782 // get the cursor y position in text
1784 Row * row = getRow(par, pos, y);
1785 Row * old_row = row;
1787 // if we are before the first char of this row and are still in the
1788 // same paragraph and there is a previous row then put the cursor on
1789 // the end of the previous row
1790 cur.iy(y + row->baseline());
1792 if (row->previous() && pos &&
1793 row->previous()->par() == row->par() &&
1794 par->getChar(pos) == Paragraph::META_INSET &&
1795 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1797 row = row->previous();
1802 // y is now the beginning of the cursor row
1803 y += row->baseline();
1804 // y is now the cursor baseline
1807 pos_type last = rowLastPrintable(old_row);
1809 if (pos > last + 1) {
1810 // This shouldn't happen.
1813 } else if (pos < row->pos()) {
1818 // now get the cursors x position
1819 float x = getCursorX(bview, row, pos, last, boundary);
1822 if (old_row != row) {
1823 x = getCursorX(bview, old_row, pos, last, boundary);
1830 float LyXText::getCursorX(BufferView * bview, Row * row,
1831 pos_type pos, pos_type last, bool boundary) const
1833 pos_type cursor_vpos = 0;
1835 float fill_separator;
1837 float fill_label_hfill;
1838 // This call HAS to be here because of the BidiTables!!!
1839 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1842 if (last < row->pos())
1843 cursor_vpos = row->pos();
1844 else if (pos > last && !boundary)
1845 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1846 ? row->pos() : last + 1;
1847 else if (pos > row->pos() &&
1848 (pos > last || boundary))
1849 /// Place cursor after char at (logical) position pos - 1
1850 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1851 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1853 /// Place cursor before char at (logical) position pos
1854 cursor_vpos = (bidi_level(pos) % 2 == 0)
1855 ? log2vis(pos) : log2vis(pos) + 1;
1857 pos_type main_body =
1858 beginningOfMainBody(bview->buffer(), row->par());
1859 if ((main_body > 0) &&
1860 ((main_body-1 > last) ||
1861 !row->par()->isLineSeparator(main_body-1)))
1864 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1865 pos_type pos = vis2log(vpos);
1866 if (main_body > 0 && pos == main_body - 1) {
1867 x += fill_label_hfill +
1868 font_metrics::width(
1869 row->par()->layout()->labelsep,
1870 getLabelFont(bview->buffer(),
1872 if (row->par()->isLineSeparator(main_body - 1))
1873 x -= singleWidth(bview,
1874 row->par(), main_body - 1);
1876 if (hfillExpansion(bview->buffer(), row, pos)) {
1877 x += singleWidth(bview, row->par(), pos);
1878 if (pos >= main_body)
1881 x += fill_label_hfill;
1882 } else if (row->par()->isSeparator(pos)) {
1883 x += singleWidth(bview, row->par(), pos);
1884 if (pos >= main_body)
1885 x += fill_separator;
1887 x += singleWidth(bview, row->par(), pos);
1893 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1894 pos_type pos, bool setfont, bool boundary) const
1896 InsetText * it = static_cast<InsetText *>(par->inInset());
1898 if (it != inset_owner) {
1899 lyxerr[Debug::INSETS] << "InsetText is " << it
1901 << "inset_owner is "
1902 << inset_owner << endl;
1903 #ifdef WITH_WARNINGS
1904 #warning I believe this code is wrong. (Lgb)
1905 #warning Jürgen, have a look at this. (Lgb)
1906 #warning Hmmm, I guess you are right but we
1907 #warning should verify when this is needed
1909 // Jürgen, would you like to have a look?
1910 // I guess we need to move the outer cursor
1911 // and open and lock the inset (bla bla bla)
1912 // stuff I don't know... so can you have a look?
1914 // I moved the lyxerr stuff in here so we can see if
1915 // this is actually really needed and where!
1917 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1922 setCursor(bview, cursor, par, pos, boundary);
1924 setCurrentFont(bview);
1928 void LyXText::setCurrentFont(BufferView * bview) const
1930 pos_type pos = cursor.pos();
1931 if (cursor.boundary() && pos > 0)
1935 if (pos == cursor.par()->size())
1937 else // potentional bug... BUG (Lgb)
1938 if (cursor.par()->isSeparator(pos)) {
1939 if (pos > cursor.row()->pos() &&
1940 bidi_level(pos) % 2 ==
1941 bidi_level(pos - 1) % 2)
1943 else if (pos + 1 < cursor.par()->size())
1949 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1950 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1952 if (cursor.pos() == cursor.par()->size() &&
1953 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1954 !cursor.boundary()) {
1955 Language const * lang =
1956 cursor.par()->getParLanguage(bview->buffer()->params);
1957 current_font.setLanguage(lang);
1958 current_font.setNumber(LyXFont::OFF);
1959 real_current_font.setLanguage(lang);
1960 real_current_font.setNumber(LyXFont::OFF);
1965 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1967 LyXCursor old_cursor = cursor;
1969 setCursorFromCoordinates(bview, cursor, x, y);
1970 setCurrentFont(bview);
1971 deleteEmptyParagraphMechanism(bview, old_cursor);
1978 * return true if the cursor given is at the end of a row,
1979 * and the next row is filled by an inset that spans an entire
1982 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1985 Row const & next = *row.next();
1987 if (next.pos() != cur.pos() || next.par() != cur.par())
1989 if (!cur.par()->isInset(cur.pos()))
1991 Inset const * inset = cur.par()->getInset(cur.pos());
1992 if (inset->needFullRow() || inset->display())
1999 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2002 // Get the row first.
2004 Row * row = getRowNearY(y);
2006 pos_type const column = getColumnNearX(bview, row, x, bound);
2007 cur.par(row->par());
2008 cur.pos(row->pos() + column);
2010 cur.y(y + row->baseline());
2013 if (beforeFullRowInset(*row, cur)) {
2014 pos_type last = rowLastPrintable(row);
2015 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2017 cur.iy(y + row->height() + row->next()->baseline());
2018 cur.irow(row->next());
2024 cur.boundary(bound);
2028 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2030 if (cursor.pos() > 0) {
2031 bool boundary = cursor.boundary();
2032 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2033 if (!internal && !boundary &&
2034 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2035 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2036 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2037 Paragraph * par = cursor.par()->previous();
2038 setCursor(bview, par, par->size());
2043 void LyXText::cursorRight(BufferView * bview, bool internal) const
2045 if (!internal && cursor.boundary() &&
2046 !cursor.par()->isNewline(cursor.pos()))
2047 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2048 else if (cursor.pos() < cursor.par()->size()) {
2049 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2051 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2052 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2053 } else if (cursor.par()->next())
2054 setCursor(bview, cursor.par()->next(), 0);
2058 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2061 int x = cursor.x_fix();
2062 int y = cursor.y() - cursor.row()->baseline() - 1;
2063 setCursorFromCoordinates(bview, x, y);
2065 int y1 = cursor.iy() - first_y;
2068 Inset * inset_hit = checkInsetHit(bview, x, y1);
2069 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2070 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2074 setCursorFromCoordinates(bview, cursor.x_fix(),
2075 cursor.y() - cursor.row()->baseline() - 1);
2080 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2083 int x = cursor.x_fix();
2084 int y = cursor.y() - cursor.row()->baseline() +
2085 cursor.row()->height() + 1;
2086 setCursorFromCoordinates(bview, x, y);
2087 if (!selecting && cursor.row() == cursor.irow()) {
2088 int y1 = cursor.iy() - first_y;
2091 Inset * inset_hit = checkInsetHit(bview, x, y1);
2092 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2093 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2097 setCursorFromCoordinates(bview, cursor.x_fix(),
2098 cursor.y() - cursor.row()->baseline()
2099 + cursor.row()->height() + 1);
2104 void LyXText::cursorUpParagraph(BufferView * bview) const
2106 if (cursor.pos() > 0) {
2107 setCursor(bview, cursor.par(), 0);
2109 else if (cursor.par()->previous()) {
2110 setCursor(bview, cursor.par()->previous(), 0);
2115 void LyXText::cursorDownParagraph(BufferView * bview) const
2117 if (cursor.par()->next()) {
2118 setCursor(bview, cursor.par()->next(), 0);
2120 setCursor(bview, cursor.par(), cursor.par()->size());
2124 // fix the cursor `cur' after a characters has been deleted at `where'
2125 // position. Called by deleteEmptyParagraphMechanism
2126 void LyXText::fixCursorAfterDelete(BufferView * bview,
2128 LyXCursor const & where) const
2130 // if cursor is not in the paragraph where the delete occured,
2132 if (cur.par() != where.par())
2135 // if cursor position is after the place where the delete occured,
2137 if (cur.pos() > where.pos())
2138 cur.pos(cur.pos()-1);
2140 // check also if we don't want to set the cursor on a spot behind the
2141 // pagragraph because we erased the last character.
2142 if (cur.pos() > cur.par()->size())
2143 cur.pos(cur.par()->size());
2145 // recompute row et al. for this cursor
2146 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2150 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2151 LyXCursor const & old_cursor) const
2153 // Would be wrong to delete anything if we have a selection.
2154 if (selection.set())
2157 // We allow all kinds of "mumbo-jumbo" when freespacing.
2158 if (old_cursor.par()->layout()->free_spacing
2159 || old_cursor.par()->isFreeSpacing()) {
2163 /* Ok I'll put some comments here about what is missing.
2164 I have fixed BackSpace (and thus Delete) to not delete
2165 double-spaces automagically. I have also changed Cut,
2166 Copy and Paste to hopefully do some sensible things.
2167 There are still some small problems that can lead to
2168 double spaces stored in the document file or space at
2169 the beginning of paragraphs. This happens if you have
2170 the cursor betwenn to spaces and then save. Or if you
2171 cut and paste and the selection have a space at the
2172 beginning and then save right after the paste. I am
2173 sure none of these are very hard to fix, but I will
2174 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2175 that I can get some feedback. (Lgb)
2178 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2179 // delete the LineSeparator.
2182 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2183 // delete the LineSeparator.
2186 // If the pos around the old_cursor were spaces, delete one of them.
2187 if (old_cursor.par() != cursor.par()
2188 || old_cursor.pos() != cursor.pos()) {
2189 // Only if the cursor has really moved
2191 if (old_cursor.pos() > 0
2192 && old_cursor.pos() < old_cursor.par()->size()
2193 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2194 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2195 old_cursor.par()->erase(old_cursor.pos() - 1);
2196 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2198 #ifdef WITH_WARNINGS
2199 #warning This will not work anymore when we have multiple views of the same buffer
2200 // In this case, we will have to correct also the cursors held by
2201 // other bufferviews. It will probably be easier to do that in a more
2202 // automated way in LyXCursor code. (JMarc 26/09/2001)
2204 // correct all cursors held by the LyXText
2205 fixCursorAfterDelete(bview, cursor, old_cursor);
2206 fixCursorAfterDelete(bview, selection.cursor,
2208 fixCursorAfterDelete(bview, selection.start,
2210 fixCursorAfterDelete(bview, selection.end, old_cursor);
2211 fixCursorAfterDelete(bview, last_sel_cursor,
2213 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2214 fixCursorAfterDelete(bview, toggle_end_cursor,
2220 // don't delete anything if this is the ONLY paragraph!
2221 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2224 // Do not delete empty paragraphs with keepempty set.
2225 if (old_cursor.par()->layout()->keepempty)
2228 // only do our magic if we changed paragraph
2229 if (old_cursor.par() == cursor.par())
2232 // record if we have deleted a paragraph
2233 // we can't possibly have deleted a paragraph before this point
2234 bool deleted = false;
2236 if ((old_cursor.par()->empty()
2237 || (old_cursor.par()->size() == 1
2238 && old_cursor.par()->isLineSeparator(0)))) {
2239 // ok, we will delete anything
2240 LyXCursor tmpcursor;
2242 // make sure that you do not delete any environments
2243 status(bview, LyXText::NEED_MORE_REFRESH);
2246 if (old_cursor.row()->previous()) {
2247 refresh_row = old_cursor.row()->previous();
2248 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2250 cursor = old_cursor; // that undo can restore the right cursor position
2251 Paragraph * endpar = old_cursor.par()->next();
2252 if (endpar && endpar->getDepth()) {
2253 while (endpar && endpar->getDepth()) {
2254 endpar = endpar->next();
2257 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2261 removeRow(old_cursor.row());
2262 if (ownerParagraph() == old_cursor.par()) {
2263 ownerParagraph(ownerParagraph()->next());
2266 delete old_cursor.par();
2268 /* Breakagain the next par. Needed because of
2269 * the parindent that can occur or dissappear.
2270 * The next row can change its height, if
2271 * there is another layout before */
2272 if (refresh_row->next()) {
2273 breakAgain(bview, refresh_row->next());
2274 updateCounters(bview);
2276 setHeightOfRow(bview, refresh_row);
2278 refresh_row = old_cursor.row()->next();
2279 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2282 cursor = old_cursor; // that undo can restore the right cursor position
2283 Paragraph * endpar = old_cursor.par()->next();
2284 if (endpar && endpar->getDepth()) {
2285 while (endpar && endpar->getDepth()) {
2286 endpar = endpar->next();
2289 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2293 removeRow(old_cursor.row());
2295 if (ownerParagraph() == old_cursor.par()) {
2296 ownerParagraph(ownerParagraph()->next());
2299 delete old_cursor.par();
2301 /* Breakagain the next par. Needed because of
2302 the parindent that can occur or dissappear.
2303 The next row can change its height, if
2304 there is another layout before */
2306 breakAgain(bview, refresh_row);
2307 updateCounters(bview);
2312 setCursorIntern(bview, cursor.par(), cursor.pos());
2314 if (selection.cursor.par() == old_cursor.par()
2315 && selection.cursor.pos() == old_cursor.pos()) {
2316 // correct selection
2317 selection.cursor = cursor;
2321 if (old_cursor.par()->stripLeadingSpaces()) {
2322 redoParagraphs(bview, old_cursor,
2323 old_cursor.par()->next());
2325 setCursorIntern(bview, cursor.par(), cursor.pos());
2326 selection.cursor = cursor;
2333 Paragraph * LyXText::ownerParagraph() const
2336 return inset_owner->paragraph();
2338 return &*(bv_owner->buffer()->paragraphs.begin());
2342 void LyXText::ownerParagraph(Paragraph * p) const
2345 inset_owner->paragraph(p);
2347 bv_owner->buffer()->paragraphs.set(p);
2352 void LyXText::ownerParagraph(int id, Paragraph * p) const
2354 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2355 if (op && op->inInset()) {
2356 static_cast<InsetText *>(op->inInset())->paragraph(p);
2363 LyXText::text_status LyXText::status() const
2369 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2371 LyXText * t = bview->text;
2373 // We should only go up with refreshing code so this means that if
2374 // we have a MORE refresh we should never set it to LITTLE if we still
2375 // didn't handle it (and then it will be UNCHANGED. Now as long as
2376 // we stay inside one LyXText this may work but we need to tell the
2377 // outermost LyXText that it should REALLY draw us if there is some
2378 // change in a Inset::LyXText. So you see that when we are inside a
2379 // inset's LyXText we give the LITTLE to the outermost LyXText to
2380 // tell'em that it should redraw the actual row (where the inset
2381 // resides! Capito?!
2383 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2385 if (inset_owner && st != UNCHANGED) {
2386 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2387 if (!t->refresh_row) {
2388 t->refresh_row = t->cursor.row();
2389 t->refresh_y = t->cursor.y() -
2390 t->cursor.row()->baseline();