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"
43 #include "support/LAssert.h"
44 #include "support/textutils.h"
45 #include "support/lstrings.h"
55 LyXText::LyXText(BufferView * bv)
56 : height(0), width(0), first_y(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
63 LyXText::LyXText(InsetText * inset)
64 : height(0), width(0), first_y(0),
65 bv_owner(0), inset_owner(inset), the_locking_inset(0),
66 need_break_row(0), refresh_y(0), refresh_row(0),
67 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
71 void LyXText::init(BufferView * bview, bool reinit)
74 // Delete all rows, this does not touch the paragraphs!
75 Row * tmprow = firstrow;
77 tmprow = firstrow->next();
86 copylayouttype.erase();
87 first_y = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
96 insertParagraph(bview, par, lastrow);
99 setCursorIntern(bview, firstrow->par(), 0);
100 selection.cursor = cursor;
102 updateCounters(bview);
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXTextClass const & tclass = buf->params.getLyXTextClass();
125 LyXFont tmpfont(font);
126 Paragraph::depth_type par_depth = par->getDepth();
128 // Resolve against environment font information
129 while (par && par_depth && !tmpfont.resolved()) {
130 par = par->outerHook();
132 #ifndef INHERIT_LANGUAGE
133 tmpfont.realize(par->layout()->font);
135 tmpfont.realize(tclass[par->layout()]->font,
136 buf->params.language);
138 par_depth = par->getDepth();
142 #ifndef INHERIT_LANGUAGE
143 tmpfont.realize(tclass.defaultfont());
145 tmpfont.realize(tclass.defaultfont(), buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout_ptr const & layout = par->layout();
168 // We specialize the 95% common case:
169 if (!par->getDepth()) {
170 if (layout->labeltype == LABEL_MANUAL
171 && pos < beginningOfMainBody(buf, par)) {
173 LyXFont f = par->getFontSettings(buf->params, pos);
175 par->inInset()->getDrawFont(f);
176 #ifndef INHERIT_LANGUAGE
177 return f.realize(layout->reslabelfont);
179 return f.realize(layout.reslabelfont, buf->params.language);
182 LyXFont f = par->getFontSettings(buf->params, pos);
184 par->inInset()->getDrawFont(f);
185 #ifndef INHERIT_LANGUAGE
186 return f.realize(layout->resfont);
188 return f.realize(layout.resfont, buf->params.language);
193 // The uncommon case need not be optimized as much
197 if (pos < beginningOfMainBody(buf, par)) {
199 layoutfont = layout->labelfont;
202 layoutfont = layout->font;
205 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
206 #ifndef INHERIT_LANGUAGE
207 tmpfont.realize(layoutfont);
209 tmpfont.realize(layoutfont, buf->params.language);
212 par->inInset()->getDrawFont(tmpfont);
214 return realizeFont(tmpfont, buf, par);
218 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
220 LyXLayout_ptr const & layout = par->layout();
222 if (!par->getDepth()) {
223 return layout->resfont;
226 return realizeFont(layout->font, buf, par);
230 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
232 LyXLayout_ptr const & layout = par->layout();
234 if (!par->getDepth()) {
235 return layout->reslabelfont;
238 return realizeFont(layout->labelfont, buf, par);
242 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
243 pos_type pos, LyXFont const & fnt,
246 Buffer const * buf = bv->buffer();
247 LyXFont font = getFont(buf, par, pos);
248 font.update(fnt, buf->params.language, toggleall);
249 // Let the insets convert their font
250 if (par->isInset(pos)) {
251 Inset * inset = par->getInset(pos);
252 if (isEditableInset(inset)) {
253 UpdatableInset * uinset =
254 static_cast<UpdatableInset *>(inset);
255 uinset->setFont(bv, fnt, toggleall, true);
259 // Plug thru to version below:
260 setCharFont(buf, par, pos, font);
264 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
265 pos_type pos, LyXFont const & fnt)
269 LyXTextClass const & tclass = buf->params.getLyXTextClass();
270 LyXLayout_ptr const & layout = par->layout();
272 // Get concrete layout font to reduce against
275 if (pos < beginningOfMainBody(buf, par))
276 layoutfont = layout->labelfont;
278 layoutfont = layout->font;
280 // Realize against environment font information
281 if (par->getDepth()) {
282 Paragraph * tp = par;
283 while (!layoutfont.resolved() && tp && tp->getDepth()) {
284 tp = tp->outerHook();
286 #ifndef INHERIT_LANGUAGE
287 layoutfont.realize(tp->layout()->font);
289 layoutfont.realize(tclass[tp->layout()].font,
290 buf->params.language);
295 #ifndef INHERIT_LANGUAGE
296 layoutfont.realize(tclass.defaultfont());
298 layoutfont.realize(tclass.defaultfont(), buf->params.language);
301 // Now, reduce font against full layout font
302 font.reduce(layoutfont);
304 par->setFont(pos, font);
308 // inserts a new row behind the specified row, increments
309 // the touched counters
310 void LyXText::insertRow(Row * row, Paragraph * par,
313 Row * tmprow = new Row;
316 tmprow->next(firstrow);
319 tmprow->previous(row);
320 tmprow->next(row->next());
325 tmprow->next()->previous(tmprow);
327 if (tmprow->previous())
328 tmprow->previous()->next(tmprow);
339 // removes the row and reset the touched counters
340 void LyXText::removeRow(Row * row) const
342 Row * row_prev = row->previous();
344 row->next()->previous(row_prev);
346 firstrow = row->next();
347 // lyx::Assert(firstrow);
349 row_prev->next(row->next());
351 if (row == lastrow) {
352 lyx::Assert(!row->next());
355 if (refresh_row == row) {
356 refresh_row = row_prev ? row_prev : row->next();
357 // what about refresh_y, refresh_height
360 height -= row->height(); // the text becomes smaller
366 // remove all following rows of the paragraph of the specified row.
367 void LyXText::removeParagraph(Row * row) const
369 Paragraph * tmppar = row->par();
373 while (row && row->par() == tmppar) {
374 tmprow = row->next();
381 // insert the specified paragraph behind the specified row
382 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
385 // insert a new row, starting at position 0
386 insertRow(row, par, 0);
388 // and now append the whole paragraph behind the new row
391 appendParagraph(bview, firstrow);
393 row->next()->height(0);
394 appendParagraph(bview, row->next());
399 Inset * LyXText::getInset() const
402 if (cursor.pos() == 0 && cursor.par()->bibkey) {
403 inset = cursor.par()->bibkey;
404 } else if (cursor.pos() < cursor.par()->size()
405 && cursor.par()->isInset(cursor.pos())) {
406 inset = cursor.par()->getInset(cursor.pos());
412 void LyXText::toggleInset(BufferView * bview)
414 Inset * inset = getInset();
415 // is there an editable inset at cursor position?
416 if (!isEditableInset(inset)) {
417 // No, try to see if we are inside a collapsable inset
418 if (inset_owner && inset_owner->owner()
419 && inset_owner->owner()->isOpen()) {
420 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
421 inset_owner->owner()->close(bview);
422 bview->getLyXText()->cursorRight(bview);
426 //bview->owner()->message(inset->editMessage());
428 // do we want to keep this?? (JMarc)
429 if (!isHighlyEditableInset(inset))
430 setCursorParUndo(bview);
432 if (inset->isOpen()) {
438 inset->open(bview, !inset->isOpen());
443 /* used in setlayout */
444 // Asger is not sure we want to do this...
445 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
448 LyXLayout_ptr const & layout = par->layout();
451 for (pos_type pos = 0; pos < par->size(); ++pos) {
452 if (pos < beginningOfMainBody(buf, par))
453 layoutfont = layout->labelfont;
455 layoutfont = layout->font;
457 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
458 tmpfont.reduce(layoutfont);
459 par->setFont(pos, tmpfont);
464 Paragraph * LyXText::setLayout(BufferView * bview,
465 LyXCursor & cur, LyXCursor & sstart_cur,
466 LyXCursor & send_cur,
467 string const & layout)
469 Paragraph * endpar = send_cur.par()->next();
470 Paragraph * undoendpar = endpar;
472 if (endpar && endpar->getDepth()) {
473 while (endpar && endpar->getDepth()) {
474 endpar = endpar->next();
478 endpar = endpar->next(); // because of parindents etc.
481 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
483 // ok we have a selection. This is always between sstart_cur
484 // and sel_end cursor
486 Paragraph * par = sstart_cur.par();
487 Paragraph * epar = send_cur.par()->next();
489 LyXLayout_ptr const & lyxlayout =
490 bview->buffer()->params.getLyXTextClass()[layout];
493 par->applyLayout(lyxlayout);
494 makeFontEntriesLayoutSpecific(bview->buffer(), par);
495 Paragraph * fppar = par;
496 fppar->params().spaceTop(lyxlayout->fill_top ?
497 VSpace(VSpace::VFILL)
498 : VSpace(VSpace::NONE));
499 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
500 VSpace(VSpace::VFILL)
501 : VSpace(VSpace::NONE));
502 if (lyxlayout->margintype == MARGIN_MANUAL)
503 par->setLabelWidthString(lyxlayout->labelstring());
504 if (lyxlayout->labeltype != LABEL_BIBLIO
506 delete fppar->bibkey;
511 } while (par != epar);
517 // set layout over selection and make a total rebreak of those paragraphs
518 void LyXText::setLayout(BufferView * bview, string const & layout)
520 LyXCursor tmpcursor = cursor; /* store the current cursor */
522 // if there is no selection just set the layout
523 // of the current paragraph */
524 if (!selection.set()) {
525 selection.start = cursor; // dummy selection
526 selection.end = cursor;
528 Paragraph * endpar = setLayout(bview, cursor, selection.start,
529 selection.end, layout);
530 redoParagraphs(bview, selection.start, endpar);
532 // we have to reset the selection, because the
533 // geometry could have changed
534 setCursor(bview, selection.start.par(),
535 selection.start.pos(), false);
536 selection.cursor = cursor;
537 setCursor(bview, selection.end.par(), selection.end.pos(), false);
538 updateCounters(bview);
541 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
545 // increment depth over selection and
546 // make a total rebreak of those paragraphs
547 void LyXText::incDepth(BufferView * bview)
549 // If there is no selection, just use the current paragraph
550 if (!selection.set()) {
551 selection.start = cursor; // dummy selection
552 selection.end = cursor;
555 // We end at the next paragraph with depth 0
556 Paragraph * endpar = selection.end.par()->next();
558 Paragraph * undoendpar = endpar;
560 if (endpar && endpar->getDepth()) {
561 while (endpar && endpar->getDepth()) {
562 endpar = endpar->next();
566 endpar = endpar->next(); // because of parindents etc.
569 setUndo(bview, Undo::EDIT,
570 selection.start.par(), undoendpar);
572 LyXCursor tmpcursor = cursor; // store the current cursor
574 // ok we have a selection. This is always between sel_start_cursor
575 // and sel_end cursor
576 cursor = selection.start;
578 bool anything_changed = false;
581 // NOTE: you can't change the depth of a bibliography entry
582 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
583 Paragraph * prev = cursor.par()->previous();
586 if (cursor.par()->getDepth()
587 < prev->getMaxDepthAfter()) {
588 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
589 anything_changed = true;
593 if (cursor.par() == selection.end.par())
595 cursor.par(cursor.par()->next());
598 // if nothing changed set all depth to 0
599 if (!anything_changed) {
600 cursor = selection.start;
601 while (cursor.par() != selection.end.par()) {
602 cursor.par()->params().depth(0);
603 cursor.par(cursor.par()->next());
605 cursor.par()->params().depth(0);
608 redoParagraphs(bview, selection.start, endpar);
610 // we have to reset the selection, because the
611 // geometry could have changed
612 setCursor(bview, selection.start.par(), selection.start.pos());
613 selection.cursor = cursor;
614 setCursor(bview, selection.end.par(), selection.end.pos());
615 updateCounters(bview);
618 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
622 // decrement depth over selection and
623 // make a total rebreak of those paragraphs
624 void LyXText::decDepth(BufferView * bview)
626 // if there is no selection just set the layout
627 // of the current paragraph
628 if (!selection.set()) {
629 selection.start = cursor; // dummy selection
630 selection.end = cursor;
632 Paragraph * endpar = selection.end.par()->next();
633 Paragraph * undoendpar = endpar;
635 if (endpar && endpar->getDepth()) {
636 while (endpar && endpar->getDepth()) {
637 endpar = endpar->next();
641 endpar = endpar->next(); // because of parindents etc.
644 setUndo(bview, Undo::EDIT,
645 selection.start.par(), undoendpar);
647 LyXCursor tmpcursor = cursor; // store the current cursor
649 // ok we have a selection. This is always between sel_start_cursor
650 // and sel_end cursor
651 cursor = selection.start;
654 if (cursor.par()->params().depth()) {
655 cursor.par()->params()
656 .depth(cursor.par()->params().depth() - 1);
658 if (cursor.par() == selection.end.par()) {
661 cursor.par(cursor.par()->next());
664 redoParagraphs(bview, selection.start, endpar);
666 // we have to reset the selection, because the
667 // geometry could have changed
668 setCursor(bview, selection.start.par(),
669 selection.start.pos());
670 selection.cursor = cursor;
671 setCursor(bview, selection.end.par(), selection.end.pos());
672 updateCounters(bview);
675 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
679 // set font over selection and make a total rebreak of those paragraphs
680 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
682 // if there is no selection just set the current_font
683 if (!selection.set()) {
684 // Determine basis font
686 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
688 layoutfont = getLabelFont(bview->buffer(),
691 layoutfont = getLayoutFont(bview->buffer(),
694 // Update current font
695 real_current_font.update(font,
696 bview->buffer()->params.language,
699 // Reduce to implicit settings
700 current_font = real_current_font;
701 current_font.reduce(layoutfont);
702 // And resolve it completely
703 #ifndef INHERIT_LANGUAGE
704 real_current_font.realize(layoutfont);
706 real_current_font.realize(layoutfont,
707 bview->buffer()->params.language);
712 LyXCursor tmpcursor = cursor; // store the current cursor
714 // ok we have a selection. This is always between sel_start_cursor
715 // and sel_end cursor
717 setUndo(bview, Undo::EDIT,
718 selection.start.par(), selection.end.par()->next());
720 cursor = selection.start;
721 while (cursor.par() != selection.end.par() ||
722 cursor.pos() < selection.end.pos())
724 if (cursor.pos() < cursor.par()->size()) {
725 // an open footnote should behave like a closed one
726 setCharFont(bview, cursor.par(), cursor.pos(),
728 cursor.pos(cursor.pos() + 1);
731 cursor.par(cursor.par()->next());
736 redoParagraphs(bview, selection.start, selection.end.par()->next());
738 // we have to reset the selection, because the
739 // geometry could have changed, but we keep
740 // it for user convenience
741 setCursor(bview, selection.start.par(), selection.start.pos());
742 selection.cursor = cursor;
743 setCursor(bview, selection.end.par(), selection.end.pos());
745 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
746 tmpcursor.boundary());
750 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
752 Row * tmprow = cur.row();
753 int y = cur.y() - tmprow->baseline();
755 setHeightOfRow(bview, tmprow);
757 while (tmprow->previous()
758 && tmprow->previous()->par() == tmprow->par()) {
759 tmprow = tmprow->previous();
760 y -= tmprow->height();
761 setHeightOfRow(bview, tmprow);
764 // we can set the refreshing parameters now
765 status(bview, LyXText::NEED_MORE_REFRESH);
767 refresh_row = tmprow;
768 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
772 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
774 Row * tmprow = cur.row();
776 int y = cur.y() - tmprow->baseline();
777 setHeightOfRow(bview, tmprow);
779 while (tmprow->previous()
780 && tmprow->previous()->par() == tmprow->par()) {
781 tmprow = tmprow->previous();
782 y -= tmprow->height();
785 // we can set the refreshing parameters now
786 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
788 refresh_row = tmprow;
790 status(bview, LyXText::NEED_MORE_REFRESH);
791 setCursor(bview, cur.par(), cur.pos());
795 // deletes and inserts again all paragaphs between the cursor
796 // and the specified par
797 // This function is needed after SetLayout and SetFont etc.
798 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
799 Paragraph const * endpar) const
802 Paragraph * tmppar = 0;
803 Paragraph * first_phys_par = 0;
805 Row * tmprow = cur.row();
807 int y = cur.y() - tmprow->baseline();
809 if (!tmprow->previous()) {
810 // a trick/hack for UNDO
811 // This is needed because in an UNDO/REDO we could have changed
812 // the ownerParagrah() so the paragraph inside the row is NOT
813 // my really first par anymore. Got it Lars ;) (Jug 20011206)
814 first_phys_par = ownerParagraph();
816 first_phys_par = tmprow->par();
817 while (tmprow->previous()
818 && tmprow->previous()->par() == first_phys_par)
820 tmprow = tmprow->previous();
821 y -= tmprow->height();
825 // we can set the refreshing parameters now
826 status(bview, LyXText::NEED_MORE_REFRESH);
828 refresh_row = tmprow->previous(); /* the real refresh row will
829 be deleted, so I store
833 tmppar = tmprow->next()->par();
836 while (tmprow->next() && tmppar != endpar) {
837 removeRow(tmprow->next());
838 if (tmprow->next()) {
839 tmppar = tmprow->next()->par();
845 // remove the first one
846 tmprow2 = tmprow; /* this is because tmprow->previous()
848 tmprow = tmprow->previous();
851 tmppar = first_phys_par;
855 insertParagraph(bview, tmppar, tmprow);
859 while (tmprow->next()
860 && tmprow->next()->par() == tmppar) {
861 tmprow = tmprow->next();
863 tmppar = tmppar->next();
865 } while (tmppar && tmppar != endpar);
867 // this is because of layout changes
869 refresh_y -= refresh_row->height();
870 setHeightOfRow(bview, refresh_row);
872 refresh_row = firstrow;
874 setHeightOfRow(bview, refresh_row);
877 if (tmprow && tmprow->next())
878 setHeightOfRow(bview, tmprow->next());
879 updateCounters(bview);
883 void LyXText::fullRebreak(BufferView * bview)
889 if (need_break_row) {
890 breakAgain(bview, need_break_row);
897 // important for the screen
900 // the cursor set functions have a special mechanism. When they
901 // realize, that you left an empty paragraph, they will delete it.
902 // They also delete the corresponding row
904 // need the selection cursor:
905 void LyXText::setSelection(BufferView * bview)
907 bool const lsel = selection.set();
909 if (!selection.set()) {
910 last_sel_cursor = selection.cursor;
911 selection.start = selection.cursor;
912 selection.end = selection.cursor;
917 // first the toggling area
918 if (cursor.y() < last_sel_cursor.y()
919 || (cursor.y() == last_sel_cursor.y()
920 && cursor.x() < last_sel_cursor.x())) {
921 toggle_end_cursor = last_sel_cursor;
922 toggle_cursor = cursor;
924 toggle_end_cursor = cursor;
925 toggle_cursor = last_sel_cursor;
928 last_sel_cursor = cursor;
930 // and now the whole selection
932 if (selection.cursor.par() == cursor.par())
933 if (selection.cursor.pos() < cursor.pos()) {
934 selection.end = cursor;
935 selection.start = selection.cursor;
937 selection.end = selection.cursor;
938 selection.start = cursor;
940 else if (selection.cursor.y() < cursor.y() ||
941 (selection.cursor.y() == cursor.y()
942 && selection.cursor.x() < cursor.x())) {
943 selection.end = cursor;
944 selection.start = selection.cursor;
947 selection.end = selection.cursor;
948 selection.start = cursor;
951 // a selection with no contents is not a selection
952 if (selection.start.par() == selection.end.par() &&
953 selection.start.pos() == selection.end.pos())
954 selection.set(false);
956 if (inset_owner && (selection.set() || lsel))
957 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
961 string const LyXText::selectionAsString(Buffer const * buffer,
964 if (!selection.set()) return string();
966 // should be const ...
967 Paragraph * startpar(selection.start.par());
968 Paragraph * endpar(selection.end.par());
969 pos_type const startpos(selection.start.pos());
970 pos_type const endpos(selection.end.pos());
972 if (startpar == endpar) {
973 return startpar->asString(buffer, startpos, endpos, label);
978 // First paragraph in selection
979 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
981 // The paragraphs in between (if any)
982 LyXCursor tmpcur(selection.start);
983 tmpcur.par(tmpcur.par()->next());
984 while (tmpcur.par() != endpar) {
985 result += tmpcur.par()->asString(buffer, 0,
986 tmpcur.par()->size(),
988 tmpcur.par(tmpcur.par()->next());
991 // Last paragraph in selection
992 result += endpar->asString(buffer, 0, endpos, label);
998 void LyXText::clearSelection() const
1000 selection.set(false);
1001 selection.mark(false);
1002 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1003 // reset this in the bv_owner!
1004 if (bv_owner && bv_owner->text)
1005 bv_owner->text->xsel_cache.set(false);
1009 void LyXText::cursorHome(BufferView * bview) const
1011 setCursor(bview, cursor.par(), cursor.row()->pos());
1015 void LyXText::cursorEnd(BufferView * bview) const
1017 if (!cursor.row()->next()
1018 || cursor.row()->next()->par() != cursor.row()->par()) {
1019 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1021 if (!cursor.par()->empty() &&
1022 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1023 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1024 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1026 setCursor(bview,cursor.par(),
1027 rowLast(cursor.row()) + 1);
1033 void LyXText::cursorTop(BufferView * bview) const
1035 while (cursor.par()->previous())
1036 cursor.par(cursor.par()->previous());
1037 setCursor(bview, cursor.par(), 0);
1041 void LyXText::cursorBottom(BufferView * bview) const
1043 while (cursor.par()->next())
1044 cursor.par(cursor.par()->next());
1045 setCursor(bview, cursor.par(), cursor.par()->size());
1049 void LyXText::toggleFree(BufferView * bview,
1050 LyXFont const & font, bool toggleall)
1052 // If the mask is completely neutral, tell user
1053 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1054 // Could only happen with user style
1055 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1059 // Try implicit word selection
1060 // If there is a change in the language the implicit word selection
1062 LyXCursor resetCursor = cursor;
1063 bool implicitSelection = (font.language() == ignore_language
1064 && font.number() == LyXFont::IGNORE)
1065 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1068 setFont(bview, font, toggleall);
1070 // Implicit selections are cleared afterwards
1071 //and cursor is set to the original position.
1072 if (implicitSelection) {
1074 cursor = resetCursor;
1075 setCursor(bview, cursor.par(), cursor.pos());
1076 selection.cursor = cursor;
1079 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1083 string LyXText::getStringToIndex(BufferView * bview)
1087 // Try implicit word selection
1088 // If there is a change in the language the implicit word selection
1090 LyXCursor const reset_cursor = cursor;
1091 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1093 if (!selection.set()) {
1094 bview->owner()->message(_("Nothing to index!"));
1097 if (selection.start.par() != selection.end.par()) {
1098 bview->owner()->message(_("Cannot index more than one paragraph!"));
1102 idxstring = selectionAsString(bview->buffer(), false);
1104 // Implicit selections are cleared afterwards
1105 //and cursor is set to the original position.
1106 if (implicitSelection) {
1108 cursor = reset_cursor;
1109 setCursor(bview, cursor.par(), cursor.pos());
1110 selection.cursor = cursor;
1116 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1117 Paragraph const * par) const
1119 if (par->layout()->labeltype != LABEL_MANUAL)
1122 return par->beginningOfMainBody();
1126 // the DTP switches for paragraphs. LyX will store them in the first
1127 // physicla paragraph. When a paragraph is broken, the top settings rest,
1128 // the bottom settings are given to the new one. So I can make shure,
1129 // they do not duplicate themself and you cannnot make dirty things with
1132 void LyXText::setParagraph(BufferView * bview,
1133 bool line_top, bool line_bottom,
1134 bool pagebreak_top, bool pagebreak_bottom,
1135 VSpace const & space_top,
1136 VSpace const & space_bottom,
1137 Spacing const & spacing,
1139 string labelwidthstring,
1142 LyXCursor tmpcursor = cursor;
1143 if (!selection.set()) {
1144 selection.start = cursor;
1145 selection.end = cursor;
1148 // make sure that the depth behind the selection are restored, too
1149 Paragraph * endpar = selection.end.par()->next();
1150 Paragraph * undoendpar = endpar;
1152 if (endpar && endpar->getDepth()) {
1153 while (endpar && endpar->getDepth()) {
1154 endpar = endpar->next();
1155 undoendpar = endpar;
1159 // because of parindents etc.
1160 endpar = endpar->next();
1163 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1166 Paragraph * tmppar = selection.end.par();
1168 while (tmppar != selection.start.par()->previous()) {
1169 setCursor(bview, tmppar, 0);
1170 status(bview, LyXText::NEED_MORE_REFRESH);
1171 refresh_row = cursor.row();
1172 refresh_y = cursor.y() - cursor.row()->baseline();
1173 cursor.par()->params().lineTop(line_top);
1174 cursor.par()->params().lineBottom(line_bottom);
1175 cursor.par()->params().pagebreakTop(pagebreak_top);
1176 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1177 cursor.par()->params().spaceTop(space_top);
1178 cursor.par()->params().spaceBottom(space_bottom);
1179 cursor.par()->params().spacing(spacing);
1180 // does the layout allow the new alignment?
1181 LyXLayout_ptr const & layout = cursor.par()->layout();
1183 if (align == LYX_ALIGN_LAYOUT)
1184 align = layout->align;
1185 if (align & layout->alignpossible) {
1186 if (align == layout->align)
1187 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1189 cursor.par()->params().align(align);
1191 cursor.par()->setLabelWidthString(labelwidthstring);
1192 cursor.par()->params().noindent(noindent);
1193 tmppar = cursor.par()->previous();
1196 redoParagraphs(bview, selection.start, endpar);
1199 setCursor(bview, selection.start.par(), selection.start.pos());
1200 selection.cursor = cursor;
1201 setCursor(bview, selection.end.par(), selection.end.pos());
1202 setSelection(bview);
1203 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1205 bview->updateInset(inset_owner, true);
1209 // set the counter of a paragraph. This includes the labels
1210 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1212 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1213 LyXLayout_ptr const & layout = par->layout();
1215 if (par->previous()) {
1217 par->params().appendix(par->previous()->params().appendix());
1218 if (!par->params().appendix() && par->params().startOfAppendix()) {
1219 par->params().appendix(true);
1220 textclass.counters().reset();
1222 par->enumdepth = par->previous()->enumdepth;
1223 par->itemdepth = par->previous()->itemdepth;
1225 par->params().appendix(par->params().startOfAppendix());
1230 /* Maybe we have to increment the enumeration depth.
1231 * BUT, enumeration in a footnote is considered in isolation from its
1232 * surrounding paragraph so don't increment if this is the
1233 * first line of the footnote
1234 * AND, bibliographies can't have their depth changed ie. they
1235 * are always of depth 0
1238 && par->previous()->getDepth() < par->getDepth()
1239 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1240 && par->enumdepth < 3
1241 && layout->labeltype != LABEL_BIBLIO) {
1245 // Maybe we have to decrement the enumeration depth, see note above
1247 && par->previous()->getDepth() > par->getDepth()
1248 && layout->labeltype != LABEL_BIBLIO) {
1249 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1252 if (!par->params().labelString().empty()) {
1253 par->params().labelString(string());
1256 if (layout->margintype == MARGIN_MANUAL) {
1257 if (par->params().labelWidthString().empty()) {
1258 par->setLabelWidthString(layout->labelstring());
1261 par->setLabelWidthString(string());
1264 // is it a layout that has an automatic label?
1265 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1267 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1272 if (i >= 0 && i <= buf->params.secnumdepth) {
1274 textclass.counters().step(layout->latexname());
1276 // Is there a label? Useful for Chapter layout
1277 if (!par->params().appendix()) {
1278 if (!layout->labelstring().empty())
1279 par->params().labelString(layout->labelstring());
1281 par->params().labelString(string());
1283 if (!layout->labelstring_appendix().empty())
1284 par->params().labelString(layout->labelstring_appendix());
1286 par->params().labelString(string());
1289 // Use if an integer is here less than elegant. For now.
1290 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1291 if (!par->params().appendix()) {
1292 numbertype = "sectioning";
1294 numbertype = "appendix";
1295 if (par->isRightToLeftPar(buf->params))
1296 langtype = "hebrew";
1301 s << textclass.counters()
1302 .numberLabel(layout->latexname(),
1303 numbertype, langtype, head);
1305 par->params().labelString(par->params().labelString() + s.str().c_str());
1306 // We really want to remove the c_str as soon as
1309 // reset enum counters
1310 textclass.counters().reset("enum");
1311 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1312 textclass.counters().reset("enum");
1313 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1315 // Yes I know this is a really, really! bad solution
1317 string enumcounter("enum");
1319 switch (par->enumdepth) {
1328 enumcounter += "iv";
1331 // not a valid enumdepth...
1335 textclass.counters().step(enumcounter);
1337 s << textclass.counters()
1338 .numberLabel(enumcounter,
1339 "enumeration", langtype);
1340 par->params().labelString(s.str().c_str());
1342 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1343 textclass.counters().step("bibitem");
1344 int number = textclass.counters().value("bibitem");
1346 InsetCommandParams p("bibitem");
1347 par->bibkey = new InsetBibKey(p);
1349 par->bibkey->setCounter(number);
1350 par->params().labelString(layout->labelstring());
1352 // In biblio should't be following counters but...
1354 string s = layout->labelstring();
1356 // the caption hack:
1357 if (layout->labeltype == LABEL_SENSITIVE) {
1358 Paragraph * tmppar = par;
1361 while (tmppar && tmppar->inInset()
1362 // the single '=' is intended below
1363 && (in = tmppar->inInset()->owner())) {
1364 if (in->lyxCode() == Inset::FLOAT_CODE) {
1368 tmppar = in->parOwner();
1374 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1376 textclass.counters().step(fl.type());
1378 // Doesn't work... yet.
1380 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1381 o << fl.name() << " #:";
1384 // par->SetLayout(0);
1385 // s = layout->labelstring;
1386 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1387 ? " :úåòîùî øñç" : "Senseless: ";
1390 par->params().labelString(s);
1392 // reset the enumeration counter. They are always reset
1393 // when there is any other layout between
1394 // Just fall-through between the cases so that all
1395 // enum counters deeper than enumdepth is also reset.
1396 switch (par->enumdepth) {
1398 textclass.counters().reset("enumi");
1400 textclass.counters().reset("enumii");
1402 textclass.counters().reset("enumiii");
1404 textclass.counters().reset("enumiv");
1410 // Updates all counters BEHIND the row. Changed paragraphs
1411 // with a dynamic left margin will be rebroken.
1412 void LyXText::updateCounters(BufferView * bview) const
1416 Row * row = firstrow;
1419 // CHECK if this is really needed. (Lgb)
1420 bview->buffer()->params.getLyXTextClass().counters().reset();
1423 while (row->par() != par)
1426 setCounter(bview->buffer(), par);
1428 // now check for the headline layouts. remember that they
1429 // have a dynamic left margin
1430 LyXLayout_ptr const & layout = par->layout();
1432 if (layout->margintype == MARGIN_DYNAMIC
1433 || layout->labeltype == LABEL_SENSITIVE) {
1434 // Rebreak the paragraph
1435 removeParagraph(row);
1436 appendParagraph(bview, row);
1443 void LyXText::insertInset(BufferView * bview, Inset * inset)
1445 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1447 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1449 cursor.par()->insertInset(cursor.pos(), inset);
1450 // Just to rebreak and refresh correctly.
1451 // The character will not be inserted a second time
1452 insertChar(bview, Paragraph::META_INSET);
1453 // If we enter a highly editable inset the cursor should be to before
1454 // the inset. This couldn't happen before as Undo was not handled inside
1455 // inset now after the Undo LyX tries to call inset->Edit(...) again
1456 // and cannot do this as the cursor is behind the inset and GetInset
1457 // does not return the inset!
1458 if (isHighlyEditableInset(inset)) {
1459 cursorLeft(bview, true);
1465 void LyXText::copyEnvironmentType()
1467 copylayouttype = cursor.par()->layout()->name();
1471 void LyXText::pasteEnvironmentType(BufferView * bview)
1473 setLayout(bview, copylayouttype);
1477 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1479 // Stuff what we got on the clipboard. Even if there is no selection.
1481 // There is a problem with having the stuffing here in that the
1482 // larger the selection the slower LyX will get. This can be
1483 // solved by running the line below only when the selection has
1484 // finished. The solution used currently just works, to make it
1485 // faster we need to be more clever and probably also have more
1486 // calls to stuffClipboard. (Lgb)
1487 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1489 // This doesn't make sense, if there is no selection
1490 if (!selection.set())
1493 // OK, we have a selection. This is always between selection.start
1494 // and selection.end
1496 // make sure that the depth behind the selection are restored, too
1497 Paragraph * endpar = selection.end.par()->next();
1498 Paragraph * undoendpar = endpar;
1500 if (endpar && endpar->getDepth()) {
1501 while (endpar && endpar->getDepth()) {
1502 endpar = endpar->next();
1503 undoendpar = endpar;
1505 } else if (endpar) {
1506 endpar = endpar->next(); // because of parindents etc.
1509 setUndo(bview, Undo::DELETE,
1510 selection.start.par(), undoendpar);
1512 // there are two cases: cut only within one paragraph or
1513 // more than one paragraph
1514 if (selection.start.par() == selection.end.par()) {
1515 // only within one paragraph
1516 endpar = selection.end.par();
1517 int pos = selection.end.pos();
1518 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1519 selection.start.pos(), pos,
1520 bview->buffer()->params.textclass,
1522 selection.end.pos(pos);
1524 endpar = selection.end.par();
1525 int pos = selection.end.pos();
1526 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1527 selection.start.pos(), pos,
1528 bview->buffer()->params.textclass,
1531 selection.end.par(endpar);
1532 selection.end.pos(pos);
1533 cursor.pos(selection.end.pos());
1535 endpar = endpar->next();
1537 // sometimes necessary
1539 selection.start.par()->stripLeadingSpaces();
1541 redoParagraphs(bview, selection.start, endpar);
1543 // cutSelection can invalidate the cursor so we need to set
1545 cursor = selection.start;
1547 // need a valid cursor. (Lgb)
1550 setCursor(bview, cursor.par(), cursor.pos());
1551 selection.cursor = cursor;
1552 updateCounters(bview);
1556 void LyXText::copySelection(BufferView * bview)
1558 // stuff the selection onto the X clipboard, from an explicit copy request
1559 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1561 // this doesnt make sense, if there is no selection
1562 if (!selection.set())
1565 // ok we have a selection. This is always between selection.start
1566 // and sel_end cursor
1568 // copy behind a space if there is one
1569 while (selection.start.par()->size() > selection.start.pos()
1570 && selection.start.par()->isLineSeparator(selection.start.pos())
1571 && (selection.start.par() != selection.end.par()
1572 || selection.start.pos() < selection.end.pos()))
1573 selection.start.pos(selection.start.pos() + 1);
1575 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1576 selection.start.pos(), selection.end.pos(),
1577 bview->buffer()->params.textclass);
1581 void LyXText::pasteSelection(BufferView * bview)
1583 // this does not make sense, if there is nothing to paste
1584 if (!CutAndPaste::checkPastePossible(cursor.par()))
1587 setUndo(bview, Undo::INSERT,
1588 cursor.par(), cursor.par()->next());
1591 Paragraph * actpar = cursor.par();
1592 int pos = cursor.pos();
1594 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1595 bview->buffer()->params.textclass);
1597 redoParagraphs(bview, cursor, endpar);
1599 setCursor(bview, cursor.par(), cursor.pos());
1602 selection.cursor = cursor;
1603 setCursor(bview, actpar, pos);
1604 setSelection(bview);
1605 updateCounters(bview);
1609 // sets the selection over the number of characters of string, no check!!
1610 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1615 selection.cursor = cursor;
1616 for (string::size_type i = 0; i < str.length(); ++i)
1618 setSelection(bview);
1622 // simple replacing. The font of the first selected character is used
1623 void LyXText::replaceSelectionWithString(BufferView * bview,
1626 setCursorParUndo(bview);
1629 if (!selection.set()) { // create a dummy selection
1630 selection.end = cursor;
1631 selection.start = cursor;
1634 // Get font setting before we cut
1635 pos_type pos = selection.end.pos();
1636 LyXFont const font = selection.start.par()
1637 ->getFontSettings(bview->buffer()->params,
1638 selection.start.pos());
1640 // Insert the new string
1641 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1642 selection.end.par()->insertChar(pos, (*cit), font);
1646 // Cut the selection
1647 cutSelection(bview, true, false);
1653 // needed to insert the selection
1654 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1656 Paragraph * par = cursor.par();
1657 pos_type pos = cursor.pos();
1658 Paragraph * endpar = cursor.par()->next();
1660 setCursorParUndo(bview);
1662 // only to be sure, should not be neccessary
1665 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1667 redoParagraphs(bview, cursor, endpar);
1668 setCursor(bview, cursor.par(), cursor.pos());
1669 selection.cursor = cursor;
1670 setCursor(bview, par, pos);
1671 setSelection(bview);
1675 // turns double-CR to single CR, others where converted into one
1676 // blank. Then InsertStringAsLines is called
1677 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1679 string linestr(str);
1680 bool newline_inserted = false;
1681 for (string::size_type i = 0; i < linestr.length(); ++i) {
1682 if (linestr[i] == '\n') {
1683 if (newline_inserted) {
1684 // we know that \r will be ignored by
1685 // InsertStringA. Of course, it is a dirty
1686 // trick, but it works...
1687 linestr[i - 1] = '\r';
1691 newline_inserted = true;
1693 } else if (IsPrintable(linestr[i])) {
1694 newline_inserted = false;
1697 insertStringAsLines(bview, linestr);
1701 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1704 LyXCursor tmpcursor;
1708 Row * row = getRow(par, pos, y);
1710 // is there a break one row above
1711 if (row->previous() && row->previous()->par() == row->par()) {
1712 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1713 if (z >= row->pos()) {
1714 // set the dimensions of the row above
1715 y -= row->previous()->height();
1717 refresh_row = row->previous();
1718 status(bview, LyXText::NEED_MORE_REFRESH);
1720 breakAgain(bview, row->previous());
1722 // set the cursor again. Otherwise
1723 // dangling pointers are possible
1724 setCursor(bview, cursor.par(), cursor.pos(),
1725 false, cursor.boundary());
1726 selection.cursor = cursor;
1731 int const tmpheight = row->height();
1732 pos_type const tmplast = rowLast(row);
1736 breakAgain(bview, row);
1737 if (row->height() == tmpheight && rowLast(row) == tmplast)
1738 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1740 status(bview, LyXText::NEED_MORE_REFRESH);
1742 // check the special right address boxes
1743 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1750 redoDrawingOfParagraph(bview, tmpcursor);
1753 // set the cursor again. Otherwise dangling pointers are possible
1754 // also set the selection
1756 if (selection.set()) {
1758 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1759 false, selection.cursor.boundary());
1760 selection.cursor = cursor;
1761 setCursorIntern(bview, selection.start.par(),
1762 selection.start.pos(),
1763 false, selection.start.boundary());
1764 selection.start = cursor;
1765 setCursorIntern(bview, selection.end.par(),
1766 selection.end.pos(),
1767 false, selection.end.boundary());
1768 selection.end = cursor;
1769 setCursorIntern(bview, last_sel_cursor.par(),
1770 last_sel_cursor.pos(),
1771 false, last_sel_cursor.boundary());
1772 last_sel_cursor = cursor;
1775 setCursorIntern(bview, cursor.par(), cursor.pos(),
1776 false, cursor.boundary());
1780 // returns false if inset wasn't found
1781 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1783 // first check the current paragraph
1784 int pos = cursor.par()->getPositionOfInset(inset);
1786 checkParagraph(bview, cursor.par(), pos);
1790 // check every paragraph
1792 Paragraph * par = ownerParagraph();
1794 pos = par->getPositionOfInset(inset);
1796 checkParagraph(bview, par, pos);
1806 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1808 bool setfont, bool boundary) const
1810 LyXCursor old_cursor = cursor;
1811 setCursorIntern(bview, par, pos, setfont, boundary);
1812 return deleteEmptyParagraphMechanism(bview, old_cursor);
1816 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1817 pos_type pos, bool boundary) const
1824 cur.boundary(boundary);
1826 // get the cursor y position in text
1828 Row * row = getRow(par, pos, y);
1829 Row * old_row = row;
1831 // if we are before the first char of this row and are still in the
1832 // same paragraph and there is a previous row then put the cursor on
1833 // the end of the previous row
1834 cur.iy(y + row->baseline());
1836 if (row->previous() && pos &&
1837 row->previous()->par() == row->par() &&
1838 par->getChar(pos) == Paragraph::META_INSET &&
1839 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1841 row = row->previous();
1846 // y is now the beginning of the cursor row
1847 y += row->baseline();
1848 // y is now the cursor baseline
1851 pos_type last = rowLastPrintable(old_row);
1853 if (pos > last + 1) {
1854 // This shouldn't happen.
1857 } else if (pos < row->pos()) {
1862 // now get the cursors x position
1863 float x = getCursorX(bview, row, pos, last, boundary);
1866 if (old_row != row) {
1867 x = getCursorX(bview, old_row, pos, last, boundary);
1874 float LyXText::getCursorX(BufferView * bview, Row * row,
1875 pos_type pos, pos_type last, bool boundary) const
1877 pos_type cursor_vpos = 0;
1879 float fill_separator;
1881 float fill_label_hfill;
1882 // This call HAS to be here because of the BidiTables!!!
1883 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1886 if (last < row->pos())
1887 cursor_vpos = row->pos();
1888 else if (pos > last && !boundary)
1889 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1890 ? row->pos() : last + 1;
1891 else if (pos > row->pos() &&
1892 (pos > last || boundary))
1893 /// Place cursor after char at (logical) position pos - 1
1894 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1895 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1897 /// Place cursor before char at (logical) position pos
1898 cursor_vpos = (bidi_level(pos) % 2 == 0)
1899 ? log2vis(pos) : log2vis(pos) + 1;
1901 pos_type main_body =
1902 beginningOfMainBody(bview->buffer(), row->par());
1903 if ((main_body > 0) &&
1904 ((main_body-1 > last) ||
1905 !row->par()->isLineSeparator(main_body-1)))
1908 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1909 pos_type pos = vis2log(vpos);
1910 if (main_body > 0 && pos == main_body - 1) {
1911 x += fill_label_hfill +
1912 font_metrics::width(
1913 row->par()->layout()->labelsep,
1914 getLabelFont(bview->buffer(),
1916 if (row->par()->isLineSeparator(main_body - 1))
1917 x -= singleWidth(bview,
1918 row->par(), main_body - 1);
1920 if (hfillExpansion(bview->buffer(), row, pos)) {
1921 x += singleWidth(bview, row->par(), pos);
1922 if (pos >= main_body)
1925 x += fill_label_hfill;
1926 } else if (row->par()->isSeparator(pos)) {
1927 x += singleWidth(bview, row->par(), pos);
1928 if (pos >= main_body)
1929 x += fill_separator;
1931 x += singleWidth(bview, row->par(), pos);
1937 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1938 pos_type pos, bool setfont, bool boundary) const
1940 InsetText * it = static_cast<InsetText *>(par->inInset());
1942 if (it != inset_owner) {
1943 lyxerr[Debug::INSETS] << "InsetText is " << it
1945 << "inset_owner is "
1946 << inset_owner << endl;
1947 #ifdef WITH_WARNINGS
1948 #warning I believe this code is wrong. (Lgb)
1949 #warning Jürgen, have a look at this. (Lgb)
1950 #warning Hmmm, I guess you are right but we
1951 #warning should verify when this is needed
1953 // Jürgen, would you like to have a look?
1954 // I guess we need to move the outer cursor
1955 // and open and lock the inset (bla bla bla)
1956 // stuff I don't know... so can you have a look?
1958 // I moved the lyxerr stuff in here so we can see if
1959 // this is actually really needed and where!
1961 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1966 setCursor(bview, cursor, par, pos, boundary);
1968 setCurrentFont(bview);
1972 void LyXText::setCurrentFont(BufferView * bview) const
1974 pos_type pos = cursor.pos();
1975 if (cursor.boundary() && pos > 0)
1979 if (pos == cursor.par()->size())
1981 else // potentional bug... BUG (Lgb)
1982 if (cursor.par()->isSeparator(pos)) {
1983 if (pos > cursor.row()->pos() &&
1984 bidi_level(pos) % 2 ==
1985 bidi_level(pos - 1) % 2)
1987 else if (pos + 1 < cursor.par()->size())
1993 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1994 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1996 if (cursor.pos() == cursor.par()->size() &&
1997 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1998 !cursor.boundary()) {
1999 Language const * lang =
2000 cursor.par()->getParLanguage(bview->buffer()->params);
2001 current_font.setLanguage(lang);
2002 current_font.setNumber(LyXFont::OFF);
2003 real_current_font.setLanguage(lang);
2004 real_current_font.setNumber(LyXFont::OFF);
2009 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2011 LyXCursor old_cursor = cursor;
2013 setCursorFromCoordinates(bview, cursor, x, y);
2014 setCurrentFont(bview);
2015 deleteEmptyParagraphMechanism(bview, old_cursor);
2022 * return true if the cursor given is at the end of a row,
2023 * and the next row is filled by an inset that spans an entire
2026 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2029 Row const & next = *row.next();
2031 if (next.pos() != cur.pos() || next.par() != cur.par())
2033 if (!cur.par()->isInset(cur.pos()))
2035 Inset const * inset = cur.par()->getInset(cur.pos());
2036 if (inset->needFullRow() || inset->display())
2043 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2046 // Get the row first.
2048 Row * row = getRowNearY(y);
2050 pos_type const column = getColumnNearX(bview, row, x, bound);
2051 cur.par(row->par());
2052 cur.pos(row->pos() + column);
2054 cur.y(y + row->baseline());
2057 if (beforeFullRowInset(*row, cur)) {
2058 pos_type last = rowLastPrintable(row);
2059 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2061 cur.iy(y + row->height() + row->next()->baseline());
2062 cur.irow(row->next());
2068 cur.boundary(bound);
2072 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2074 if (cursor.pos() > 0) {
2075 bool boundary = cursor.boundary();
2076 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2077 if (!internal && !boundary &&
2078 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2079 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2080 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2081 Paragraph * par = cursor.par()->previous();
2082 setCursor(bview, par, par->size());
2087 void LyXText::cursorRight(BufferView * bview, bool internal) const
2089 if (!internal && cursor.boundary() &&
2090 !cursor.par()->isNewline(cursor.pos()))
2091 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2092 else if (cursor.pos() < cursor.par()->size()) {
2093 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2095 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2096 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2097 } else if (cursor.par()->next())
2098 setCursor(bview, cursor.par()->next(), 0);
2102 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2105 int x = cursor.x_fix();
2106 int y = cursor.y() - cursor.row()->baseline() - 1;
2107 setCursorFromCoordinates(bview, x, y);
2109 int y1 = cursor.iy() - first_y;
2112 Inset * inset_hit = checkInsetHit(bview, x, y1);
2113 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2114 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2118 setCursorFromCoordinates(bview, cursor.x_fix(),
2119 cursor.y() - cursor.row()->baseline() - 1);
2124 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2127 int x = cursor.x_fix();
2128 int y = cursor.y() - cursor.row()->baseline() +
2129 cursor.row()->height() + 1;
2130 setCursorFromCoordinates(bview, x, y);
2131 if (!selecting && cursor.row() == cursor.irow()) {
2132 int y1 = cursor.iy() - first_y;
2135 Inset * inset_hit = checkInsetHit(bview, x, y1);
2136 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2137 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2141 setCursorFromCoordinates(bview, cursor.x_fix(),
2142 cursor.y() - cursor.row()->baseline()
2143 + cursor.row()->height() + 1);
2148 void LyXText::cursorUpParagraph(BufferView * bview) const
2150 if (cursor.pos() > 0) {
2151 setCursor(bview, cursor.par(), 0);
2153 else if (cursor.par()->previous()) {
2154 setCursor(bview, cursor.par()->previous(), 0);
2159 void LyXText::cursorDownParagraph(BufferView * bview) const
2161 if (cursor.par()->next()) {
2162 setCursor(bview, cursor.par()->next(), 0);
2164 setCursor(bview, cursor.par(), cursor.par()->size());
2168 // fix the cursor `cur' after a characters has been deleted at `where'
2169 // position. Called by deleteEmptyParagraphMechanism
2170 void LyXText::fixCursorAfterDelete(BufferView * bview,
2172 LyXCursor const & where) const
2174 // if cursor is not in the paragraph where the delete occured,
2176 if (cur.par() != where.par())
2179 // if cursor position is after the place where the delete occured,
2181 if (cur.pos() > where.pos())
2182 cur.pos(cur.pos()-1);
2184 // check also if we don't want to set the cursor on a spot behind the
2185 // pagragraph because we erased the last character.
2186 if (cur.pos() > cur.par()->size())
2187 cur.pos(cur.par()->size());
2189 // recompute row et al. for this cursor
2190 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2194 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2195 LyXCursor const & old_cursor) const
2197 // Would be wrong to delete anything if we have a selection.
2198 if (selection.set())
2201 // We allow all kinds of "mumbo-jumbo" when freespacing.
2202 if (old_cursor.par()->layout()->free_spacing
2203 || old_cursor.par()->isFreeSpacing()) {
2207 /* Ok I'll put some comments here about what is missing.
2208 I have fixed BackSpace (and thus Delete) to not delete
2209 double-spaces automagically. I have also changed Cut,
2210 Copy and Paste to hopefully do some sensible things.
2211 There are still some small problems that can lead to
2212 double spaces stored in the document file or space at
2213 the beginning of paragraphs. This happens if you have
2214 the cursor betwenn to spaces and then save. Or if you
2215 cut and paste and the selection have a space at the
2216 beginning and then save right after the paste. I am
2217 sure none of these are very hard to fix, but I will
2218 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2219 that I can get some feedback. (Lgb)
2222 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2223 // delete the LineSeparator.
2226 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2227 // delete the LineSeparator.
2230 // If the pos around the old_cursor were spaces, delete one of them.
2231 if (old_cursor.par() != cursor.par()
2232 || old_cursor.pos() != cursor.pos()) {
2233 // Only if the cursor has really moved
2235 if (old_cursor.pos() > 0
2236 && old_cursor.pos() < old_cursor.par()->size()
2237 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2238 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2239 old_cursor.par()->erase(old_cursor.pos() - 1);
2240 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2242 #ifdef WITH_WARNINGS
2243 #warning This will not work anymore when we have multiple views of the same buffer
2244 // In this case, we will have to correct also the cursors held by
2245 // other bufferviews. It will probably be easier to do that in a more
2246 // automated way in LyXCursor code. (JMarc 26/09/2001)
2248 // correct all cursors held by the LyXText
2249 fixCursorAfterDelete(bview, cursor, old_cursor);
2250 fixCursorAfterDelete(bview, selection.cursor,
2252 fixCursorAfterDelete(bview, selection.start,
2254 fixCursorAfterDelete(bview, selection.end, old_cursor);
2255 fixCursorAfterDelete(bview, last_sel_cursor,
2257 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2258 fixCursorAfterDelete(bview, toggle_end_cursor,
2264 // don't delete anything if this is the ONLY paragraph!
2265 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2268 // Do not delete empty paragraphs with keepempty set.
2269 if (old_cursor.par()->layout()->keepempty)
2272 // only do our magic if we changed paragraph
2273 if (old_cursor.par() == cursor.par())
2276 // record if we have deleted a paragraph
2277 // we can't possibly have deleted a paragraph before this point
2278 bool deleted = false;
2280 if ((old_cursor.par()->empty()
2281 || (old_cursor.par()->size() == 1
2282 && old_cursor.par()->isLineSeparator(0)))) {
2283 // ok, we will delete anything
2284 LyXCursor tmpcursor;
2286 // make sure that you do not delete any environments
2287 status(bview, LyXText::NEED_MORE_REFRESH);
2290 if (old_cursor.row()->previous()) {
2291 refresh_row = old_cursor.row()->previous();
2292 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2294 cursor = old_cursor; // that undo can restore the right cursor position
2295 Paragraph * endpar = old_cursor.par()->next();
2296 if (endpar && endpar->getDepth()) {
2297 while (endpar && endpar->getDepth()) {
2298 endpar = endpar->next();
2301 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2305 removeRow(old_cursor.row());
2306 if (ownerParagraph() == old_cursor.par()) {
2307 ownerParagraph(ownerParagraph()->next());
2310 delete old_cursor.par();
2312 /* Breakagain the next par. Needed because of
2313 * the parindent that can occur or dissappear.
2314 * The next row can change its height, if
2315 * there is another layout before */
2316 if (refresh_row->next()) {
2317 breakAgain(bview, refresh_row->next());
2318 updateCounters(bview);
2320 setHeightOfRow(bview, refresh_row);
2322 refresh_row = old_cursor.row()->next();
2323 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2326 cursor = old_cursor; // that undo can restore the right cursor position
2327 Paragraph * endpar = old_cursor.par()->next();
2328 if (endpar && endpar->getDepth()) {
2329 while (endpar && endpar->getDepth()) {
2330 endpar = endpar->next();
2333 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2337 removeRow(old_cursor.row());
2339 if (ownerParagraph() == old_cursor.par()) {
2340 ownerParagraph(ownerParagraph()->next());
2343 delete old_cursor.par();
2345 /* Breakagain the next par. Needed because of
2346 the parindent that can occur or dissappear.
2347 The next row can change its height, if
2348 there is another layout before */
2350 breakAgain(bview, refresh_row);
2351 updateCounters(bview);
2356 setCursorIntern(bview, cursor.par(), cursor.pos());
2358 if (selection.cursor.par() == old_cursor.par()
2359 && selection.cursor.pos() == old_cursor.pos()) {
2360 // correct selection
2361 selection.cursor = cursor;
2365 if (old_cursor.par()->stripLeadingSpaces()) {
2366 redoParagraphs(bview, old_cursor,
2367 old_cursor.par()->next());
2369 setCursorIntern(bview, cursor.par(), cursor.pos());
2370 selection.cursor = cursor;
2377 Paragraph * LyXText::ownerParagraph() const
2380 return inset_owner->paragraph();
2382 return &*(bv_owner->buffer()->paragraphs.begin());
2386 void LyXText::ownerParagraph(Paragraph * p) const
2389 inset_owner->paragraph(p);
2391 bv_owner->buffer()->paragraphs.set(p);
2396 void LyXText::ownerParagraph(int id, Paragraph * p) const
2398 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2399 if (op && op->inInset()) {
2400 static_cast<InsetText *>(op->inInset())->paragraph(p);
2407 LyXText::text_status LyXText::status() const
2413 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2415 LyXText * t = bview->text;
2417 // We should only go up with refreshing code so this means that if
2418 // we have a MORE refresh we should never set it to LITTLE if we still
2419 // didn't handle it (and then it will be UNCHANGED. Now as long as
2420 // we stay inside one LyXText this may work but we need to tell the
2421 // outermost LyXText that it should REALLY draw us if there is some
2422 // change in a Inset::LyXText. So you see that when we are inside a
2423 // inset's LyXText we give the LITTLE to the outermost LyXText to
2424 // tell'em that it should redraw the actual row (where the inset
2425 // resides! Capito?!
2427 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2429 if (inset_owner && st != UNCHANGED) {
2430 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2431 if (!t->refresh_row) {
2432 t->refresh_row = t->cursor.row();
2433 t->refresh_y = t->cursor.y() -
2434 t->cursor.row()->baseline();