1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
15 #include "paragraph.h"
16 #include "frontends/LyXView.h"
17 #include "undo_funcs.h"
19 #include "bufferparams.h"
21 #include "BufferView.h"
22 #include "CutAndPaste.h"
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
28 #include "FloatList.h"
30 #include "ParagraphParameters.h"
33 #include "insets/inseterror.h"
34 #include "insets/insetbib.h"
35 #include "insets/insetspecialchar.h"
36 #include "insets/insettext.h"
37 #include "insets/insetfloat.h"
38 #include "insets/insetwrap.h"
40 #include "support/LAssert.h"
41 #include "support/textutils.h"
42 #include "support/lstrings.h"
44 #include "BoostFormat.h"
54 LyXText::LyXText(BufferView * bv)
55 : height(0), width(0), first_y(0),
56 inset_owner(0), the_locking_inset(0), need_break_row(0),
57 refresh_y(0), refresh_row(0), bv_owner(bv),
58 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
62 LyXText::LyXText(InsetText * inset)
63 : height(0), width(0), first_y(0),
64 inset_owner(inset), the_locking_inset(0), need_break_row(0),
65 refresh_y(0), refresh_row(0), bv_owner(0),
66 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
70 void LyXText::init(BufferView * bview, bool reinit)
73 // Delete all rows, this does not touch the paragraphs!
74 Row * tmprow = firstrow;
76 tmprow = firstrow->next();
85 copylayouttype.erase();
86 first_y = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(bview, par, lastrow);
98 setCursorIntern(bview, firstrow->par(), 0);
99 selection.cursor = cursor;
101 updateCounters(bview);
107 // Delete all rows, this does not touch the paragraphs!
108 Row * tmprow = firstrow;
110 tmprow = firstrow->next();
119 LyXFont const realizeFont(LyXFont const & font,
123 LyXTextClass const & tclass = buf->params.getLyXTextClass();
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 tmpfont.realize(par->layout()->font);
132 par_depth = par->getDepth();
136 tmpfont.realize(tclass.defaultfont());
144 // Gets the fully instantiated font at a given position in a paragraph
145 // Basically the same routine as Paragraph::getFont() in paragraph.C.
146 // The difference is that this one is used for displaying, and thus we
147 // are allowed to make cosmetic improvements. For instance make footnotes
149 // If position is -1, we get the layout font of the paragraph.
150 // If position is -2, we get the font of the manual label of the paragraph.
151 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
154 lyx::Assert(pos >= 0);
156 LyXLayout_ptr const & layout = par->layout();
158 // We specialize the 95% common case:
159 if (!par->getDepth()) {
160 if (layout->labeltype == LABEL_MANUAL
161 && pos < par->beginningOfMainBody()) {
163 LyXFont f = par->getFontSettings(buf->params, pos);
165 par->inInset()->getDrawFont(f);
166 return f.realize(layout->reslabelfont);
168 LyXFont f = par->getFontSettings(buf->params, pos);
170 par->inInset()->getDrawFont(f);
171 return f.realize(layout->resfont);
175 // The uncommon case need not be optimized as much
179 if (pos < par->beginningOfMainBody()) {
181 layoutfont = layout->labelfont;
184 layoutfont = layout->font;
187 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
188 tmpfont.realize(layoutfont);
191 par->inInset()->getDrawFont(tmpfont);
193 return realizeFont(tmpfont, buf, par);
197 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
199 LyXLayout_ptr const & layout = par->layout();
201 if (!par->getDepth()) {
202 return layout->resfont;
205 return realizeFont(layout->font, buf, par);
209 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
211 LyXLayout_ptr const & layout = par->layout();
213 if (!par->getDepth()) {
214 return layout->reslabelfont;
217 return realizeFont(layout->labelfont, buf, par);
221 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
222 pos_type pos, LyXFont const & fnt,
225 Buffer const * buf = bv->buffer();
226 LyXFont font = getFont(buf, par, pos);
227 font.update(fnt, buf->params.language, toggleall);
228 // Let the insets convert their font
229 if (par->isInset(pos)) {
230 Inset * inset = par->getInset(pos);
231 if (isEditableInset(inset)) {
232 UpdatableInset * uinset =
233 static_cast<UpdatableInset *>(inset);
234 uinset->setFont(bv, fnt, toggleall, true);
238 // Plug thru to version below:
239 setCharFont(buf, par, pos, font);
243 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
244 pos_type pos, LyXFont const & fnt)
248 LyXTextClass const & tclass = buf->params.getLyXTextClass();
249 LyXLayout_ptr const & layout = par->layout();
251 // Get concrete layout font to reduce against
254 if (pos < par->beginningOfMainBody())
255 layoutfont = layout->labelfont;
257 layoutfont = layout->font;
259 // Realize against environment font information
260 if (par->getDepth()) {
261 Paragraph * tp = par;
262 while (!layoutfont.resolved() && tp && tp->getDepth()) {
263 tp = tp->outerHook();
265 layoutfont.realize(tp->layout()->font);
269 layoutfont.realize(tclass.defaultfont());
271 // Now, reduce font against full layout font
272 font.reduce(layoutfont);
274 par->setFont(pos, font);
278 // inserts a new row behind the specified row, increments
279 // the touched counters
280 void LyXText::insertRow(Row * row, Paragraph * par,
283 Row * tmprow = new Row;
286 tmprow->next(firstrow);
289 tmprow->previous(row);
290 tmprow->next(row->next());
295 tmprow->next()->previous(tmprow);
297 if (tmprow->previous())
298 tmprow->previous()->next(tmprow);
309 // removes the row and reset the touched counters
310 void LyXText::removeRow(Row * row) const
312 Row * row_prev = row->previous();
314 row->next()->previous(row_prev);
316 firstrow = row->next();
317 // lyx::Assert(firstrow);
319 row_prev->next(row->next());
321 if (row == lastrow) {
322 lyx::Assert(!row->next());
325 if (refresh_row == row) {
326 refresh_row = row_prev ? row_prev : row->next();
327 // what about refresh_y, refresh_height
330 height -= row->height(); // the text becomes smaller
336 // remove all following rows of the paragraph of the specified row.
337 void LyXText::removeParagraph(Row * row) const
339 Paragraph * tmppar = row->par();
343 while (row && row->par() == tmppar) {
344 tmprow = row->next();
351 // insert the specified paragraph behind the specified row
352 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
355 // insert a new row, starting at position 0
356 insertRow(row, par, 0);
358 // and now append the whole paragraph behind the new row
361 appendParagraph(bview, firstrow);
363 row->next()->height(0);
364 appendParagraph(bview, row->next());
369 Inset * LyXText::getInset() const
371 if (cursor.pos() < cursor.par()->size()
372 && cursor.par()->isInset(cursor.pos())) {
373 return cursor.par()->getInset(cursor.pos());
379 void LyXText::toggleInset(BufferView * bview)
381 Inset * inset = getInset();
382 // is there an editable inset at cursor position?
383 if (!isEditableInset(inset)) {
384 // No, try to see if we are inside a collapsable inset
385 if (inset_owner && inset_owner->owner()
386 && inset_owner->owner()->isOpen()) {
387 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
388 inset_owner->owner()->close(bview);
389 bview->getLyXText()->cursorRight(bview);
393 //bview->owner()->message(inset->editMessage());
395 // do we want to keep this?? (JMarc)
396 if (!isHighlyEditableInset(inset))
397 setCursorParUndo(bview);
399 if (inset->isOpen()) {
405 inset->open(bview, !inset->isOpen());
410 /* used in setlayout */
411 // Asger is not sure we want to do this...
412 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
415 LyXLayout_ptr const & layout = par->layout();
418 for (pos_type pos = 0; pos < par->size(); ++pos) {
419 if (pos < par->beginningOfMainBody())
420 layoutfont = layout->labelfont;
422 layoutfont = layout->font;
424 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
425 tmpfont.reduce(layoutfont);
426 par->setFont(pos, tmpfont);
431 Paragraph * LyXText::setLayout(BufferView * bview,
432 LyXCursor & cur, LyXCursor & sstart_cur,
433 LyXCursor & send_cur,
434 string const & layout)
436 Paragraph * endpar = send_cur.par()->next();
437 Paragraph * undoendpar = endpar;
439 if (endpar && endpar->getDepth()) {
440 while (endpar && endpar->getDepth()) {
441 endpar = endpar->next();
445 endpar = endpar->next(); // because of parindents etc.
448 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
450 // ok we have a selection. This is always between sstart_cur
451 // and sel_end cursor
453 Paragraph * par = sstart_cur.par();
454 Paragraph * epar = send_cur.par()->next();
456 LyXLayout_ptr const & lyxlayout =
457 bview->buffer()->params.getLyXTextClass()[layout];
460 par->applyLayout(lyxlayout);
461 makeFontEntriesLayoutSpecific(bview->buffer(), par);
462 Paragraph * fppar = par;
463 fppar->params().spaceTop(lyxlayout->fill_top ?
464 VSpace(VSpace::VFILL)
465 : VSpace(VSpace::NONE));
466 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
467 VSpace(VSpace::VFILL)
468 : VSpace(VSpace::NONE));
469 if (lyxlayout->margintype == MARGIN_MANUAL)
470 par->setLabelWidthString(lyxlayout->labelstring());
473 } while (par != epar);
479 // set layout over selection and make a total rebreak of those paragraphs
480 void LyXText::setLayout(BufferView * bview, string const & layout)
482 LyXCursor tmpcursor = cursor; /* store the current cursor */
484 // if there is no selection just set the layout
485 // of the current paragraph */
486 if (!selection.set()) {
487 selection.start = cursor; // dummy selection
488 selection.end = cursor;
490 Paragraph * endpar = setLayout(bview, cursor, selection.start,
491 selection.end, layout);
492 redoParagraphs(bview, selection.start, endpar);
494 // we have to reset the selection, because the
495 // geometry could have changed
496 setCursor(bview, selection.start.par(),
497 selection.start.pos(), false);
498 selection.cursor = cursor;
499 setCursor(bview, selection.end.par(), selection.end.pos(), false);
500 updateCounters(bview);
503 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
507 // increment depth over selection and
508 // make a total rebreak of those paragraphs
509 void LyXText::incDepth(BufferView * bview)
511 // If there is no selection, just use the current paragraph
512 if (!selection.set()) {
513 selection.start = cursor; // dummy selection
514 selection.end = cursor;
517 // We end at the next paragraph with depth 0
518 Paragraph * endpar = selection.end.par()->next();
520 Paragraph * undoendpar = endpar;
522 if (endpar && endpar->getDepth()) {
523 while (endpar && endpar->getDepth()) {
524 endpar = endpar->next();
528 endpar = endpar->next(); // because of parindents etc.
531 setUndo(bview, Undo::EDIT,
532 selection.start.par(), undoendpar);
534 LyXCursor tmpcursor = cursor; // store the current cursor
536 // ok we have a selection. This is always between sel_start_cursor
537 // and sel_end cursor
538 cursor = selection.start;
541 // NOTE: you can't change the depth of a bibliography entry
542 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
543 Paragraph * prev = cursor.par()->previous();
546 if (cursor.par()->getDepth()
547 < prev->getMaxDepthAfter()) {
548 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
552 if (cursor.par() == selection.end.par())
554 cursor.par(cursor.par()->next());
557 redoParagraphs(bview, selection.start, endpar);
559 // we have to reset the selection, because the
560 // geometry could have changed
561 setCursor(bview, selection.start.par(), selection.start.pos());
562 selection.cursor = cursor;
563 setCursor(bview, selection.end.par(), selection.end.pos());
564 updateCounters(bview);
567 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
571 // decrement depth over selection and
572 // make a total rebreak of those paragraphs
573 void LyXText::decDepth(BufferView * bview)
575 // if there is no selection just set the layout
576 // of the current paragraph
577 if (!selection.set()) {
578 selection.start = cursor; // dummy selection
579 selection.end = cursor;
581 Paragraph * endpar = selection.end.par()->next();
582 Paragraph * undoendpar = endpar;
584 if (endpar && endpar->getDepth()) {
585 while (endpar && endpar->getDepth()) {
586 endpar = endpar->next();
590 endpar = endpar->next(); // because of parindents etc.
593 setUndo(bview, Undo::EDIT,
594 selection.start.par(), undoendpar);
596 LyXCursor tmpcursor = cursor; // store the current cursor
598 // ok we have a selection. This is always between sel_start_cursor
599 // and sel_end cursor
600 cursor = selection.start;
603 if (cursor.par()->params().depth()) {
604 cursor.par()->params()
605 .depth(cursor.par()->params().depth() - 1);
607 if (cursor.par() == selection.end.par()) {
610 cursor.par(cursor.par()->next());
613 redoParagraphs(bview, selection.start, endpar);
615 // we have to reset the selection, because the
616 // geometry could have changed
617 setCursor(bview, selection.start.par(),
618 selection.start.pos());
619 selection.cursor = cursor;
620 setCursor(bview, selection.end.par(), selection.end.pos());
621 updateCounters(bview);
624 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
628 // set font over selection and make a total rebreak of those paragraphs
629 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
631 // if there is no selection just set the current_font
632 if (!selection.set()) {
633 // Determine basis font
635 if (cursor.pos() < cursor.par()->beginningOfMainBody()) {
636 layoutfont = getLabelFont(bview->buffer(),
639 layoutfont = getLayoutFont(bview->buffer(),
642 // Update current font
643 real_current_font.update(font,
644 bview->buffer()->params.language,
647 // Reduce to implicit settings
648 current_font = real_current_font;
649 current_font.reduce(layoutfont);
650 // And resolve it completely
651 real_current_font.realize(layoutfont);
656 LyXCursor tmpcursor = cursor; // store the current cursor
658 // ok we have a selection. This is always between sel_start_cursor
659 // and sel_end cursor
661 setUndo(bview, Undo::EDIT,
662 selection.start.par(), selection.end.par()->next());
664 cursor = selection.start;
665 while (cursor.par() != selection.end.par() ||
666 cursor.pos() < selection.end.pos())
668 if (cursor.pos() < cursor.par()->size()) {
669 // an open footnote should behave like a closed one
670 setCharFont(bview, cursor.par(), cursor.pos(),
672 cursor.pos(cursor.pos() + 1);
675 cursor.par(cursor.par()->next());
680 redoParagraphs(bview, selection.start, selection.end.par()->next());
682 // we have to reset the selection, because the
683 // geometry could have changed, but we keep
684 // it for user convenience
685 setCursor(bview, selection.start.par(), selection.start.pos());
686 selection.cursor = cursor;
687 setCursor(bview, selection.end.par(), selection.end.pos());
689 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
690 tmpcursor.boundary());
694 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
696 Row * tmprow = cur.row();
697 int y = cur.y() - tmprow->baseline();
699 setHeightOfRow(bview, tmprow);
701 while (tmprow->previous()
702 && tmprow->previous()->par() == tmprow->par()) {
703 tmprow = tmprow->previous();
704 y -= tmprow->height();
705 setHeightOfRow(bview, tmprow);
708 // we can set the refreshing parameters now
709 status(bview, LyXText::NEED_MORE_REFRESH);
711 refresh_row = tmprow;
712 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
716 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
718 Row * tmprow = cur.row();
720 int y = cur.y() - tmprow->baseline();
721 setHeightOfRow(bview, tmprow);
723 while (tmprow->previous()
724 && tmprow->previous()->par() == tmprow->par()) {
725 tmprow = tmprow->previous();
726 y -= tmprow->height();
729 // we can set the refreshing parameters now
730 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
732 refresh_row = tmprow;
734 status(bview, LyXText::NEED_MORE_REFRESH);
735 setCursor(bview, cur.par(), cur.pos());
739 // deletes and inserts again all paragaphs between the cursor
740 // and the specified par
741 // This function is needed after SetLayout and SetFont etc.
742 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
743 Paragraph const * endpar) const
746 Paragraph * tmppar = 0;
747 Paragraph * first_phys_par = 0;
749 Row * tmprow = cur.row();
751 int y = cur.y() - tmprow->baseline();
753 if (!tmprow->previous()) {
754 // a trick/hack for UNDO
755 // This is needed because in an UNDO/REDO we could have changed
756 // the ownerParagrah() so the paragraph inside the row is NOT
757 // my really first par anymore. Got it Lars ;) (Jug 20011206)
758 first_phys_par = ownerParagraph();
760 first_phys_par = tmprow->par();
761 while (tmprow->previous()
762 && tmprow->previous()->par() == first_phys_par)
764 tmprow = tmprow->previous();
765 y -= tmprow->height();
769 // we can set the refreshing parameters now
770 status(bview, LyXText::NEED_MORE_REFRESH);
772 refresh_row = tmprow->previous(); /* the real refresh row will
773 be deleted, so I store
777 tmppar = tmprow->next()->par();
780 while (tmprow->next() && tmppar != endpar) {
781 removeRow(tmprow->next());
782 if (tmprow->next()) {
783 tmppar = tmprow->next()->par();
789 // remove the first one
790 tmprow2 = tmprow; /* this is because tmprow->previous()
792 tmprow = tmprow->previous();
795 tmppar = first_phys_par;
799 insertParagraph(bview, tmppar, tmprow);
803 while (tmprow->next()
804 && tmprow->next()->par() == tmppar) {
805 tmprow = tmprow->next();
807 tmppar = tmppar->next();
809 } while (tmppar && tmppar != endpar);
811 // this is because of layout changes
813 refresh_y -= refresh_row->height();
814 setHeightOfRow(bview, refresh_row);
816 refresh_row = firstrow;
818 setHeightOfRow(bview, refresh_row);
821 if (tmprow && tmprow->next())
822 setHeightOfRow(bview, tmprow->next());
823 updateCounters(bview);
827 void LyXText::fullRebreak(BufferView * bview)
833 if (need_break_row) {
834 breakAgain(bview, need_break_row);
841 // important for the screen
844 // the cursor set functions have a special mechanism. When they
845 // realize, that you left an empty paragraph, they will delete it.
846 // They also delete the corresponding row
848 // need the selection cursor:
849 void LyXText::setSelection(BufferView * bview)
851 bool const lsel = selection.set();
853 if (!selection.set()) {
854 last_sel_cursor = selection.cursor;
855 selection.start = selection.cursor;
856 selection.end = selection.cursor;
861 // first the toggling area
862 if (cursor.y() < last_sel_cursor.y()
863 || (cursor.y() == last_sel_cursor.y()
864 && cursor.x() < last_sel_cursor.x())) {
865 toggle_end_cursor = last_sel_cursor;
866 toggle_cursor = cursor;
868 toggle_end_cursor = cursor;
869 toggle_cursor = last_sel_cursor;
872 last_sel_cursor = cursor;
874 // and now the whole selection
876 if (selection.cursor.par() == cursor.par())
877 if (selection.cursor.pos() < cursor.pos()) {
878 selection.end = cursor;
879 selection.start = selection.cursor;
881 selection.end = selection.cursor;
882 selection.start = cursor;
884 else if (selection.cursor.y() < cursor.y() ||
885 (selection.cursor.y() == cursor.y()
886 && selection.cursor.x() < cursor.x())) {
887 selection.end = cursor;
888 selection.start = selection.cursor;
891 selection.end = selection.cursor;
892 selection.start = cursor;
895 // a selection with no contents is not a selection
896 if (selection.start.par() == selection.end.par() &&
897 selection.start.pos() == selection.end.pos())
898 selection.set(false);
900 if (inset_owner && (selection.set() || lsel))
901 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
905 string const LyXText::selectionAsString(Buffer const * buffer,
908 if (!selection.set()) return string();
910 // should be const ...
911 Paragraph * startpar(selection.start.par());
912 Paragraph * endpar(selection.end.par());
913 pos_type const startpos(selection.start.pos());
914 pos_type const endpos(selection.end.pos());
916 if (startpar == endpar) {
917 return startpar->asString(buffer, startpos, endpos, label);
922 // First paragraph in selection
923 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
925 // The paragraphs in between (if any)
926 LyXCursor tmpcur(selection.start);
927 tmpcur.par(tmpcur.par()->next());
928 while (tmpcur.par() != endpar) {
929 result += tmpcur.par()->asString(buffer, 0,
930 tmpcur.par()->size(),
932 tmpcur.par(tmpcur.par()->next());
935 // Last paragraph in selection
936 result += endpar->asString(buffer, 0, endpos, label);
942 void LyXText::clearSelection() const
944 selection.set(false);
945 selection.mark(false);
946 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
947 // reset this in the bv_owner!
948 if (bv_owner && bv_owner->text)
949 bv_owner->text->xsel_cache.set(false);
953 void LyXText::cursorHome(BufferView * bview) const
955 setCursor(bview, cursor.par(), cursor.row()->pos());
959 void LyXText::cursorEnd(BufferView * bview) const
961 if (!cursor.row()->next()
962 || cursor.row()->next()->par() != cursor.row()->par()) {
963 setCursor(bview, cursor.par(), cursor.row()->lastPos() + 1);
965 if (!cursor.par()->empty() &&
966 (cursor.par()->getChar(cursor.row()->lastPos()) == ' '
967 || cursor.par()->isNewline(cursor.row()->lastPos()))) {
968 setCursor(bview, cursor.par(), cursor.row()->lastPos());
970 setCursor(bview,cursor.par(),
971 cursor.row()->lastPos() + 1);
977 void LyXText::cursorTop(BufferView * bview) const
979 while (cursor.par()->previous())
980 cursor.par(cursor.par()->previous());
981 setCursor(bview, cursor.par(), 0);
985 void LyXText::cursorBottom(BufferView * bview) const
987 while (cursor.par()->next())
988 cursor.par(cursor.par()->next());
989 setCursor(bview, cursor.par(), cursor.par()->size());
993 void LyXText::toggleFree(BufferView * bview,
994 LyXFont const & font, bool toggleall)
996 // If the mask is completely neutral, tell user
997 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
998 // Could only happen with user style
999 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1003 // Try implicit word selection
1004 // If there is a change in the language the implicit word selection
1006 LyXCursor resetCursor = cursor;
1007 bool implicitSelection = (font.language() == ignore_language
1008 && font.number() == LyXFont::IGNORE)
1009 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1012 setFont(bview, font, toggleall);
1014 // Implicit selections are cleared afterwards
1015 //and cursor is set to the original position.
1016 if (implicitSelection) {
1018 cursor = resetCursor;
1019 setCursor(bview, cursor.par(), cursor.pos());
1020 selection.cursor = cursor;
1023 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1027 string LyXText::getStringToIndex(BufferView * bview)
1029 // Try implicit word selection
1030 // If there is a change in the language the implicit word selection
1032 LyXCursor const reset_cursor = cursor;
1033 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1036 if (!selection.set())
1037 bview->owner()->message(_("Nothing to index!"));
1038 else if (selection.start.par() != selection.end.par())
1039 bview->owner()->message(_("Cannot index more than one paragraph!"));
1041 idxstring = selectionAsString(bview->buffer(), false);
1043 // Reset cursors to their original position.
1044 cursor = reset_cursor;
1045 setCursor(bview, cursor.par(), cursor.pos());
1046 selection.cursor = cursor;
1048 // Clear the implicit selection.
1049 if (implicitSelection)
1056 // the DTP switches for paragraphs. LyX will store them in the first
1057 // physicla paragraph. When a paragraph is broken, the top settings rest,
1058 // the bottom settings are given to the new one. So I can make shure,
1059 // they do not duplicate themself and you cannnot make dirty things with
1062 void LyXText::setParagraph(BufferView * bview,
1063 bool line_top, bool line_bottom,
1064 bool pagebreak_top, bool pagebreak_bottom,
1065 VSpace const & space_top,
1066 VSpace const & space_bottom,
1067 Spacing const & spacing,
1069 string labelwidthstring,
1072 LyXCursor tmpcursor = cursor;
1073 if (!selection.set()) {
1074 selection.start = cursor;
1075 selection.end = cursor;
1078 // make sure that the depth behind the selection are restored, too
1079 Paragraph * endpar = selection.end.par()->next();
1080 Paragraph * undoendpar = endpar;
1082 if (endpar && endpar->getDepth()) {
1083 while (endpar && endpar->getDepth()) {
1084 endpar = endpar->next();
1085 undoendpar = endpar;
1089 // because of parindents etc.
1090 endpar = endpar->next();
1093 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1096 Paragraph * tmppar = selection.end.par();
1098 while (tmppar != selection.start.par()->previous()) {
1099 setCursor(bview, tmppar, 0);
1100 status(bview, LyXText::NEED_MORE_REFRESH);
1101 refresh_row = cursor.row();
1102 refresh_y = cursor.y() - cursor.row()->baseline();
1103 cursor.par()->params().lineTop(line_top);
1104 cursor.par()->params().lineBottom(line_bottom);
1105 cursor.par()->params().pagebreakTop(pagebreak_top);
1106 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1107 cursor.par()->params().spaceTop(space_top);
1108 cursor.par()->params().spaceBottom(space_bottom);
1109 cursor.par()->params().spacing(spacing);
1110 // does the layout allow the new alignment?
1111 LyXLayout_ptr const & layout = cursor.par()->layout();
1113 if (align == LYX_ALIGN_LAYOUT)
1114 align = layout->align;
1115 if (align & layout->alignpossible) {
1116 if (align == layout->align)
1117 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1119 cursor.par()->params().align(align);
1121 cursor.par()->setLabelWidthString(labelwidthstring);
1122 cursor.par()->params().noindent(noindent);
1123 tmppar = cursor.par()->previous();
1126 redoParagraphs(bview, selection.start, endpar);
1129 setCursor(bview, selection.start.par(), selection.start.pos());
1130 selection.cursor = cursor;
1131 setCursor(bview, selection.end.par(), selection.end.pos());
1132 setSelection(bview);
1133 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1135 bview->updateInset(inset_owner, true);
1139 // set the counter of a paragraph. This includes the labels
1140 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1142 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1143 LyXLayout_ptr const & layout = par->layout();
1145 if (par->previous()) {
1147 par->params().appendix(par->previous()->params().appendix());
1148 if (!par->params().appendix() && par->params().startOfAppendix()) {
1149 par->params().appendix(true);
1150 textclass.counters().reset();
1152 par->enumdepth = par->previous()->enumdepth;
1153 par->itemdepth = par->previous()->itemdepth;
1155 par->params().appendix(par->params().startOfAppendix());
1160 /* Maybe we have to increment the enumeration depth.
1161 * BUT, enumeration in a footnote is considered in isolation from its
1162 * surrounding paragraph so don't increment if this is the
1163 * first line of the footnote
1164 * AND, bibliographies can't have their depth changed ie. they
1165 * are always of depth 0
1168 && par->previous()->getDepth() < par->getDepth()
1169 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1170 && par->enumdepth < 3
1171 && layout->labeltype != LABEL_BIBLIO) {
1175 // Maybe we have to decrement the enumeration depth, see note above
1177 && par->previous()->getDepth() > par->getDepth()
1178 && layout->labeltype != LABEL_BIBLIO) {
1179 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1182 if (!par->params().labelString().empty()) {
1183 par->params().labelString(string());
1186 if (layout->margintype == MARGIN_MANUAL) {
1187 if (par->params().labelWidthString().empty()) {
1188 par->setLabelWidthString(layout->labelstring());
1191 par->setLabelWidthString(string());
1194 // is it a layout that has an automatic label?
1195 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1196 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1200 if (i >= 0 && i <= buf->params.secnumdepth) {
1204 textclass.counters().step(layout->latexname());
1206 // Is there a label? Useful for Chapter layout
1207 if (!par->params().appendix()) {
1208 s << layout->labelstring();
1210 s << layout->labelstring_appendix();
1213 // Use of an integer is here less than elegant. For now.
1214 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1215 if (!par->params().appendix()) {
1216 numbertype = "sectioning";
1218 numbertype = "appendix";
1219 if (par->isRightToLeftPar(buf->params))
1220 langtype = "hebrew";
1225 s << textclass.counters()
1226 .numberLabel(layout->latexname(),
1227 numbertype, langtype, head);
1229 par->params().labelString(STRCONV(s.str()));
1231 // reset enum counters
1232 textclass.counters().reset("enum");
1233 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1234 textclass.counters().reset("enum");
1235 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1237 // Yes I know this is a really, really! bad solution
1239 string enumcounter("enum");
1241 switch (par->enumdepth) {
1250 enumcounter += "iv";
1253 // not a valid enumdepth...
1257 textclass.counters().step(enumcounter);
1259 s << textclass.counters()
1260 .numberLabel(enumcounter, "enumeration");
1261 par->params().labelString(STRCONV(s.str()));
1263 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1264 textclass.counters().step("bibitem");
1265 int number = textclass.counters().value("bibitem");
1266 //if (!par->bibkey()) {
1267 if (par->bibkey()) {
1268 par->bibkey()->setCounter(number);
1269 par->params().labelString(layout->labelstring());
1272 // InsetCommandParams p("bibitem");
1273 // par->bibkey() = new InsetBibKey(p);
1275 // In biblio should't be following counters but...
1277 string s = layout->labelstring();
1279 // the caption hack:
1280 if (layout->labeltype == LABEL_SENSITIVE) {
1281 Paragraph * tmppar = par;
1284 while (tmppar && tmppar->inInset()
1285 // the single '=' is intended below
1286 && (in = tmppar->inInset()->owner())) {
1287 if (in->lyxCode() == Inset::FLOAT_CODE ||
1288 in->lyxCode() == Inset::WRAP_CODE) {
1292 tmppar = in->parOwner();
1298 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1300 textclass.counters().step(fl.type());
1302 // Doesn't work... yet.
1303 #warning use boost.format
1304 #if USE_BOOST_FORMAT
1305 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1306 // s << boost::format(_("%1$s %1$d:")
1308 // % buf->counters().value(fl.name());
1311 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1312 o << fl.name() << " #:";
1313 s = STRCONV(o.str());
1316 // par->SetLayout(0);
1317 // s = layout->labelstring;
1318 s = _("Senseless: ");
1321 par->params().labelString(s);
1323 // reset the enumeration counter. They are always reset
1324 // when there is any other layout between
1325 // Just fall-through between the cases so that all
1326 // enum counters deeper than enumdepth is also reset.
1327 switch (par->enumdepth) {
1329 textclass.counters().reset("enumi");
1331 textclass.counters().reset("enumii");
1333 textclass.counters().reset("enumiii");
1335 textclass.counters().reset("enumiv");
1341 // Updates all counters. Paragraphs with changed label string will be rebroken
1342 void LyXText::updateCounters(BufferView * bview) const
1344 Row * row = firstrow;
1345 Paragraph * par = row->par();
1347 // CHECK if this is really needed. (Lgb)
1348 bview->buffer()->params.getLyXTextClass().counters().reset();
1351 while (row->par() != par)
1354 string const oldLabel = par->params().labelString();
1356 // setCounter can potentially change the labelString.
1357 setCounter(bview->buffer(), par);
1359 string const & newLabel = par->params().labelString();
1361 if (oldLabel.empty() && !newLabel.empty()) {
1362 removeParagraph(row);
1363 appendParagraph(bview, row);
1371 void LyXText::insertInset(BufferView * bview, Inset * inset)
1373 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1375 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1377 cursor.par()->insertInset(cursor.pos(), inset);
1378 // Just to rebreak and refresh correctly.
1379 // The character will not be inserted a second time
1380 insertChar(bview, Paragraph::META_INSET);
1381 // If we enter a highly editable inset the cursor should be to before
1382 // the inset. This couldn't happen before as Undo was not handled inside
1383 // inset now after the Undo LyX tries to call inset->Edit(...) again
1384 // and cannot do this as the cursor is behind the inset and GetInset
1385 // does not return the inset!
1386 if (isHighlyEditableInset(inset)) {
1387 cursorLeft(bview, true);
1393 void LyXText::copyEnvironmentType()
1395 copylayouttype = cursor.par()->layout()->name();
1399 void LyXText::pasteEnvironmentType(BufferView * bview)
1401 // do nothing if there has been no previous copyEnvironmentType()
1402 if (!copylayouttype.empty())
1403 setLayout(bview, copylayouttype);
1407 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1409 // Stuff what we got on the clipboard. Even if there is no selection.
1411 // There is a problem with having the stuffing here in that the
1412 // larger the selection the slower LyX will get. This can be
1413 // solved by running the line below only when the selection has
1414 // finished. The solution used currently just works, to make it
1415 // faster we need to be more clever and probably also have more
1416 // calls to stuffClipboard. (Lgb)
1417 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1419 // This doesn't make sense, if there is no selection
1420 if (!selection.set())
1423 // OK, we have a selection. This is always between selection.start
1424 // and selection.end
1426 // make sure that the depth behind the selection are restored, too
1427 Paragraph * endpar = selection.end.par()->next();
1428 Paragraph * undoendpar = endpar;
1430 if (endpar && endpar->getDepth()) {
1431 while (endpar && endpar->getDepth()) {
1432 endpar = endpar->next();
1433 undoendpar = endpar;
1435 } else if (endpar) {
1436 endpar = endpar->next(); // because of parindents etc.
1439 setUndo(bview, Undo::DELETE,
1440 selection.start.par(), undoendpar);
1442 // there are two cases: cut only within one paragraph or
1443 // more than one paragraph
1444 if (selection.start.par() == selection.end.par()) {
1445 // only within one paragraph
1446 endpar = selection.end.par();
1447 int pos = selection.end.pos();
1448 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1449 selection.start.pos(), pos,
1450 bview->buffer()->params.textclass,
1452 selection.end.pos(pos);
1454 endpar = selection.end.par();
1455 int pos = selection.end.pos();
1456 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1457 selection.start.pos(), pos,
1458 bview->buffer()->params.textclass,
1461 selection.end.par(endpar);
1462 selection.end.pos(pos);
1463 cursor.pos(selection.end.pos());
1465 endpar = endpar->next();
1467 // sometimes necessary
1469 selection.start.par()->stripLeadingSpaces();
1471 redoParagraphs(bview, selection.start, endpar);
1473 // cutSelection can invalidate the cursor so we need to set
1475 // we prefer the end for when tracking changes
1476 cursor = selection.end;
1478 // need a valid cursor. (Lgb)
1481 setCursor(bview, cursor.par(), cursor.pos());
1482 selection.cursor = cursor;
1483 updateCounters(bview);
1487 void LyXText::copySelection(BufferView * bview)
1489 // stuff the selection onto the X clipboard, from an explicit copy request
1490 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1492 // this doesnt make sense, if there is no selection
1493 if (!selection.set())
1496 // ok we have a selection. This is always between selection.start
1497 // and sel_end cursor
1499 // copy behind a space if there is one
1500 while (selection.start.par()->size() > selection.start.pos()
1501 && selection.start.par()->isLineSeparator(selection.start.pos())
1502 && (selection.start.par() != selection.end.par()
1503 || selection.start.pos() < selection.end.pos()))
1504 selection.start.pos(selection.start.pos() + 1);
1506 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1507 selection.start.pos(), selection.end.pos(),
1508 bview->buffer()->params.textclass);
1512 void LyXText::pasteSelection(BufferView * bview)
1514 // this does not make sense, if there is nothing to paste
1515 if (!CutAndPaste::checkPastePossible(cursor.par()))
1518 setUndo(bview, Undo::INSERT,
1519 cursor.par(), cursor.par()->next());
1522 Paragraph * actpar = cursor.par();
1523 int pos = cursor.pos();
1525 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1526 bview->buffer()->params.textclass);
1528 redoParagraphs(bview, cursor, endpar);
1530 setCursor(bview, cursor.par(), cursor.pos());
1533 selection.cursor = cursor;
1534 setCursor(bview, actpar, pos);
1535 setSelection(bview);
1536 updateCounters(bview);
1540 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1545 selection.cursor = cursor;
1548 setSelection(bview);
1552 // simple replacing. The font of the first selected character is used
1553 void LyXText::replaceSelectionWithString(BufferView * bview,
1556 setCursorParUndo(bview);
1559 if (!selection.set()) { // create a dummy selection
1560 selection.end = cursor;
1561 selection.start = cursor;
1564 // Get font setting before we cut
1565 pos_type pos = selection.end.pos();
1566 LyXFont const font = selection.start.par()
1567 ->getFontSettings(bview->buffer()->params,
1568 selection.start.pos());
1570 // Insert the new string
1571 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1572 selection.end.par()->insertChar(pos, (*cit), font);
1576 // Cut the selection
1577 cutSelection(bview, true, false);
1583 // needed to insert the selection
1584 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1586 Paragraph * par = cursor.par();
1587 pos_type pos = cursor.pos();
1588 Paragraph * endpar = cursor.par()->next();
1590 setCursorParUndo(bview);
1592 // only to be sure, should not be neccessary
1595 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1597 redoParagraphs(bview, cursor, endpar);
1598 setCursor(bview, cursor.par(), cursor.pos());
1599 selection.cursor = cursor;
1600 setCursor(bview, par, pos);
1601 setSelection(bview);
1605 // turns double-CR to single CR, others where converted into one
1606 // blank. Then InsertStringAsLines is called
1607 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1609 string linestr(str);
1610 bool newline_inserted = false;
1611 for (string::size_type i = 0; i < linestr.length(); ++i) {
1612 if (linestr[i] == '\n') {
1613 if (newline_inserted) {
1614 // we know that \r will be ignored by
1615 // InsertStringA. Of course, it is a dirty
1616 // trick, but it works...
1617 linestr[i - 1] = '\r';
1621 newline_inserted = true;
1623 } else if (IsPrintable(linestr[i])) {
1624 newline_inserted = false;
1627 insertStringAsLines(bview, linestr);
1631 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1634 LyXCursor tmpcursor;
1638 Row * row = getRow(par, pos, y);
1640 // is there a break one row above
1641 if (row->previous() && row->previous()->par() == row->par()) {
1642 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1643 if (z >= row->pos()) {
1644 // set the dimensions of the row above
1645 y -= row->previous()->height();
1647 refresh_row = row->previous();
1648 status(bview, LyXText::NEED_MORE_REFRESH);
1650 breakAgain(bview, row->previous());
1652 // set the cursor again. Otherwise
1653 // dangling pointers are possible
1654 setCursor(bview, cursor.par(), cursor.pos(),
1655 false, cursor.boundary());
1656 selection.cursor = cursor;
1661 int const tmpheight = row->height();
1662 pos_type const tmplast = row->lastPos();
1666 breakAgain(bview, row);
1667 if (row->height() == tmpheight && row->lastPos() == tmplast)
1668 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1670 status(bview, LyXText::NEED_MORE_REFRESH);
1672 // check the special right address boxes
1673 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1680 redoDrawingOfParagraph(bview, tmpcursor);
1683 // set the cursor again. Otherwise dangling pointers are possible
1684 // also set the selection
1686 if (selection.set()) {
1688 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1689 false, selection.cursor.boundary());
1690 selection.cursor = cursor;
1691 setCursorIntern(bview, selection.start.par(),
1692 selection.start.pos(),
1693 false, selection.start.boundary());
1694 selection.start = cursor;
1695 setCursorIntern(bview, selection.end.par(),
1696 selection.end.pos(),
1697 false, selection.end.boundary());
1698 selection.end = cursor;
1699 setCursorIntern(bview, last_sel_cursor.par(),
1700 last_sel_cursor.pos(),
1701 false, last_sel_cursor.boundary());
1702 last_sel_cursor = cursor;
1705 setCursorIntern(bview, cursor.par(), cursor.pos(),
1706 false, cursor.boundary());
1710 // returns false if inset wasn't found
1711 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1713 // first check the current paragraph
1714 int pos = cursor.par()->getPositionOfInset(inset);
1716 checkParagraph(bview, cursor.par(), pos);
1720 // check every paragraph
1722 Paragraph * par = ownerParagraph();
1724 pos = par->getPositionOfInset(inset);
1726 checkParagraph(bview, par, pos);
1736 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1738 bool setfont, bool boundary) const
1740 LyXCursor old_cursor = cursor;
1741 setCursorIntern(bview, par, pos, setfont, boundary);
1742 return deleteEmptyParagraphMechanism(bview, old_cursor);
1746 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1747 pos_type pos, bool boundary) const
1754 cur.boundary(boundary);
1756 // get the cursor y position in text
1758 Row * row = getRow(par, pos, y);
1759 Row * old_row = row;
1761 // if we are before the first char of this row and are still in the
1762 // same paragraph and there is a previous row then put the cursor on
1763 // the end of the previous row
1764 cur.iy(y + row->baseline());
1766 if (row->previous() && pos &&
1767 row->previous()->par() == row->par() &&
1768 par->getChar(pos) == Paragraph::META_INSET &&
1769 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1771 row = row->previous();
1776 // y is now the beginning of the cursor row
1777 y += row->baseline();
1778 // y is now the cursor baseline
1781 pos_type last = old_row->lastPrintablePos();
1783 // None of these should happen, but we're scaredy-cats
1784 if (pos > par->size()) {
1787 } else if (pos > last + 1) {
1788 // This shouldn't happen.
1791 } else if (pos < row->pos()) {
1796 // now get the cursors x position
1797 float x = getCursorX(bview, row, pos, last, boundary);
1800 if (old_row != row) {
1801 x = getCursorX(bview, old_row, pos, last, boundary);
1808 float LyXText::getCursorX(BufferView * bview, Row * row,
1809 pos_type pos, pos_type last, bool boundary) const
1811 pos_type cursor_vpos = 0;
1813 float fill_separator;
1815 float fill_label_hfill;
1816 // This call HAS to be here because of the BidiTables!!!
1817 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1820 if (last < row->pos())
1821 cursor_vpos = row->pos();
1822 else if (pos > last && !boundary)
1823 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1824 ? row->pos() : last + 1;
1825 else if (pos > row->pos() &&
1826 (pos > last || boundary))
1827 /// Place cursor after char at (logical) position pos - 1
1828 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1829 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1831 /// Place cursor before char at (logical) position pos
1832 cursor_vpos = (bidi_level(pos) % 2 == 0)
1833 ? log2vis(pos) : log2vis(pos) + 1;
1835 pos_type main_body = row->par()->beginningOfMainBody();
1836 if ((main_body > 0) &&
1837 ((main_body-1 > last) ||
1838 !row->par()->isLineSeparator(main_body-1)))
1841 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1842 pos_type pos = vis2log(vpos);
1843 if (main_body > 0 && pos == main_body - 1) {
1844 x += fill_label_hfill +
1845 font_metrics::width(
1846 row->par()->layout()->labelsep,
1847 getLabelFont(bview->buffer(),
1849 if (row->par()->isLineSeparator(main_body - 1))
1850 x -= singleWidth(bview,
1851 row->par(), main_body - 1);
1853 if (row->hfillExpansion(pos)) {
1854 x += singleWidth(bview, row->par(), pos);
1855 if (pos >= main_body)
1858 x += fill_label_hfill;
1859 } else if (row->par()->isSeparator(pos)) {
1860 x += singleWidth(bview, row->par(), pos);
1861 if (pos >= main_body)
1862 x += fill_separator;
1864 x += singleWidth(bview, row->par(), pos);
1870 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1871 pos_type pos, bool setfont, bool boundary) const
1873 InsetText * it = static_cast<InsetText *>(par->inInset());
1875 if (it != inset_owner) {
1876 lyxerr[Debug::INSETS] << "InsetText is " << it
1878 << "inset_owner is "
1879 << inset_owner << endl;
1880 #ifdef WITH_WARNINGS
1881 #warning I believe this code is wrong. (Lgb)
1882 #warning Jürgen, have a look at this. (Lgb)
1883 #warning Hmmm, I guess you are right but we
1884 #warning should verify when this is needed
1886 // Jürgen, would you like to have a look?
1887 // I guess we need to move the outer cursor
1888 // and open and lock the inset (bla bla bla)
1889 // stuff I don't know... so can you have a look?
1891 // I moved the lyxerr stuff in here so we can see if
1892 // this is actually really needed and where!
1894 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1899 setCursor(bview, cursor, par, pos, boundary);
1901 setCurrentFont(bview);
1905 void LyXText::setCurrentFont(BufferView * bview) const
1907 pos_type pos = cursor.pos();
1908 if (cursor.boundary() && pos > 0)
1912 if (pos == cursor.par()->size())
1914 else // potentional bug... BUG (Lgb)
1915 if (cursor.par()->isSeparator(pos)) {
1916 if (pos > cursor.row()->pos() &&
1917 bidi_level(pos) % 2 ==
1918 bidi_level(pos - 1) % 2)
1920 else if (pos + 1 < cursor.par()->size())
1926 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1927 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1929 if (cursor.pos() == cursor.par()->size() &&
1930 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1931 !cursor.boundary()) {
1932 Language const * lang =
1933 cursor.par()->getParLanguage(bview->buffer()->params);
1934 current_font.setLanguage(lang);
1935 current_font.setNumber(LyXFont::OFF);
1936 real_current_font.setLanguage(lang);
1937 real_current_font.setNumber(LyXFont::OFF);
1942 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1944 LyXCursor old_cursor = cursor;
1946 setCursorFromCoordinates(bview, cursor, x, y);
1947 setCurrentFont(bview);
1948 deleteEmptyParagraphMechanism(bview, old_cursor);
1955 * return true if the cursor given is at the end of a row,
1956 * and the next row is filled by an inset that spans an entire
1959 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1962 Row const & next = *row.next();
1964 if (next.pos() != cur.pos() || next.par() != cur.par())
1966 if (!cur.par()->isInset(cur.pos()))
1968 Inset const * inset = cur.par()->getInset(cur.pos());
1969 if (inset->needFullRow() || inset->display())
1976 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1979 // Get the row first.
1981 Row * row = getRowNearY(y);
1983 pos_type const column = getColumnNearX(bview, row, x, bound);
1984 cur.par(row->par());
1985 cur.pos(row->pos() + column);
1987 cur.y(y + row->baseline());
1990 if (beforeFullRowInset(*row, cur)) {
1991 pos_type last = row->lastPrintablePos();
1992 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
1994 cur.iy(y + row->height() + row->next()->baseline());
1995 cur.irow(row->next());
2001 cur.boundary(bound);
2005 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2007 if (cursor.pos() > 0) {
2008 bool boundary = cursor.boundary();
2009 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2010 if (!internal && !boundary &&
2011 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2012 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2013 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2014 Paragraph * par = cursor.par()->previous();
2015 setCursor(bview, par, par->size());
2020 void LyXText::cursorRight(BufferView * bview, bool internal) const
2022 if (!internal && cursor.boundary() &&
2023 !cursor.par()->isNewline(cursor.pos()))
2024 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2025 else if (cursor.pos() < cursor.par()->size()) {
2026 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2028 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2029 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2030 } else if (cursor.par()->next())
2031 setCursor(bview, cursor.par()->next(), 0);
2035 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2038 int x = cursor.x_fix();
2039 int y = cursor.y() - cursor.row()->baseline() - 1;
2040 setCursorFromCoordinates(bview, x, y);
2042 int y1 = cursor.iy() - first_y;
2045 Inset * inset_hit = checkInsetHit(bview, x, y1);
2046 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2047 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2051 setCursorFromCoordinates(bview, cursor.x_fix(),
2052 cursor.y() - cursor.row()->baseline() - 1);
2057 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2060 int x = cursor.x_fix();
2061 int y = cursor.y() - cursor.row()->baseline() +
2062 cursor.row()->height() + 1;
2063 setCursorFromCoordinates(bview, x, y);
2064 if (!selecting && cursor.row() == cursor.irow()) {
2065 int y1 = cursor.iy() - first_y;
2068 Inset * inset_hit = checkInsetHit(bview, x, y1);
2069 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2070 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2074 setCursorFromCoordinates(bview, cursor.x_fix(),
2075 cursor.y() - cursor.row()->baseline()
2076 + cursor.row()->height() + 1);
2081 void LyXText::cursorUpParagraph(BufferView * bview) const
2083 if (cursor.pos() > 0) {
2084 setCursor(bview, cursor.par(), 0);
2086 else if (cursor.par()->previous()) {
2087 setCursor(bview, cursor.par()->previous(), 0);
2092 void LyXText::cursorDownParagraph(BufferView * bview) const
2094 if (cursor.par()->next()) {
2095 setCursor(bview, cursor.par()->next(), 0);
2097 setCursor(bview, cursor.par(), cursor.par()->size());
2101 // fix the cursor `cur' after a characters has been deleted at `where'
2102 // position. Called by deleteEmptyParagraphMechanism
2103 void LyXText::fixCursorAfterDelete(BufferView * bview,
2105 LyXCursor const & where) const
2107 // if cursor is not in the paragraph where the delete occured,
2109 if (cur.par() != where.par())
2112 // if cursor position is after the place where the delete occured,
2114 if (cur.pos() > where.pos())
2115 cur.pos(cur.pos()-1);
2117 // check also if we don't want to set the cursor on a spot behind the
2118 // pagragraph because we erased the last character.
2119 if (cur.pos() > cur.par()->size())
2120 cur.pos(cur.par()->size());
2122 // recompute row et al. for this cursor
2123 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2127 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2128 LyXCursor const & old_cursor) const
2130 // Would be wrong to delete anything if we have a selection.
2131 if (selection.set())
2134 // We allow all kinds of "mumbo-jumbo" when freespacing.
2135 if (old_cursor.par()->layout()->free_spacing
2136 || old_cursor.par()->isFreeSpacing()) {
2140 /* Ok I'll put some comments here about what is missing.
2141 I have fixed BackSpace (and thus Delete) to not delete
2142 double-spaces automagically. I have also changed Cut,
2143 Copy and Paste to hopefully do some sensible things.
2144 There are still some small problems that can lead to
2145 double spaces stored in the document file or space at
2146 the beginning of paragraphs. This happens if you have
2147 the cursor betwenn to spaces and then save. Or if you
2148 cut and paste and the selection have a space at the
2149 beginning and then save right after the paste. I am
2150 sure none of these are very hard to fix, but I will
2151 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2152 that I can get some feedback. (Lgb)
2155 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2156 // delete the LineSeparator.
2159 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2160 // delete the LineSeparator.
2163 // If the pos around the old_cursor were spaces, delete one of them.
2164 if (old_cursor.par() != cursor.par()
2165 || old_cursor.pos() != cursor.pos()) {
2166 // Only if the cursor has really moved
2168 if (old_cursor.pos() > 0
2169 && old_cursor.pos() < old_cursor.par()->size()
2170 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2171 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2172 old_cursor.par()->erase(old_cursor.pos() - 1);
2173 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2175 #ifdef WITH_WARNINGS
2176 #warning This will not work anymore when we have multiple views of the same buffer
2177 // In this case, we will have to correct also the cursors held by
2178 // other bufferviews. It will probably be easier to do that in a more
2179 // automated way in LyXCursor code. (JMarc 26/09/2001)
2181 // correct all cursors held by the LyXText
2182 fixCursorAfterDelete(bview, cursor, old_cursor);
2183 fixCursorAfterDelete(bview, selection.cursor,
2185 fixCursorAfterDelete(bview, selection.start,
2187 fixCursorAfterDelete(bview, selection.end, old_cursor);
2188 fixCursorAfterDelete(bview, last_sel_cursor,
2190 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2191 fixCursorAfterDelete(bview, toggle_end_cursor,
2197 // don't delete anything if this is the ONLY paragraph!
2198 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2201 // Do not delete empty paragraphs with keepempty set.
2202 if (old_cursor.par()->layout()->keepempty)
2205 // only do our magic if we changed paragraph
2206 if (old_cursor.par() == cursor.par())
2209 // record if we have deleted a paragraph
2210 // we can't possibly have deleted a paragraph before this point
2211 bool deleted = false;
2213 if ((old_cursor.par()->empty()
2214 || (old_cursor.par()->size() == 1
2215 && old_cursor.par()->isLineSeparator(0)))) {
2216 // ok, we will delete anything
2217 LyXCursor tmpcursor;
2219 // make sure that you do not delete any environments
2220 status(bview, LyXText::NEED_MORE_REFRESH);
2223 if (old_cursor.row()->previous()) {
2224 refresh_row = old_cursor.row()->previous();
2225 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2227 cursor = old_cursor; // that undo can restore the right cursor position
2228 Paragraph * endpar = old_cursor.par()->next();
2229 if (endpar && endpar->getDepth()) {
2230 while (endpar && endpar->getDepth()) {
2231 endpar = endpar->next();
2234 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2238 removeRow(old_cursor.row());
2239 if (ownerParagraph() == old_cursor.par()) {
2240 ownerParagraph(ownerParagraph()->next());
2243 delete old_cursor.par();
2245 /* Breakagain the next par. Needed because of
2246 * the parindent that can occur or dissappear.
2247 * The next row can change its height, if
2248 * there is another layout before */
2249 if (refresh_row->next()) {
2250 breakAgain(bview, refresh_row->next());
2251 updateCounters(bview);
2253 setHeightOfRow(bview, refresh_row);
2255 refresh_row = old_cursor.row()->next();
2256 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2259 cursor = old_cursor; // that undo can restore the right cursor position
2260 Paragraph * endpar = old_cursor.par()->next();
2261 if (endpar && endpar->getDepth()) {
2262 while (endpar && endpar->getDepth()) {
2263 endpar = endpar->next();
2266 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2270 removeRow(old_cursor.row());
2272 if (ownerParagraph() == old_cursor.par()) {
2273 ownerParagraph(ownerParagraph()->next());
2276 delete old_cursor.par();
2278 /* Breakagain the next par. Needed because of
2279 the parindent that can occur or dissappear.
2280 The next row can change its height, if
2281 there is another layout before */
2283 breakAgain(bview, refresh_row);
2284 updateCounters(bview);
2289 setCursorIntern(bview, cursor.par(), cursor.pos());
2291 if (selection.cursor.par() == old_cursor.par()
2292 && selection.cursor.pos() == old_cursor.pos()) {
2293 // correct selection
2294 selection.cursor = cursor;
2298 if (old_cursor.par()->stripLeadingSpaces()) {
2299 redoParagraphs(bview, old_cursor,
2300 old_cursor.par()->next());
2302 setCursorIntern(bview, cursor.par(), cursor.pos());
2303 selection.cursor = cursor;
2310 Paragraph * LyXText::ownerParagraph() const
2313 return inset_owner->paragraph();
2315 return &*(bv_owner->buffer()->paragraphs.begin());
2319 void LyXText::ownerParagraph(Paragraph * p) const
2322 inset_owner->paragraph(p);
2324 bv_owner->buffer()->paragraphs.set(p);
2329 void LyXText::ownerParagraph(int id, Paragraph * p) const
2331 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2332 if (op && op->inInset()) {
2333 static_cast<InsetText *>(op->inInset())->paragraph(p);
2340 LyXText::text_status LyXText::status() const
2346 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2348 LyXText * t = bview->text;
2350 // We should only go up with refreshing code so this means that if
2351 // we have a MORE refresh we should never set it to LITTLE if we still
2352 // didn't handle it (and then it will be UNCHANGED. Now as long as
2353 // we stay inside one LyXText this may work but we need to tell the
2354 // outermost LyXText that it should REALLY draw us if there is some
2355 // change in a Inset::LyXText. So you see that when we are inside a
2356 // inset's LyXText we give the LITTLE to the outermost LyXText to
2357 // tell'em that it should redraw the actual row (where the inset
2358 // resides! Capito?!
2360 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2362 if (inset_owner && st != UNCHANGED) {
2363 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2364 if (!t->refresh_row) {
2365 t->refresh_row = t->cursor.row();
2366 t->refresh_y = t->cursor.y() -
2367 t->cursor.row()->baseline();
2374 bool LyXText::isTopLevel() const
2376 /// only the top-level lyxtext has a non-null bv owner
2381 int defaultRowHeight()
2383 LyXFont const font(LyXFont::ALL_SANE);
2384 return int(font_metrics::maxAscent(font)
2385 + font_metrics::maxDescent(font) * 1.5);