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 tmpfont.realize(par->layout()->font);
133 par_depth = par->getDepth();
137 tmpfont.realize(tclass.defaultfont());
145 // Gets the fully instantiated font at a given position in a paragraph
146 // Basically the same routine as Paragraph::getFont() in paragraph.C.
147 // The difference is that this one is used for displaying, and thus we
148 // are allowed to make cosmetic improvements. For instance make footnotes
150 // If position is -1, we get the layout font of the paragraph.
151 // If position is -2, we get the font of the manual label of the paragraph.
152 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
155 lyx::Assert(pos >= 0);
157 LyXLayout_ptr const & layout = par->layout();
159 // We specialize the 95% common case:
160 if (!par->getDepth()) {
161 if (layout->labeltype == LABEL_MANUAL
162 && pos < beginningOfMainBody(buf, par)) {
164 LyXFont f = par->getFontSettings(buf->params, pos);
166 par->inInset()->getDrawFont(f);
167 return f.realize(layout->reslabelfont);
169 LyXFont f = par->getFontSettings(buf->params, pos);
171 par->inInset()->getDrawFont(f);
172 return f.realize(layout->resfont);
176 // The uncommon case need not be optimized as much
180 if (pos < beginningOfMainBody(buf, par)) {
182 layoutfont = layout->labelfont;
185 layoutfont = layout->font;
188 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
189 tmpfont.realize(layoutfont);
192 par->inInset()->getDrawFont(tmpfont);
194 return realizeFont(tmpfont, buf, par);
198 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
200 LyXLayout_ptr const & layout = par->layout();
202 if (!par->getDepth()) {
203 return layout->resfont;
206 return realizeFont(layout->font, buf, par);
210 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
212 LyXLayout_ptr const & layout = par->layout();
214 if (!par->getDepth()) {
215 return layout->reslabelfont;
218 return realizeFont(layout->labelfont, buf, par);
222 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
223 pos_type pos, LyXFont const & fnt,
226 Buffer const * buf = bv->buffer();
227 LyXFont font = getFont(buf, par, pos);
228 font.update(fnt, buf->params.language, toggleall);
229 // Let the insets convert their font
230 if (par->isInset(pos)) {
231 Inset * inset = par->getInset(pos);
232 if (isEditableInset(inset)) {
233 UpdatableInset * uinset =
234 static_cast<UpdatableInset *>(inset);
235 uinset->setFont(bv, fnt, toggleall, true);
239 // Plug thru to version below:
240 setCharFont(buf, par, pos, font);
244 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
245 pos_type pos, LyXFont const & fnt)
249 LyXTextClass const & tclass = buf->params.getLyXTextClass();
250 LyXLayout_ptr const & layout = par->layout();
252 // Get concrete layout font to reduce against
255 if (pos < beginningOfMainBody(buf, par))
256 layoutfont = layout->labelfont;
258 layoutfont = layout->font;
260 // Realize against environment font information
261 if (par->getDepth()) {
262 Paragraph * tp = par;
263 while (!layoutfont.resolved() && tp && tp->getDepth()) {
264 tp = tp->outerHook();
266 layoutfont.realize(tp->layout()->font);
270 layoutfont.realize(tclass.defaultfont());
272 // Now, reduce font against full layout font
273 font.reduce(layoutfont);
275 par->setFont(pos, font);
279 // inserts a new row behind the specified row, increments
280 // the touched counters
281 void LyXText::insertRow(Row * row, Paragraph * par,
284 Row * tmprow = new Row;
287 tmprow->next(firstrow);
290 tmprow->previous(row);
291 tmprow->next(row->next());
296 tmprow->next()->previous(tmprow);
298 if (tmprow->previous())
299 tmprow->previous()->next(tmprow);
310 // removes the row and reset the touched counters
311 void LyXText::removeRow(Row * row) const
313 Row * row_prev = row->previous();
315 row->next()->previous(row_prev);
317 firstrow = row->next();
318 // lyx::Assert(firstrow);
320 row_prev->next(row->next());
322 if (row == lastrow) {
323 lyx::Assert(!row->next());
326 if (refresh_row == row) {
327 refresh_row = row_prev ? row_prev : row->next();
328 // what about refresh_y, refresh_height
331 height -= row->height(); // the text becomes smaller
337 // remove all following rows of the paragraph of the specified row.
338 void LyXText::removeParagraph(Row * row) const
340 Paragraph * tmppar = row->par();
344 while (row && row->par() == tmppar) {
345 tmprow = row->next();
352 // insert the specified paragraph behind the specified row
353 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
356 // insert a new row, starting at position 0
357 insertRow(row, par, 0);
359 // and now append the whole paragraph behind the new row
362 appendParagraph(bview, firstrow);
364 row->next()->height(0);
365 appendParagraph(bview, row->next());
370 Inset * LyXText::getInset() const
373 if (cursor.pos() == 0 && cursor.par()->bibkey) {
374 inset = cursor.par()->bibkey;
375 } else if (cursor.pos() < cursor.par()->size()
376 && cursor.par()->isInset(cursor.pos())) {
377 inset = cursor.par()->getInset(cursor.pos());
383 void LyXText::toggleInset(BufferView * bview)
385 Inset * inset = getInset();
386 // is there an editable inset at cursor position?
387 if (!isEditableInset(inset)) {
388 // No, try to see if we are inside a collapsable inset
389 if (inset_owner && inset_owner->owner()
390 && inset_owner->owner()->isOpen()) {
391 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
392 inset_owner->owner()->close(bview);
393 bview->getLyXText()->cursorRight(bview);
397 //bview->owner()->message(inset->editMessage());
399 // do we want to keep this?? (JMarc)
400 if (!isHighlyEditableInset(inset))
401 setCursorParUndo(bview);
403 if (inset->isOpen()) {
409 inset->open(bview, !inset->isOpen());
414 /* used in setlayout */
415 // Asger is not sure we want to do this...
416 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
419 LyXLayout_ptr const & layout = par->layout();
422 for (pos_type pos = 0; pos < par->size(); ++pos) {
423 if (pos < beginningOfMainBody(buf, par))
424 layoutfont = layout->labelfont;
426 layoutfont = layout->font;
428 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
429 tmpfont.reduce(layoutfont);
430 par->setFont(pos, tmpfont);
435 Paragraph * LyXText::setLayout(BufferView * bview,
436 LyXCursor & cur, LyXCursor & sstart_cur,
437 LyXCursor & send_cur,
438 string const & layout)
440 Paragraph * endpar = send_cur.par()->next();
441 Paragraph * undoendpar = endpar;
443 if (endpar && endpar->getDepth()) {
444 while (endpar && endpar->getDepth()) {
445 endpar = endpar->next();
449 endpar = endpar->next(); // because of parindents etc.
452 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
454 // ok we have a selection. This is always between sstart_cur
455 // and sel_end cursor
457 Paragraph * par = sstart_cur.par();
458 Paragraph * epar = send_cur.par()->next();
460 LyXLayout_ptr const & lyxlayout =
461 bview->buffer()->params.getLyXTextClass()[layout];
464 par->applyLayout(lyxlayout);
465 makeFontEntriesLayoutSpecific(bview->buffer(), par);
466 Paragraph * fppar = par;
467 fppar->params().spaceTop(lyxlayout->fill_top ?
468 VSpace(VSpace::VFILL)
469 : VSpace(VSpace::NONE));
470 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
471 VSpace(VSpace::VFILL)
472 : VSpace(VSpace::NONE));
473 if (lyxlayout->margintype == MARGIN_MANUAL)
474 par->setLabelWidthString(lyxlayout->labelstring());
475 if (lyxlayout->labeltype != LABEL_BIBLIO
477 delete fppar->bibkey;
482 } while (par != epar);
488 // set layout over selection and make a total rebreak of those paragraphs
489 void LyXText::setLayout(BufferView * bview, string const & layout)
491 LyXCursor tmpcursor = cursor; /* store the current cursor */
493 // if there is no selection just set the layout
494 // of the current paragraph */
495 if (!selection.set()) {
496 selection.start = cursor; // dummy selection
497 selection.end = cursor;
499 Paragraph * endpar = setLayout(bview, cursor, selection.start,
500 selection.end, layout);
501 redoParagraphs(bview, selection.start, endpar);
503 // we have to reset the selection, because the
504 // geometry could have changed
505 setCursor(bview, selection.start.par(),
506 selection.start.pos(), false);
507 selection.cursor = cursor;
508 setCursor(bview, selection.end.par(), selection.end.pos(), false);
509 updateCounters(bview);
512 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
516 // increment depth over selection and
517 // make a total rebreak of those paragraphs
518 void LyXText::incDepth(BufferView * bview)
520 // If there is no selection, just use the current paragraph
521 if (!selection.set()) {
522 selection.start = cursor; // dummy selection
523 selection.end = cursor;
526 // We end at the next paragraph with depth 0
527 Paragraph * endpar = selection.end.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 selection.start.par(), undoendpar);
543 LyXCursor tmpcursor = cursor; // store the current cursor
545 // ok we have a selection. This is always between sel_start_cursor
546 // and sel_end cursor
547 cursor = selection.start;
550 // NOTE: you can't change the depth of a bibliography entry
551 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
552 Paragraph * prev = cursor.par()->previous();
555 if (cursor.par()->getDepth()
556 < prev->getMaxDepthAfter()) {
557 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
561 if (cursor.par() == selection.end.par())
563 cursor.par(cursor.par()->next());
566 redoParagraphs(bview, selection.start, endpar);
568 // we have to reset the selection, because the
569 // geometry could have changed
570 setCursor(bview, selection.start.par(), selection.start.pos());
571 selection.cursor = cursor;
572 setCursor(bview, selection.end.par(), selection.end.pos());
573 updateCounters(bview);
576 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
580 // decrement depth over selection and
581 // make a total rebreak of those paragraphs
582 void LyXText::decDepth(BufferView * bview)
584 // if there is no selection just set the layout
585 // of the current paragraph
586 if (!selection.set()) {
587 selection.start = cursor; // dummy selection
588 selection.end = cursor;
590 Paragraph * endpar = selection.end.par()->next();
591 Paragraph * undoendpar = endpar;
593 if (endpar && endpar->getDepth()) {
594 while (endpar && endpar->getDepth()) {
595 endpar = endpar->next();
599 endpar = endpar->next(); // because of parindents etc.
602 setUndo(bview, Undo::EDIT,
603 selection.start.par(), undoendpar);
605 LyXCursor tmpcursor = cursor; // store the current cursor
607 // ok we have a selection. This is always between sel_start_cursor
608 // and sel_end cursor
609 cursor = selection.start;
612 if (cursor.par()->params().depth()) {
613 cursor.par()->params()
614 .depth(cursor.par()->params().depth() - 1);
616 if (cursor.par() == selection.end.par()) {
619 cursor.par(cursor.par()->next());
622 redoParagraphs(bview, selection.start, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 setCursor(bview, selection.start.par(),
627 selection.start.pos());
628 selection.cursor = cursor;
629 setCursor(bview, selection.end.par(), selection.end.pos());
630 updateCounters(bview);
633 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
637 // set font over selection and make a total rebreak of those paragraphs
638 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
640 // if there is no selection just set the current_font
641 if (!selection.set()) {
642 // Determine basis font
644 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
646 layoutfont = getLabelFont(bview->buffer(),
649 layoutfont = getLayoutFont(bview->buffer(),
652 // Update current font
653 real_current_font.update(font,
654 bview->buffer()->params.language,
657 // Reduce to implicit settings
658 current_font = real_current_font;
659 current_font.reduce(layoutfont);
660 // And resolve it completely
661 real_current_font.realize(layoutfont);
666 LyXCursor tmpcursor = cursor; // store the current cursor
668 // ok we have a selection. This is always between sel_start_cursor
669 // and sel_end cursor
671 setUndo(bview, Undo::EDIT,
672 selection.start.par(), selection.end.par()->next());
674 cursor = selection.start;
675 while (cursor.par() != selection.end.par() ||
676 cursor.pos() < selection.end.pos())
678 if (cursor.pos() < cursor.par()->size()) {
679 // an open footnote should behave like a closed one
680 setCharFont(bview, cursor.par(), cursor.pos(),
682 cursor.pos(cursor.pos() + 1);
685 cursor.par(cursor.par()->next());
690 redoParagraphs(bview, selection.start, selection.end.par()->next());
692 // we have to reset the selection, because the
693 // geometry could have changed, but we keep
694 // it for user convenience
695 setCursor(bview, selection.start.par(), selection.start.pos());
696 selection.cursor = cursor;
697 setCursor(bview, selection.end.par(), selection.end.pos());
699 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
700 tmpcursor.boundary());
704 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
706 Row * tmprow = cur.row();
707 int y = cur.y() - tmprow->baseline();
709 setHeightOfRow(bview, tmprow);
711 while (tmprow->previous()
712 && tmprow->previous()->par() == tmprow->par()) {
713 tmprow = tmprow->previous();
714 y -= tmprow->height();
715 setHeightOfRow(bview, tmprow);
718 // we can set the refreshing parameters now
719 status(bview, LyXText::NEED_MORE_REFRESH);
721 refresh_row = tmprow;
722 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
726 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
728 Row * tmprow = cur.row();
730 int y = cur.y() - tmprow->baseline();
731 setHeightOfRow(bview, tmprow);
733 while (tmprow->previous()
734 && tmprow->previous()->par() == tmprow->par()) {
735 tmprow = tmprow->previous();
736 y -= tmprow->height();
739 // we can set the refreshing parameters now
740 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
742 refresh_row = tmprow;
744 status(bview, LyXText::NEED_MORE_REFRESH);
745 setCursor(bview, cur.par(), cur.pos());
749 // deletes and inserts again all paragaphs between the cursor
750 // and the specified par
751 // This function is needed after SetLayout and SetFont etc.
752 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
753 Paragraph const * endpar) const
756 Paragraph * tmppar = 0;
757 Paragraph * first_phys_par = 0;
759 Row * tmprow = cur.row();
761 int y = cur.y() - tmprow->baseline();
763 if (!tmprow->previous()) {
764 // a trick/hack for UNDO
765 // This is needed because in an UNDO/REDO we could have changed
766 // the ownerParagrah() so the paragraph inside the row is NOT
767 // my really first par anymore. Got it Lars ;) (Jug 20011206)
768 first_phys_par = ownerParagraph();
770 first_phys_par = tmprow->par();
771 while (tmprow->previous()
772 && tmprow->previous()->par() == first_phys_par)
774 tmprow = tmprow->previous();
775 y -= tmprow->height();
779 // we can set the refreshing parameters now
780 status(bview, LyXText::NEED_MORE_REFRESH);
782 refresh_row = tmprow->previous(); /* the real refresh row will
783 be deleted, so I store
787 tmppar = tmprow->next()->par();
790 while (tmprow->next() && tmppar != endpar) {
791 removeRow(tmprow->next());
792 if (tmprow->next()) {
793 tmppar = tmprow->next()->par();
799 // remove the first one
800 tmprow2 = tmprow; /* this is because tmprow->previous()
802 tmprow = tmprow->previous();
805 tmppar = first_phys_par;
809 insertParagraph(bview, tmppar, tmprow);
813 while (tmprow->next()
814 && tmprow->next()->par() == tmppar) {
815 tmprow = tmprow->next();
817 tmppar = tmppar->next();
819 } while (tmppar && tmppar != endpar);
821 // this is because of layout changes
823 refresh_y -= refresh_row->height();
824 setHeightOfRow(bview, refresh_row);
826 refresh_row = firstrow;
828 setHeightOfRow(bview, refresh_row);
831 if (tmprow && tmprow->next())
832 setHeightOfRow(bview, tmprow->next());
833 updateCounters(bview);
837 void LyXText::fullRebreak(BufferView * bview)
843 if (need_break_row) {
844 breakAgain(bview, need_break_row);
851 // important for the screen
854 // the cursor set functions have a special mechanism. When they
855 // realize, that you left an empty paragraph, they will delete it.
856 // They also delete the corresponding row
858 // need the selection cursor:
859 void LyXText::setSelection(BufferView * bview)
861 bool const lsel = selection.set();
863 if (!selection.set()) {
864 last_sel_cursor = selection.cursor;
865 selection.start = selection.cursor;
866 selection.end = selection.cursor;
871 // first the toggling area
872 if (cursor.y() < last_sel_cursor.y()
873 || (cursor.y() == last_sel_cursor.y()
874 && cursor.x() < last_sel_cursor.x())) {
875 toggle_end_cursor = last_sel_cursor;
876 toggle_cursor = cursor;
878 toggle_end_cursor = cursor;
879 toggle_cursor = last_sel_cursor;
882 last_sel_cursor = cursor;
884 // and now the whole selection
886 if (selection.cursor.par() == cursor.par())
887 if (selection.cursor.pos() < cursor.pos()) {
888 selection.end = cursor;
889 selection.start = selection.cursor;
891 selection.end = selection.cursor;
892 selection.start = cursor;
894 else if (selection.cursor.y() < cursor.y() ||
895 (selection.cursor.y() == cursor.y()
896 && selection.cursor.x() < cursor.x())) {
897 selection.end = cursor;
898 selection.start = selection.cursor;
901 selection.end = selection.cursor;
902 selection.start = cursor;
905 // a selection with no contents is not a selection
906 if (selection.start.par() == selection.end.par() &&
907 selection.start.pos() == selection.end.pos())
908 selection.set(false);
910 if (inset_owner && (selection.set() || lsel))
911 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
915 string const LyXText::selectionAsString(Buffer const * buffer,
918 if (!selection.set()) return string();
920 // should be const ...
921 Paragraph * startpar(selection.start.par());
922 Paragraph * endpar(selection.end.par());
923 pos_type const startpos(selection.start.pos());
924 pos_type const endpos(selection.end.pos());
926 if (startpar == endpar) {
927 return startpar->asString(buffer, startpos, endpos, label);
932 // First paragraph in selection
933 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
935 // The paragraphs in between (if any)
936 LyXCursor tmpcur(selection.start);
937 tmpcur.par(tmpcur.par()->next());
938 while (tmpcur.par() != endpar) {
939 result += tmpcur.par()->asString(buffer, 0,
940 tmpcur.par()->size(),
942 tmpcur.par(tmpcur.par()->next());
945 // Last paragraph in selection
946 result += endpar->asString(buffer, 0, endpos, label);
952 void LyXText::clearSelection() const
954 selection.set(false);
955 selection.mark(false);
956 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
957 // reset this in the bv_owner!
958 if (bv_owner && bv_owner->text)
959 bv_owner->text->xsel_cache.set(false);
963 void LyXText::cursorHome(BufferView * bview) const
965 setCursor(bview, cursor.par(), cursor.row()->pos());
969 void LyXText::cursorEnd(BufferView * bview) const
971 if (!cursor.row()->next()
972 || cursor.row()->next()->par() != cursor.row()->par()) {
973 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
975 if (!cursor.par()->empty() &&
976 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
977 || cursor.par()->isNewline(rowLast(cursor.row())))) {
978 setCursor(bview, cursor.par(), rowLast(cursor.row()));
980 setCursor(bview,cursor.par(),
981 rowLast(cursor.row()) + 1);
987 void LyXText::cursorTop(BufferView * bview) const
989 while (cursor.par()->previous())
990 cursor.par(cursor.par()->previous());
991 setCursor(bview, cursor.par(), 0);
995 void LyXText::cursorBottom(BufferView * bview) const
997 while (cursor.par()->next())
998 cursor.par(cursor.par()->next());
999 setCursor(bview, cursor.par(), cursor.par()->size());
1003 void LyXText::toggleFree(BufferView * bview,
1004 LyXFont const & font, bool toggleall)
1006 // If the mask is completely neutral, tell user
1007 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1008 // Could only happen with user style
1009 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1013 // Try implicit word selection
1014 // If there is a change in the language the implicit word selection
1016 LyXCursor resetCursor = cursor;
1017 bool implicitSelection = (font.language() == ignore_language
1018 && font.number() == LyXFont::IGNORE)
1019 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1022 setFont(bview, font, toggleall);
1024 // Implicit selections are cleared afterwards
1025 //and cursor is set to the original position.
1026 if (implicitSelection) {
1028 cursor = resetCursor;
1029 setCursor(bview, cursor.par(), cursor.pos());
1030 selection.cursor = cursor;
1033 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1037 string LyXText::getStringToIndex(BufferView * bview)
1041 // Try implicit word selection
1042 // If there is a change in the language the implicit word selection
1044 LyXCursor const reset_cursor = cursor;
1045 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1047 if (!selection.set()) {
1048 bview->owner()->message(_("Nothing to index!"));
1051 if (selection.start.par() != selection.end.par()) {
1052 bview->owner()->message(_("Cannot index more than one paragraph!"));
1056 idxstring = selectionAsString(bview->buffer(), false);
1058 // Implicit selections are cleared afterwards
1059 //and cursor is set to the original position.
1060 if (implicitSelection) {
1062 cursor = reset_cursor;
1063 setCursor(bview, cursor.par(), cursor.pos());
1064 selection.cursor = cursor;
1070 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1071 Paragraph const * par) const
1073 if (par->layout()->labeltype != LABEL_MANUAL)
1076 return par->beginningOfMainBody();
1080 // the DTP switches for paragraphs. LyX will store them in the first
1081 // physicla paragraph. When a paragraph is broken, the top settings rest,
1082 // the bottom settings are given to the new one. So I can make shure,
1083 // they do not duplicate themself and you cannnot make dirty things with
1086 void LyXText::setParagraph(BufferView * bview,
1087 bool line_top, bool line_bottom,
1088 bool pagebreak_top, bool pagebreak_bottom,
1089 VSpace const & space_top,
1090 VSpace const & space_bottom,
1091 Spacing const & spacing,
1093 string labelwidthstring,
1096 LyXCursor tmpcursor = cursor;
1097 if (!selection.set()) {
1098 selection.start = cursor;
1099 selection.end = cursor;
1102 // make sure that the depth behind the selection are restored, too
1103 Paragraph * endpar = selection.end.par()->next();
1104 Paragraph * undoendpar = endpar;
1106 if (endpar && endpar->getDepth()) {
1107 while (endpar && endpar->getDepth()) {
1108 endpar = endpar->next();
1109 undoendpar = endpar;
1113 // because of parindents etc.
1114 endpar = endpar->next();
1117 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1120 Paragraph * tmppar = selection.end.par();
1122 while (tmppar != selection.start.par()->previous()) {
1123 setCursor(bview, tmppar, 0);
1124 status(bview, LyXText::NEED_MORE_REFRESH);
1125 refresh_row = cursor.row();
1126 refresh_y = cursor.y() - cursor.row()->baseline();
1127 cursor.par()->params().lineTop(line_top);
1128 cursor.par()->params().lineBottom(line_bottom);
1129 cursor.par()->params().pagebreakTop(pagebreak_top);
1130 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1131 cursor.par()->params().spaceTop(space_top);
1132 cursor.par()->params().spaceBottom(space_bottom);
1133 cursor.par()->params().spacing(spacing);
1134 // does the layout allow the new alignment?
1135 LyXLayout_ptr const & layout = cursor.par()->layout();
1137 if (align == LYX_ALIGN_LAYOUT)
1138 align = layout->align;
1139 if (align & layout->alignpossible) {
1140 if (align == layout->align)
1141 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1143 cursor.par()->params().align(align);
1145 cursor.par()->setLabelWidthString(labelwidthstring);
1146 cursor.par()->params().noindent(noindent);
1147 tmppar = cursor.par()->previous();
1150 redoParagraphs(bview, selection.start, endpar);
1153 setCursor(bview, selection.start.par(), selection.start.pos());
1154 selection.cursor = cursor;
1155 setCursor(bview, selection.end.par(), selection.end.pos());
1156 setSelection(bview);
1157 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1159 bview->updateInset(inset_owner, true);
1163 // set the counter of a paragraph. This includes the labels
1164 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1166 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1167 LyXLayout_ptr const & layout = par->layout();
1169 if (par->previous()) {
1171 par->params().appendix(par->previous()->params().appendix());
1172 if (!par->params().appendix() && par->params().startOfAppendix()) {
1173 par->params().appendix(true);
1174 textclass.counters().reset();
1176 par->enumdepth = par->previous()->enumdepth;
1177 par->itemdepth = par->previous()->itemdepth;
1179 par->params().appendix(par->params().startOfAppendix());
1184 /* Maybe we have to increment the enumeration depth.
1185 * BUT, enumeration in a footnote is considered in isolation from its
1186 * surrounding paragraph so don't increment if this is the
1187 * first line of the footnote
1188 * AND, bibliographies can't have their depth changed ie. they
1189 * are always of depth 0
1192 && par->previous()->getDepth() < par->getDepth()
1193 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1194 && par->enumdepth < 3
1195 && layout->labeltype != LABEL_BIBLIO) {
1199 // Maybe we have to decrement the enumeration depth, see note above
1201 && par->previous()->getDepth() > par->getDepth()
1202 && layout->labeltype != LABEL_BIBLIO) {
1203 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1206 if (!par->params().labelString().empty()) {
1207 par->params().labelString(string());
1210 if (layout->margintype == MARGIN_MANUAL) {
1211 if (par->params().labelWidthString().empty()) {
1212 par->setLabelWidthString(layout->labelstring());
1215 par->setLabelWidthString(string());
1218 // is it a layout that has an automatic label?
1219 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1221 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1226 if (i >= 0 && i <= buf->params.secnumdepth) {
1228 textclass.counters().step(layout->latexname());
1230 // Is there a label? Useful for Chapter layout
1231 if (!par->params().appendix()) {
1232 if (!layout->labelstring().empty())
1233 par->params().labelString(layout->labelstring());
1235 par->params().labelString(string());
1237 if (!layout->labelstring_appendix().empty())
1238 par->params().labelString(layout->labelstring_appendix());
1240 par->params().labelString(string());
1243 // Use if an integer is here less than elegant. For now.
1244 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1245 if (!par->params().appendix()) {
1246 numbertype = "sectioning";
1248 numbertype = "appendix";
1249 if (par->isRightToLeftPar(buf->params))
1250 langtype = "hebrew";
1255 s << textclass.counters()
1256 .numberLabel(layout->latexname(),
1257 numbertype, langtype, head);
1259 par->params().labelString(par->params().labelString() + s.str().c_str());
1260 // We really want to remove the c_str as soon as
1263 // reset enum counters
1264 textclass.counters().reset("enum");
1265 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1266 textclass.counters().reset("enum");
1267 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1269 // Yes I know this is a really, really! bad solution
1271 string enumcounter("enum");
1273 switch (par->enumdepth) {
1282 enumcounter += "iv";
1285 // not a valid enumdepth...
1289 textclass.counters().step(enumcounter);
1291 s << textclass.counters()
1292 .numberLabel(enumcounter,
1293 "enumeration", langtype);
1294 par->params().labelString(s.str().c_str());
1296 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1297 textclass.counters().step("bibitem");
1298 int number = textclass.counters().value("bibitem");
1300 InsetCommandParams p("bibitem");
1301 par->bibkey = new InsetBibKey(p);
1303 par->bibkey->setCounter(number);
1304 par->params().labelString(layout->labelstring());
1306 // In biblio should't be following counters but...
1308 string s = layout->labelstring();
1310 // the caption hack:
1311 if (layout->labeltype == LABEL_SENSITIVE) {
1312 Paragraph * tmppar = par;
1315 while (tmppar && tmppar->inInset()
1316 // the single '=' is intended below
1317 && (in = tmppar->inInset()->owner())) {
1318 if (in->lyxCode() == Inset::FLOAT_CODE) {
1322 tmppar = in->parOwner();
1328 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1330 textclass.counters().step(fl.type());
1332 // Doesn't work... yet.
1334 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1335 o << fl.name() << " #:";
1338 // par->SetLayout(0);
1339 // s = layout->labelstring;
1340 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1341 ? " :úåòîùî øñç" : "Senseless: ";
1344 par->params().labelString(s);
1346 // reset the enumeration counter. They are always reset
1347 // when there is any other layout between
1348 // Just fall-through between the cases so that all
1349 // enum counters deeper than enumdepth is also reset.
1350 switch (par->enumdepth) {
1352 textclass.counters().reset("enumi");
1354 textclass.counters().reset("enumii");
1356 textclass.counters().reset("enumiii");
1358 textclass.counters().reset("enumiv");
1364 // Updates all counters BEHIND the row. Changed paragraphs
1365 // with a dynamic left margin will be rebroken.
1366 void LyXText::updateCounters(BufferView * bview) const
1370 Row * row = firstrow;
1373 // CHECK if this is really needed. (Lgb)
1374 bview->buffer()->params.getLyXTextClass().counters().reset();
1377 while (row->par() != par)
1380 setCounter(bview->buffer(), par);
1382 // now check for the headline layouts. remember that they
1383 // have a dynamic left margin
1384 LyXLayout_ptr const & layout = par->layout();
1386 if (layout->margintype == MARGIN_DYNAMIC
1387 || layout->labeltype == LABEL_SENSITIVE) {
1388 // Rebreak the paragraph
1389 removeParagraph(row);
1390 appendParagraph(bview, row);
1397 void LyXText::insertInset(BufferView * bview, Inset * inset)
1399 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1401 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1403 cursor.par()->insertInset(cursor.pos(), inset);
1404 // Just to rebreak and refresh correctly.
1405 // The character will not be inserted a second time
1406 insertChar(bview, Paragraph::META_INSET);
1407 // If we enter a highly editable inset the cursor should be to before
1408 // the inset. This couldn't happen before as Undo was not handled inside
1409 // inset now after the Undo LyX tries to call inset->Edit(...) again
1410 // and cannot do this as the cursor is behind the inset and GetInset
1411 // does not return the inset!
1412 if (isHighlyEditableInset(inset)) {
1413 cursorLeft(bview, true);
1419 void LyXText::copyEnvironmentType()
1421 copylayouttype = cursor.par()->layout()->name();
1425 void LyXText::pasteEnvironmentType(BufferView * bview)
1427 setLayout(bview, copylayouttype);
1431 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1433 // Stuff what we got on the clipboard. Even if there is no selection.
1435 // There is a problem with having the stuffing here in that the
1436 // larger the selection the slower LyX will get. This can be
1437 // solved by running the line below only when the selection has
1438 // finished. The solution used currently just works, to make it
1439 // faster we need to be more clever and probably also have more
1440 // calls to stuffClipboard. (Lgb)
1441 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1443 // This doesn't make sense, if there is no selection
1444 if (!selection.set())
1447 // OK, we have a selection. This is always between selection.start
1448 // and selection.end
1450 // make sure that the depth behind the selection are restored, too
1451 Paragraph * endpar = selection.end.par()->next();
1452 Paragraph * undoendpar = endpar;
1454 if (endpar && endpar->getDepth()) {
1455 while (endpar && endpar->getDepth()) {
1456 endpar = endpar->next();
1457 undoendpar = endpar;
1459 } else if (endpar) {
1460 endpar = endpar->next(); // because of parindents etc.
1463 setUndo(bview, Undo::DELETE,
1464 selection.start.par(), undoendpar);
1466 // there are two cases: cut only within one paragraph or
1467 // more than one paragraph
1468 if (selection.start.par() == selection.end.par()) {
1469 // only within one paragraph
1470 endpar = selection.end.par();
1471 int pos = selection.end.pos();
1472 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1473 selection.start.pos(), pos,
1474 bview->buffer()->params.textclass,
1476 selection.end.pos(pos);
1478 endpar = selection.end.par();
1479 int pos = selection.end.pos();
1480 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1481 selection.start.pos(), pos,
1482 bview->buffer()->params.textclass,
1485 selection.end.par(endpar);
1486 selection.end.pos(pos);
1487 cursor.pos(selection.end.pos());
1489 endpar = endpar->next();
1491 // sometimes necessary
1493 selection.start.par()->stripLeadingSpaces();
1495 redoParagraphs(bview, selection.start, endpar);
1497 // cutSelection can invalidate the cursor so we need to set
1499 cursor = selection.start;
1501 // need a valid cursor. (Lgb)
1504 setCursor(bview, cursor.par(), cursor.pos());
1505 selection.cursor = cursor;
1506 updateCounters(bview);
1510 void LyXText::copySelection(BufferView * bview)
1512 // stuff the selection onto the X clipboard, from an explicit copy request
1513 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1515 // this doesnt make sense, if there is no selection
1516 if (!selection.set())
1519 // ok we have a selection. This is always between selection.start
1520 // and sel_end cursor
1522 // copy behind a space if there is one
1523 while (selection.start.par()->size() > selection.start.pos()
1524 && selection.start.par()->isLineSeparator(selection.start.pos())
1525 && (selection.start.par() != selection.end.par()
1526 || selection.start.pos() < selection.end.pos()))
1527 selection.start.pos(selection.start.pos() + 1);
1529 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1530 selection.start.pos(), selection.end.pos(),
1531 bview->buffer()->params.textclass);
1535 void LyXText::pasteSelection(BufferView * bview)
1537 // this does not make sense, if there is nothing to paste
1538 if (!CutAndPaste::checkPastePossible(cursor.par()))
1541 setUndo(bview, Undo::INSERT,
1542 cursor.par(), cursor.par()->next());
1545 Paragraph * actpar = cursor.par();
1546 int pos = cursor.pos();
1548 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1549 bview->buffer()->params.textclass);
1551 redoParagraphs(bview, cursor, endpar);
1553 setCursor(bview, cursor.par(), cursor.pos());
1556 selection.cursor = cursor;
1557 setCursor(bview, actpar, pos);
1558 setSelection(bview);
1559 updateCounters(bview);
1563 // sets the selection over the number of characters of string, no check!!
1564 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1569 selection.cursor = cursor;
1570 for (string::size_type i = 0; i < str.length(); ++i)
1572 setSelection(bview);
1576 // simple replacing. The font of the first selected character is used
1577 void LyXText::replaceSelectionWithString(BufferView * bview,
1580 setCursorParUndo(bview);
1583 if (!selection.set()) { // create a dummy selection
1584 selection.end = cursor;
1585 selection.start = cursor;
1588 // Get font setting before we cut
1589 pos_type pos = selection.end.pos();
1590 LyXFont const font = selection.start.par()
1591 ->getFontSettings(bview->buffer()->params,
1592 selection.start.pos());
1594 // Insert the new string
1595 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1596 selection.end.par()->insertChar(pos, (*cit), font);
1600 // Cut the selection
1601 cutSelection(bview, true, false);
1607 // needed to insert the selection
1608 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1610 Paragraph * par = cursor.par();
1611 pos_type pos = cursor.pos();
1612 Paragraph * endpar = cursor.par()->next();
1614 setCursorParUndo(bview);
1616 // only to be sure, should not be neccessary
1619 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1621 redoParagraphs(bview, cursor, endpar);
1622 setCursor(bview, cursor.par(), cursor.pos());
1623 selection.cursor = cursor;
1624 setCursor(bview, par, pos);
1625 setSelection(bview);
1629 // turns double-CR to single CR, others where converted into one
1630 // blank. Then InsertStringAsLines is called
1631 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1633 string linestr(str);
1634 bool newline_inserted = false;
1635 for (string::size_type i = 0; i < linestr.length(); ++i) {
1636 if (linestr[i] == '\n') {
1637 if (newline_inserted) {
1638 // we know that \r will be ignored by
1639 // InsertStringA. Of course, it is a dirty
1640 // trick, but it works...
1641 linestr[i - 1] = '\r';
1645 newline_inserted = true;
1647 } else if (IsPrintable(linestr[i])) {
1648 newline_inserted = false;
1651 insertStringAsLines(bview, linestr);
1655 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1658 LyXCursor tmpcursor;
1662 Row * row = getRow(par, pos, y);
1664 // is there a break one row above
1665 if (row->previous() && row->previous()->par() == row->par()) {
1666 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1667 if (z >= row->pos()) {
1668 // set the dimensions of the row above
1669 y -= row->previous()->height();
1671 refresh_row = row->previous();
1672 status(bview, LyXText::NEED_MORE_REFRESH);
1674 breakAgain(bview, row->previous());
1676 // set the cursor again. Otherwise
1677 // dangling pointers are possible
1678 setCursor(bview, cursor.par(), cursor.pos(),
1679 false, cursor.boundary());
1680 selection.cursor = cursor;
1685 int const tmpheight = row->height();
1686 pos_type const tmplast = rowLast(row);
1690 breakAgain(bview, row);
1691 if (row->height() == tmpheight && rowLast(row) == tmplast)
1692 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1694 status(bview, LyXText::NEED_MORE_REFRESH);
1696 // check the special right address boxes
1697 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1704 redoDrawingOfParagraph(bview, tmpcursor);
1707 // set the cursor again. Otherwise dangling pointers are possible
1708 // also set the selection
1710 if (selection.set()) {
1712 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1713 false, selection.cursor.boundary());
1714 selection.cursor = cursor;
1715 setCursorIntern(bview, selection.start.par(),
1716 selection.start.pos(),
1717 false, selection.start.boundary());
1718 selection.start = cursor;
1719 setCursorIntern(bview, selection.end.par(),
1720 selection.end.pos(),
1721 false, selection.end.boundary());
1722 selection.end = cursor;
1723 setCursorIntern(bview, last_sel_cursor.par(),
1724 last_sel_cursor.pos(),
1725 false, last_sel_cursor.boundary());
1726 last_sel_cursor = cursor;
1729 setCursorIntern(bview, cursor.par(), cursor.pos(),
1730 false, cursor.boundary());
1734 // returns false if inset wasn't found
1735 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1737 // first check the current paragraph
1738 int pos = cursor.par()->getPositionOfInset(inset);
1740 checkParagraph(bview, cursor.par(), pos);
1744 // check every paragraph
1746 Paragraph * par = ownerParagraph();
1748 pos = par->getPositionOfInset(inset);
1750 checkParagraph(bview, par, pos);
1760 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1762 bool setfont, bool boundary) const
1764 LyXCursor old_cursor = cursor;
1765 setCursorIntern(bview, par, pos, setfont, boundary);
1766 return deleteEmptyParagraphMechanism(bview, old_cursor);
1770 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1771 pos_type pos, bool boundary) const
1778 cur.boundary(boundary);
1780 // get the cursor y position in text
1782 Row * row = getRow(par, pos, y);
1783 Row * old_row = row;
1785 // if we are before the first char of this row and are still in the
1786 // same paragraph and there is a previous row then put the cursor on
1787 // the end of the previous row
1788 cur.iy(y + row->baseline());
1790 if (row->previous() && pos &&
1791 row->previous()->par() == row->par() &&
1792 par->getChar(pos) == Paragraph::META_INSET &&
1793 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1795 row = row->previous();
1800 // y is now the beginning of the cursor row
1801 y += row->baseline();
1802 // y is now the cursor baseline
1805 pos_type last = rowLastPrintable(old_row);
1807 if (pos > last + 1) {
1808 // This shouldn't happen.
1811 } else if (pos < row->pos()) {
1816 // now get the cursors x position
1817 float x = getCursorX(bview, row, pos, last, boundary);
1820 if (old_row != row) {
1821 x = getCursorX(bview, old_row, pos, last, boundary);
1828 float LyXText::getCursorX(BufferView * bview, Row * row,
1829 pos_type pos, pos_type last, bool boundary) const
1831 pos_type cursor_vpos = 0;
1833 float fill_separator;
1835 float fill_label_hfill;
1836 // This call HAS to be here because of the BidiTables!!!
1837 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1840 if (last < row->pos())
1841 cursor_vpos = row->pos();
1842 else if (pos > last && !boundary)
1843 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1844 ? row->pos() : last + 1;
1845 else if (pos > row->pos() &&
1846 (pos > last || boundary))
1847 /// Place cursor after char at (logical) position pos - 1
1848 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1849 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1851 /// Place cursor before char at (logical) position pos
1852 cursor_vpos = (bidi_level(pos) % 2 == 0)
1853 ? log2vis(pos) : log2vis(pos) + 1;
1855 pos_type main_body =
1856 beginningOfMainBody(bview->buffer(), row->par());
1857 if ((main_body > 0) &&
1858 ((main_body-1 > last) ||
1859 !row->par()->isLineSeparator(main_body-1)))
1862 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1863 pos_type pos = vis2log(vpos);
1864 if (main_body > 0 && pos == main_body - 1) {
1865 x += fill_label_hfill +
1866 font_metrics::width(
1867 row->par()->layout()->labelsep,
1868 getLabelFont(bview->buffer(),
1870 if (row->par()->isLineSeparator(main_body - 1))
1871 x -= singleWidth(bview,
1872 row->par(), main_body - 1);
1874 if (hfillExpansion(bview->buffer(), row, pos)) {
1875 x += singleWidth(bview, row->par(), pos);
1876 if (pos >= main_body)
1879 x += fill_label_hfill;
1880 } else if (row->par()->isSeparator(pos)) {
1881 x += singleWidth(bview, row->par(), pos);
1882 if (pos >= main_body)
1883 x += fill_separator;
1885 x += singleWidth(bview, row->par(), pos);
1891 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1892 pos_type pos, bool setfont, bool boundary) const
1894 InsetText * it = static_cast<InsetText *>(par->inInset());
1896 if (it != inset_owner) {
1897 lyxerr[Debug::INSETS] << "InsetText is " << it
1899 << "inset_owner is "
1900 << inset_owner << endl;
1901 #ifdef WITH_WARNINGS
1902 #warning I believe this code is wrong. (Lgb)
1903 #warning Jürgen, have a look at this. (Lgb)
1904 #warning Hmmm, I guess you are right but we
1905 #warning should verify when this is needed
1907 // Jürgen, would you like to have a look?
1908 // I guess we need to move the outer cursor
1909 // and open and lock the inset (bla bla bla)
1910 // stuff I don't know... so can you have a look?
1912 // I moved the lyxerr stuff in here so we can see if
1913 // this is actually really needed and where!
1915 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1920 setCursor(bview, cursor, par, pos, boundary);
1922 setCurrentFont(bview);
1926 void LyXText::setCurrentFont(BufferView * bview) const
1928 pos_type pos = cursor.pos();
1929 if (cursor.boundary() && pos > 0)
1933 if (pos == cursor.par()->size())
1935 else // potentional bug... BUG (Lgb)
1936 if (cursor.par()->isSeparator(pos)) {
1937 if (pos > cursor.row()->pos() &&
1938 bidi_level(pos) % 2 ==
1939 bidi_level(pos - 1) % 2)
1941 else if (pos + 1 < cursor.par()->size())
1947 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1948 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1950 if (cursor.pos() == cursor.par()->size() &&
1951 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1952 !cursor.boundary()) {
1953 Language const * lang =
1954 cursor.par()->getParLanguage(bview->buffer()->params);
1955 current_font.setLanguage(lang);
1956 current_font.setNumber(LyXFont::OFF);
1957 real_current_font.setLanguage(lang);
1958 real_current_font.setNumber(LyXFont::OFF);
1963 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1965 LyXCursor old_cursor = cursor;
1967 setCursorFromCoordinates(bview, cursor, x, y);
1968 setCurrentFont(bview);
1969 deleteEmptyParagraphMechanism(bview, old_cursor);
1976 * return true if the cursor given is at the end of a row,
1977 * and the next row is filled by an inset that spans an entire
1980 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1983 Row const & next = *row.next();
1985 if (next.pos() != cur.pos() || next.par() != cur.par())
1987 if (!cur.par()->isInset(cur.pos()))
1989 Inset const * inset = cur.par()->getInset(cur.pos());
1990 if (inset->needFullRow() || inset->display())
1997 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2000 // Get the row first.
2002 Row * row = getRowNearY(y);
2004 pos_type const column = getColumnNearX(bview, row, x, bound);
2005 cur.par(row->par());
2006 cur.pos(row->pos() + column);
2008 cur.y(y + row->baseline());
2011 if (beforeFullRowInset(*row, cur)) {
2012 pos_type last = rowLastPrintable(row);
2013 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2015 cur.iy(y + row->height() + row->next()->baseline());
2016 cur.irow(row->next());
2022 cur.boundary(bound);
2026 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2028 if (cursor.pos() > 0) {
2029 bool boundary = cursor.boundary();
2030 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2031 if (!internal && !boundary &&
2032 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2033 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2034 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2035 Paragraph * par = cursor.par()->previous();
2036 setCursor(bview, par, par->size());
2041 void LyXText::cursorRight(BufferView * bview, bool internal) const
2043 if (!internal && cursor.boundary() &&
2044 !cursor.par()->isNewline(cursor.pos()))
2045 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2046 else if (cursor.pos() < cursor.par()->size()) {
2047 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2049 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2050 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2051 } else if (cursor.par()->next())
2052 setCursor(bview, cursor.par()->next(), 0);
2056 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2059 int x = cursor.x_fix();
2060 int y = cursor.y() - cursor.row()->baseline() - 1;
2061 setCursorFromCoordinates(bview, x, y);
2063 int y1 = cursor.iy() - first_y;
2066 Inset * inset_hit = checkInsetHit(bview, x, y1);
2067 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2068 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2072 setCursorFromCoordinates(bview, cursor.x_fix(),
2073 cursor.y() - cursor.row()->baseline() - 1);
2078 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2081 int x = cursor.x_fix();
2082 int y = cursor.y() - cursor.row()->baseline() +
2083 cursor.row()->height() + 1;
2084 setCursorFromCoordinates(bview, x, y);
2085 if (!selecting && cursor.row() == cursor.irow()) {
2086 int y1 = cursor.iy() - first_y;
2089 Inset * inset_hit = checkInsetHit(bview, x, y1);
2090 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2091 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2095 setCursorFromCoordinates(bview, cursor.x_fix(),
2096 cursor.y() - cursor.row()->baseline()
2097 + cursor.row()->height() + 1);
2102 void LyXText::cursorUpParagraph(BufferView * bview) const
2104 if (cursor.pos() > 0) {
2105 setCursor(bview, cursor.par(), 0);
2107 else if (cursor.par()->previous()) {
2108 setCursor(bview, cursor.par()->previous(), 0);
2113 void LyXText::cursorDownParagraph(BufferView * bview) const
2115 if (cursor.par()->next()) {
2116 setCursor(bview, cursor.par()->next(), 0);
2118 setCursor(bview, cursor.par(), cursor.par()->size());
2122 // fix the cursor `cur' after a characters has been deleted at `where'
2123 // position. Called by deleteEmptyParagraphMechanism
2124 void LyXText::fixCursorAfterDelete(BufferView * bview,
2126 LyXCursor const & where) const
2128 // if cursor is not in the paragraph where the delete occured,
2130 if (cur.par() != where.par())
2133 // if cursor position is after the place where the delete occured,
2135 if (cur.pos() > where.pos())
2136 cur.pos(cur.pos()-1);
2138 // check also if we don't want to set the cursor on a spot behind the
2139 // pagragraph because we erased the last character.
2140 if (cur.pos() > cur.par()->size())
2141 cur.pos(cur.par()->size());
2143 // recompute row et al. for this cursor
2144 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2148 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2149 LyXCursor const & old_cursor) const
2151 // Would be wrong to delete anything if we have a selection.
2152 if (selection.set())
2155 // We allow all kinds of "mumbo-jumbo" when freespacing.
2156 if (old_cursor.par()->layout()->free_spacing
2157 || old_cursor.par()->isFreeSpacing()) {
2161 /* Ok I'll put some comments here about what is missing.
2162 I have fixed BackSpace (and thus Delete) to not delete
2163 double-spaces automagically. I have also changed Cut,
2164 Copy and Paste to hopefully do some sensible things.
2165 There are still some small problems that can lead to
2166 double spaces stored in the document file or space at
2167 the beginning of paragraphs. This happens if you have
2168 the cursor betwenn to spaces and then save. Or if you
2169 cut and paste and the selection have a space at the
2170 beginning and then save right after the paste. I am
2171 sure none of these are very hard to fix, but I will
2172 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2173 that I can get some feedback. (Lgb)
2176 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2177 // delete the LineSeparator.
2180 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2181 // delete the LineSeparator.
2184 // If the pos around the old_cursor were spaces, delete one of them.
2185 if (old_cursor.par() != cursor.par()
2186 || old_cursor.pos() != cursor.pos()) {
2187 // Only if the cursor has really moved
2189 if (old_cursor.pos() > 0
2190 && old_cursor.pos() < old_cursor.par()->size()
2191 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2192 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2193 old_cursor.par()->erase(old_cursor.pos() - 1);
2194 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2196 #ifdef WITH_WARNINGS
2197 #warning This will not work anymore when we have multiple views of the same buffer
2198 // In this case, we will have to correct also the cursors held by
2199 // other bufferviews. It will probably be easier to do that in a more
2200 // automated way in LyXCursor code. (JMarc 26/09/2001)
2202 // correct all cursors held by the LyXText
2203 fixCursorAfterDelete(bview, cursor, old_cursor);
2204 fixCursorAfterDelete(bview, selection.cursor,
2206 fixCursorAfterDelete(bview, selection.start,
2208 fixCursorAfterDelete(bview, selection.end, old_cursor);
2209 fixCursorAfterDelete(bview, last_sel_cursor,
2211 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2212 fixCursorAfterDelete(bview, toggle_end_cursor,
2218 // don't delete anything if this is the ONLY paragraph!
2219 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2222 // Do not delete empty paragraphs with keepempty set.
2223 if (old_cursor.par()->layout()->keepempty)
2226 // only do our magic if we changed paragraph
2227 if (old_cursor.par() == cursor.par())
2230 // record if we have deleted a paragraph
2231 // we can't possibly have deleted a paragraph before this point
2232 bool deleted = false;
2234 if ((old_cursor.par()->empty()
2235 || (old_cursor.par()->size() == 1
2236 && old_cursor.par()->isLineSeparator(0)))) {
2237 // ok, we will delete anything
2238 LyXCursor tmpcursor;
2240 // make sure that you do not delete any environments
2241 status(bview, LyXText::NEED_MORE_REFRESH);
2244 if (old_cursor.row()->previous()) {
2245 refresh_row = old_cursor.row()->previous();
2246 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2248 cursor = old_cursor; // that undo can restore the right cursor position
2249 Paragraph * endpar = old_cursor.par()->next();
2250 if (endpar && endpar->getDepth()) {
2251 while (endpar && endpar->getDepth()) {
2252 endpar = endpar->next();
2255 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2259 removeRow(old_cursor.row());
2260 if (ownerParagraph() == old_cursor.par()) {
2261 ownerParagraph(ownerParagraph()->next());
2264 delete old_cursor.par();
2266 /* Breakagain the next par. Needed because of
2267 * the parindent that can occur or dissappear.
2268 * The next row can change its height, if
2269 * there is another layout before */
2270 if (refresh_row->next()) {
2271 breakAgain(bview, refresh_row->next());
2272 updateCounters(bview);
2274 setHeightOfRow(bview, refresh_row);
2276 refresh_row = old_cursor.row()->next();
2277 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2280 cursor = old_cursor; // that undo can restore the right cursor position
2281 Paragraph * endpar = old_cursor.par()->next();
2282 if (endpar && endpar->getDepth()) {
2283 while (endpar && endpar->getDepth()) {
2284 endpar = endpar->next();
2287 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2291 removeRow(old_cursor.row());
2293 if (ownerParagraph() == old_cursor.par()) {
2294 ownerParagraph(ownerParagraph()->next());
2297 delete old_cursor.par();
2299 /* Breakagain the next par. Needed because of
2300 the parindent that can occur or dissappear.
2301 The next row can change its height, if
2302 there is another layout before */
2304 breakAgain(bview, refresh_row);
2305 updateCounters(bview);
2310 setCursorIntern(bview, cursor.par(), cursor.pos());
2312 if (selection.cursor.par() == old_cursor.par()
2313 && selection.cursor.pos() == old_cursor.pos()) {
2314 // correct selection
2315 selection.cursor = cursor;
2319 if (old_cursor.par()->stripLeadingSpaces()) {
2320 redoParagraphs(bview, old_cursor,
2321 old_cursor.par()->next());
2323 setCursorIntern(bview, cursor.par(), cursor.pos());
2324 selection.cursor = cursor;
2331 Paragraph * LyXText::ownerParagraph() const
2334 return inset_owner->paragraph();
2336 return &*(bv_owner->buffer()->paragraphs.begin());
2340 void LyXText::ownerParagraph(Paragraph * p) const
2343 inset_owner->paragraph(p);
2345 bv_owner->buffer()->paragraphs.set(p);
2350 void LyXText::ownerParagraph(int id, Paragraph * p) const
2352 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2353 if (op && op->inInset()) {
2354 static_cast<InsetText *>(op->inInset())->paragraph(p);
2361 LyXText::text_status LyXText::status() const
2367 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2369 LyXText * t = bview->text;
2371 // We should only go up with refreshing code so this means that if
2372 // we have a MORE refresh we should never set it to LITTLE if we still
2373 // didn't handle it (and then it will be UNCHANGED. Now as long as
2374 // we stay inside one LyXText this may work but we need to tell the
2375 // outermost LyXText that it should REALLY draw us if there is some
2376 // change in a Inset::LyXText. So you see that when we are inside a
2377 // inset's LyXText we give the LITTLE to the outermost LyXText to
2378 // tell'em that it should redraw the actual row (where the inset
2379 // resides! Capito?!
2381 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2383 if (inset_owner && st != UNCHANGED) {
2384 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2385 if (!t->refresh_row) {
2386 t->refresh_row = t->cursor.row();
2387 t->refresh_y = t->cursor.y() -
2388 t->cursor.row()->baseline();