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 // Inset * inset = new InsetBibKey(InsetCommandParams("bibitem"));
1268 // //par->insertInset(0, inset);
1270 if (par->bibkey()) {
1271 par->bibkey()->setCounter(number);
1272 par->params().labelString(layout->labelstring());
1274 // In biblio should't be following counters but...
1276 string s = layout->labelstring();
1278 // the caption hack:
1279 if (layout->labeltype == LABEL_SENSITIVE) {
1280 Paragraph * tmppar = par;
1283 while (tmppar && tmppar->inInset()
1284 // the single '=' is intended below
1285 && (in = tmppar->inInset()->owner())) {
1286 if (in->lyxCode() == Inset::FLOAT_CODE ||
1287 in->lyxCode() == Inset::WRAP_CODE) {
1291 tmppar = in->parOwner();
1297 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1299 textclass.counters().step(fl.type());
1301 // Doesn't work... yet.
1302 #warning use boost.format
1303 #if USE_BOOST_FORMAT
1304 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1305 // s << boost::format(_("%1$s %1$d:")
1307 // % buf->counters().value(fl.name());
1310 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1311 o << fl.name() << " #:";
1312 s = STRCONV(o.str());
1315 // par->SetLayout(0);
1316 // s = layout->labelstring;
1317 s = _("Senseless: ");
1320 par->params().labelString(s);
1322 // reset the enumeration counter. They are always reset
1323 // when there is any other layout between
1324 // Just fall-through between the cases so that all
1325 // enum counters deeper than enumdepth is also reset.
1326 switch (par->enumdepth) {
1328 textclass.counters().reset("enumi");
1330 textclass.counters().reset("enumii");
1332 textclass.counters().reset("enumiii");
1334 textclass.counters().reset("enumiv");
1340 // Updates all counters. Paragraphs with changed label string will be rebroken
1341 void LyXText::updateCounters(BufferView * bview) const
1343 Row * row = firstrow;
1344 Paragraph * par = row->par();
1346 // CHECK if this is really needed. (Lgb)
1347 bview->buffer()->params.getLyXTextClass().counters().reset();
1350 while (row->par() != par)
1353 string const oldLabel = par->params().labelString();
1355 // setCounter can potentially change the labelString.
1356 setCounter(bview->buffer(), par);
1358 string const & newLabel = par->params().labelString();
1360 if (oldLabel.empty() && !newLabel.empty()) {
1361 removeParagraph(row);
1362 appendParagraph(bview, row);
1370 void LyXText::insertInset(BufferView * bview, Inset * inset)
1372 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1374 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1376 cursor.par()->insertInset(cursor.pos(), inset);
1377 // Just to rebreak and refresh correctly.
1378 // The character will not be inserted a second time
1379 insertChar(bview, Paragraph::META_INSET);
1380 // If we enter a highly editable inset the cursor should be to before
1381 // the inset. This couldn't happen before as Undo was not handled inside
1382 // inset now after the Undo LyX tries to call inset->Edit(...) again
1383 // and cannot do this as the cursor is behind the inset and GetInset
1384 // does not return the inset!
1385 if (isHighlyEditableInset(inset)) {
1386 cursorLeft(bview, true);
1392 void LyXText::copyEnvironmentType()
1394 copylayouttype = cursor.par()->layout()->name();
1398 void LyXText::pasteEnvironmentType(BufferView * bview)
1400 // do nothing if there has been no previous copyEnvironmentType()
1401 if (!copylayouttype.empty())
1402 setLayout(bview, copylayouttype);
1406 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1408 // Stuff what we got on the clipboard. Even if there is no selection.
1410 // There is a problem with having the stuffing here in that the
1411 // larger the selection the slower LyX will get. This can be
1412 // solved by running the line below only when the selection has
1413 // finished. The solution used currently just works, to make it
1414 // faster we need to be more clever and probably also have more
1415 // calls to stuffClipboard. (Lgb)
1416 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1418 // This doesn't make sense, if there is no selection
1419 if (!selection.set())
1422 // OK, we have a selection. This is always between selection.start
1423 // and selection.end
1425 // make sure that the depth behind the selection are restored, too
1426 Paragraph * endpar = selection.end.par()->next();
1427 Paragraph * undoendpar = endpar;
1429 if (endpar && endpar->getDepth()) {
1430 while (endpar && endpar->getDepth()) {
1431 endpar = endpar->next();
1432 undoendpar = endpar;
1434 } else if (endpar) {
1435 endpar = endpar->next(); // because of parindents etc.
1438 setUndo(bview, Undo::DELETE,
1439 selection.start.par(), undoendpar);
1441 // there are two cases: cut only within one paragraph or
1442 // more than one paragraph
1443 if (selection.start.par() == selection.end.par()) {
1444 // only within one paragraph
1445 endpar = selection.end.par();
1446 int pos = selection.end.pos();
1447 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1448 selection.start.pos(), pos,
1449 bview->buffer()->params.textclass,
1451 selection.end.pos(pos);
1453 endpar = selection.end.par();
1454 int pos = selection.end.pos();
1455 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1456 selection.start.pos(), pos,
1457 bview->buffer()->params.textclass,
1460 selection.end.par(endpar);
1461 selection.end.pos(pos);
1462 cursor.pos(selection.end.pos());
1464 endpar = endpar->next();
1466 // sometimes necessary
1468 selection.start.par()->stripLeadingSpaces();
1470 redoParagraphs(bview, selection.start, endpar);
1472 // cutSelection can invalidate the cursor so we need to set
1474 // we prefer the end for when tracking changes
1475 cursor = selection.end;
1477 // need a valid cursor. (Lgb)
1480 setCursor(bview, cursor.par(), cursor.pos());
1481 selection.cursor = cursor;
1482 updateCounters(bview);
1486 void LyXText::copySelection(BufferView * bview)
1488 // stuff the selection onto the X clipboard, from an explicit copy request
1489 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1491 // this doesnt make sense, if there is no selection
1492 if (!selection.set())
1495 // ok we have a selection. This is always between selection.start
1496 // and sel_end cursor
1498 // copy behind a space if there is one
1499 while (selection.start.par()->size() > selection.start.pos()
1500 && selection.start.par()->isLineSeparator(selection.start.pos())
1501 && (selection.start.par() != selection.end.par()
1502 || selection.start.pos() < selection.end.pos()))
1503 selection.start.pos(selection.start.pos() + 1);
1505 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1506 selection.start.pos(), selection.end.pos(),
1507 bview->buffer()->params.textclass);
1511 void LyXText::pasteSelection(BufferView * bview)
1513 // this does not make sense, if there is nothing to paste
1514 if (!CutAndPaste::checkPastePossible(cursor.par()))
1517 setUndo(bview, Undo::INSERT,
1518 cursor.par(), cursor.par()->next());
1521 Paragraph * actpar = cursor.par();
1522 int pos = cursor.pos();
1524 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1525 bview->buffer()->params.textclass);
1527 redoParagraphs(bview, cursor, endpar);
1529 setCursor(bview, cursor.par(), cursor.pos());
1532 selection.cursor = cursor;
1533 setCursor(bview, actpar, pos);
1534 setSelection(bview);
1535 updateCounters(bview);
1539 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1544 selection.cursor = cursor;
1547 setSelection(bview);
1551 // simple replacing. The font of the first selected character is used
1552 void LyXText::replaceSelectionWithString(BufferView * bview,
1555 setCursorParUndo(bview);
1558 if (!selection.set()) { // create a dummy selection
1559 selection.end = cursor;
1560 selection.start = cursor;
1563 // Get font setting before we cut
1564 pos_type pos = selection.end.pos();
1565 LyXFont const font = selection.start.par()
1566 ->getFontSettings(bview->buffer()->params,
1567 selection.start.pos());
1569 // Insert the new string
1570 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1571 selection.end.par()->insertChar(pos, (*cit), font);
1575 // Cut the selection
1576 cutSelection(bview, true, false);
1582 // needed to insert the selection
1583 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1585 Paragraph * par = cursor.par();
1586 pos_type pos = cursor.pos();
1587 Paragraph * endpar = cursor.par()->next();
1589 setCursorParUndo(bview);
1591 // only to be sure, should not be neccessary
1594 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1596 redoParagraphs(bview, cursor, endpar);
1597 setCursor(bview, cursor.par(), cursor.pos());
1598 selection.cursor = cursor;
1599 setCursor(bview, par, pos);
1600 setSelection(bview);
1604 // turns double-CR to single CR, others where converted into one
1605 // blank. Then InsertStringAsLines is called
1606 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1608 string linestr(str);
1609 bool newline_inserted = false;
1610 for (string::size_type i = 0; i < linestr.length(); ++i) {
1611 if (linestr[i] == '\n') {
1612 if (newline_inserted) {
1613 // we know that \r will be ignored by
1614 // InsertStringA. Of course, it is a dirty
1615 // trick, but it works...
1616 linestr[i - 1] = '\r';
1620 newline_inserted = true;
1622 } else if (IsPrintable(linestr[i])) {
1623 newline_inserted = false;
1626 insertStringAsLines(bview, linestr);
1630 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1633 LyXCursor tmpcursor;
1637 Row * row = getRow(par, pos, y);
1639 // is there a break one row above
1640 if (row->previous() && row->previous()->par() == row->par()) {
1641 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1642 if (z >= row->pos()) {
1643 // set the dimensions of the row above
1644 y -= row->previous()->height();
1646 refresh_row = row->previous();
1647 status(bview, LyXText::NEED_MORE_REFRESH);
1649 breakAgain(bview, row->previous());
1651 // set the cursor again. Otherwise
1652 // dangling pointers are possible
1653 setCursor(bview, cursor.par(), cursor.pos(),
1654 false, cursor.boundary());
1655 selection.cursor = cursor;
1660 int const tmpheight = row->height();
1661 pos_type const tmplast = row->lastPos();
1665 breakAgain(bview, row);
1666 if (row->height() == tmpheight && row->lastPos() == tmplast)
1667 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1669 status(bview, LyXText::NEED_MORE_REFRESH);
1671 // check the special right address boxes
1672 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1679 redoDrawingOfParagraph(bview, tmpcursor);
1682 // set the cursor again. Otherwise dangling pointers are possible
1683 // also set the selection
1685 if (selection.set()) {
1687 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1688 false, selection.cursor.boundary());
1689 selection.cursor = cursor;
1690 setCursorIntern(bview, selection.start.par(),
1691 selection.start.pos(),
1692 false, selection.start.boundary());
1693 selection.start = cursor;
1694 setCursorIntern(bview, selection.end.par(),
1695 selection.end.pos(),
1696 false, selection.end.boundary());
1697 selection.end = cursor;
1698 setCursorIntern(bview, last_sel_cursor.par(),
1699 last_sel_cursor.pos(),
1700 false, last_sel_cursor.boundary());
1701 last_sel_cursor = cursor;
1704 setCursorIntern(bview, cursor.par(), cursor.pos(),
1705 false, cursor.boundary());
1709 // returns false if inset wasn't found
1710 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1712 // first check the current paragraph
1713 int pos = cursor.par()->getPositionOfInset(inset);
1715 checkParagraph(bview, cursor.par(), pos);
1719 // check every paragraph
1721 Paragraph * par = ownerParagraph();
1723 pos = par->getPositionOfInset(inset);
1725 checkParagraph(bview, par, pos);
1735 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1737 bool setfont, bool boundary) const
1739 LyXCursor old_cursor = cursor;
1740 setCursorIntern(bview, par, pos, setfont, boundary);
1741 return deleteEmptyParagraphMechanism(bview, old_cursor);
1745 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1746 pos_type pos, bool boundary) const
1753 cur.boundary(boundary);
1755 // get the cursor y position in text
1757 Row * row = getRow(par, pos, y);
1758 Row * old_row = row;
1760 // if we are before the first char of this row and are still in the
1761 // same paragraph and there is a previous row then put the cursor on
1762 // the end of the previous row
1763 cur.iy(y + row->baseline());
1765 if (row->previous() && pos &&
1766 row->previous()->par() == row->par() &&
1767 par->getChar(pos) == Paragraph::META_INSET &&
1768 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1770 row = row->previous();
1775 // y is now the beginning of the cursor row
1776 y += row->baseline();
1777 // y is now the cursor baseline
1780 pos_type last = old_row->lastPrintablePos();
1782 // None of these should happen, but we're scaredy-cats
1783 if (pos > par->size()) {
1786 } else if (pos > last + 1) {
1787 // This shouldn't happen.
1790 } else if (pos < row->pos()) {
1795 // now get the cursors x position
1796 float x = getCursorX(bview, row, pos, last, boundary);
1799 if (old_row != row) {
1800 x = getCursorX(bview, old_row, pos, last, boundary);
1807 float LyXText::getCursorX(BufferView * bview, Row * row,
1808 pos_type pos, pos_type last, bool boundary) const
1810 pos_type cursor_vpos = 0;
1812 float fill_separator;
1814 float fill_label_hfill;
1815 // This call HAS to be here because of the BidiTables!!!
1816 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1819 if (last < row->pos())
1820 cursor_vpos = row->pos();
1821 else if (pos > last && !boundary)
1822 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1823 ? row->pos() : last + 1;
1824 else if (pos > row->pos() &&
1825 (pos > last || boundary))
1826 /// Place cursor after char at (logical) position pos - 1
1827 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1828 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1830 /// Place cursor before char at (logical) position pos
1831 cursor_vpos = (bidi_level(pos) % 2 == 0)
1832 ? log2vis(pos) : log2vis(pos) + 1;
1834 pos_type main_body = row->par()->beginningOfMainBody();
1835 if ((main_body > 0) &&
1836 ((main_body-1 > last) ||
1837 !row->par()->isLineSeparator(main_body-1)))
1840 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1841 pos_type pos = vis2log(vpos);
1842 if (main_body > 0 && pos == main_body - 1) {
1843 x += fill_label_hfill +
1844 font_metrics::width(
1845 row->par()->layout()->labelsep,
1846 getLabelFont(bview->buffer(),
1848 if (row->par()->isLineSeparator(main_body - 1))
1849 x -= singleWidth(bview,
1850 row->par(), main_body - 1);
1852 if (row->hfillExpansion(pos)) {
1853 x += singleWidth(bview, row->par(), pos);
1854 if (pos >= main_body)
1857 x += fill_label_hfill;
1858 } else if (row->par()->isSeparator(pos)) {
1859 x += singleWidth(bview, row->par(), pos);
1860 if (pos >= main_body)
1861 x += fill_separator;
1863 x += singleWidth(bview, row->par(), pos);
1869 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1870 pos_type pos, bool setfont, bool boundary) const
1872 InsetText * it = static_cast<InsetText *>(par->inInset());
1874 if (it != inset_owner) {
1875 lyxerr[Debug::INSETS] << "InsetText is " << it
1877 << "inset_owner is "
1878 << inset_owner << endl;
1879 #ifdef WITH_WARNINGS
1880 #warning I believe this code is wrong. (Lgb)
1881 #warning Jürgen, have a look at this. (Lgb)
1882 #warning Hmmm, I guess you are right but we
1883 #warning should verify when this is needed
1885 // Jürgen, would you like to have a look?
1886 // I guess we need to move the outer cursor
1887 // and open and lock the inset (bla bla bla)
1888 // stuff I don't know... so can you have a look?
1890 // I moved the lyxerr stuff in here so we can see if
1891 // this is actually really needed and where!
1893 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1898 setCursor(bview, cursor, par, pos, boundary);
1900 setCurrentFont(bview);
1904 void LyXText::setCurrentFont(BufferView * bview) const
1906 pos_type pos = cursor.pos();
1907 if (cursor.boundary() && pos > 0)
1911 if (pos == cursor.par()->size())
1913 else // potentional bug... BUG (Lgb)
1914 if (cursor.par()->isSeparator(pos)) {
1915 if (pos > cursor.row()->pos() &&
1916 bidi_level(pos) % 2 ==
1917 bidi_level(pos - 1) % 2)
1919 else if (pos + 1 < cursor.par()->size())
1925 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1926 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1928 if (cursor.pos() == cursor.par()->size() &&
1929 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1930 !cursor.boundary()) {
1931 Language const * lang =
1932 cursor.par()->getParLanguage(bview->buffer()->params);
1933 current_font.setLanguage(lang);
1934 current_font.setNumber(LyXFont::OFF);
1935 real_current_font.setLanguage(lang);
1936 real_current_font.setNumber(LyXFont::OFF);
1941 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1943 LyXCursor old_cursor = cursor;
1945 setCursorFromCoordinates(bview, cursor, x, y);
1946 setCurrentFont(bview);
1947 deleteEmptyParagraphMechanism(bview, old_cursor);
1954 * return true if the cursor given is at the end of a row,
1955 * and the next row is filled by an inset that spans an entire
1958 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1961 Row const & next = *row.next();
1963 if (next.pos() != cur.pos() || next.par() != cur.par())
1965 if (!cur.par()->isInset(cur.pos()))
1967 Inset const * inset = cur.par()->getInset(cur.pos());
1968 if (inset->needFullRow() || inset->display())
1975 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1978 // Get the row first.
1980 Row * row = getRowNearY(y);
1982 pos_type const column = getColumnNearX(bview, row, x, bound);
1983 cur.par(row->par());
1984 cur.pos(row->pos() + column);
1986 cur.y(y + row->baseline());
1989 if (beforeFullRowInset(*row, cur)) {
1990 pos_type last = row->lastPrintablePos();
1991 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
1993 cur.iy(y + row->height() + row->next()->baseline());
1994 cur.irow(row->next());
2000 cur.boundary(bound);
2004 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2006 if (cursor.pos() > 0) {
2007 bool boundary = cursor.boundary();
2008 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2009 if (!internal && !boundary &&
2010 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2011 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2012 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2013 Paragraph * par = cursor.par()->previous();
2014 setCursor(bview, par, par->size());
2019 void LyXText::cursorRight(BufferView * bview, bool internal) const
2021 if (!internal && cursor.boundary() &&
2022 !cursor.par()->isNewline(cursor.pos()))
2023 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2024 else if (cursor.pos() < cursor.par()->size()) {
2025 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2027 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2028 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2029 } else if (cursor.par()->next())
2030 setCursor(bview, cursor.par()->next(), 0);
2034 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2037 int x = cursor.x_fix();
2038 int y = cursor.y() - cursor.row()->baseline() - 1;
2039 setCursorFromCoordinates(bview, x, y);
2041 int y1 = cursor.iy() - first_y;
2044 Inset * inset_hit = checkInsetHit(bview, x, y1);
2045 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2046 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2050 setCursorFromCoordinates(bview, cursor.x_fix(),
2051 cursor.y() - cursor.row()->baseline() - 1);
2056 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2059 int x = cursor.x_fix();
2060 int y = cursor.y() - cursor.row()->baseline() +
2061 cursor.row()->height() + 1;
2062 setCursorFromCoordinates(bview, x, y);
2063 if (!selecting && cursor.row() == cursor.irow()) {
2064 int y1 = cursor.iy() - first_y;
2067 Inset * inset_hit = checkInsetHit(bview, x, y1);
2068 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2069 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2073 setCursorFromCoordinates(bview, cursor.x_fix(),
2074 cursor.y() - cursor.row()->baseline()
2075 + cursor.row()->height() + 1);
2080 void LyXText::cursorUpParagraph(BufferView * bview) const
2082 if (cursor.pos() > 0) {
2083 setCursor(bview, cursor.par(), 0);
2085 else if (cursor.par()->previous()) {
2086 setCursor(bview, cursor.par()->previous(), 0);
2091 void LyXText::cursorDownParagraph(BufferView * bview) const
2093 if (cursor.par()->next()) {
2094 setCursor(bview, cursor.par()->next(), 0);
2096 setCursor(bview, cursor.par(), cursor.par()->size());
2100 // fix the cursor `cur' after a characters has been deleted at `where'
2101 // position. Called by deleteEmptyParagraphMechanism
2102 void LyXText::fixCursorAfterDelete(BufferView * bview,
2104 LyXCursor const & where) const
2106 // if cursor is not in the paragraph where the delete occured,
2108 if (cur.par() != where.par())
2111 // if cursor position is after the place where the delete occured,
2113 if (cur.pos() > where.pos())
2114 cur.pos(cur.pos()-1);
2116 // check also if we don't want to set the cursor on a spot behind the
2117 // pagragraph because we erased the last character.
2118 if (cur.pos() > cur.par()->size())
2119 cur.pos(cur.par()->size());
2121 // recompute row et al. for this cursor
2122 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2126 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2127 LyXCursor const & old_cursor) const
2129 // Would be wrong to delete anything if we have a selection.
2130 if (selection.set())
2133 // We allow all kinds of "mumbo-jumbo" when freespacing.
2134 if (old_cursor.par()->layout()->free_spacing
2135 || old_cursor.par()->isFreeSpacing()) {
2139 /* Ok I'll put some comments here about what is missing.
2140 I have fixed BackSpace (and thus Delete) to not delete
2141 double-spaces automagically. I have also changed Cut,
2142 Copy and Paste to hopefully do some sensible things.
2143 There are still some small problems that can lead to
2144 double spaces stored in the document file or space at
2145 the beginning of paragraphs. This happens if you have
2146 the cursor betwenn to spaces and then save. Or if you
2147 cut and paste and the selection have a space at the
2148 beginning and then save right after the paste. I am
2149 sure none of these are very hard to fix, but I will
2150 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2151 that I can get some feedback. (Lgb)
2154 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2155 // delete the LineSeparator.
2158 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2159 // delete the LineSeparator.
2162 // If the pos around the old_cursor were spaces, delete one of them.
2163 if (old_cursor.par() != cursor.par()
2164 || old_cursor.pos() != cursor.pos()) {
2165 // Only if the cursor has really moved
2167 if (old_cursor.pos() > 0
2168 && old_cursor.pos() < old_cursor.par()->size()
2169 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2170 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2171 old_cursor.par()->erase(old_cursor.pos() - 1);
2172 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2174 #ifdef WITH_WARNINGS
2175 #warning This will not work anymore when we have multiple views of the same buffer
2176 // In this case, we will have to correct also the cursors held by
2177 // other bufferviews. It will probably be easier to do that in a more
2178 // automated way in LyXCursor code. (JMarc 26/09/2001)
2180 // correct all cursors held by the LyXText
2181 fixCursorAfterDelete(bview, cursor, old_cursor);
2182 fixCursorAfterDelete(bview, selection.cursor,
2184 fixCursorAfterDelete(bview, selection.start,
2186 fixCursorAfterDelete(bview, selection.end, old_cursor);
2187 fixCursorAfterDelete(bview, last_sel_cursor,
2189 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2190 fixCursorAfterDelete(bview, toggle_end_cursor,
2196 // don't delete anything if this is the ONLY paragraph!
2197 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2200 // Do not delete empty paragraphs with keepempty set.
2201 if (old_cursor.par()->layout()->keepempty)
2204 // only do our magic if we changed paragraph
2205 if (old_cursor.par() == cursor.par())
2208 // record if we have deleted a paragraph
2209 // we can't possibly have deleted a paragraph before this point
2210 bool deleted = false;
2212 if ((old_cursor.par()->empty()
2213 || (old_cursor.par()->size() == 1
2214 && old_cursor.par()->isLineSeparator(0)))) {
2215 // ok, we will delete anything
2216 LyXCursor tmpcursor;
2218 // make sure that you do not delete any environments
2219 status(bview, LyXText::NEED_MORE_REFRESH);
2222 if (old_cursor.row()->previous()) {
2223 refresh_row = old_cursor.row()->previous();
2224 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2226 cursor = old_cursor; // that undo can restore the right cursor position
2227 Paragraph * endpar = old_cursor.par()->next();
2228 if (endpar && endpar->getDepth()) {
2229 while (endpar && endpar->getDepth()) {
2230 endpar = endpar->next();
2233 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2237 removeRow(old_cursor.row());
2238 if (ownerParagraph() == old_cursor.par()) {
2239 ownerParagraph(ownerParagraph()->next());
2242 delete old_cursor.par();
2244 /* Breakagain the next par. Needed because of
2245 * the parindent that can occur or dissappear.
2246 * The next row can change its height, if
2247 * there is another layout before */
2248 if (refresh_row->next()) {
2249 breakAgain(bview, refresh_row->next());
2250 updateCounters(bview);
2252 setHeightOfRow(bview, refresh_row);
2254 refresh_row = old_cursor.row()->next();
2255 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2258 cursor = old_cursor; // that undo can restore the right cursor position
2259 Paragraph * endpar = old_cursor.par()->next();
2260 if (endpar && endpar->getDepth()) {
2261 while (endpar && endpar->getDepth()) {
2262 endpar = endpar->next();
2265 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2269 removeRow(old_cursor.row());
2271 if (ownerParagraph() == old_cursor.par()) {
2272 ownerParagraph(ownerParagraph()->next());
2275 delete old_cursor.par();
2277 /* Breakagain the next par. Needed because of
2278 the parindent that can occur or dissappear.
2279 The next row can change its height, if
2280 there is another layout before */
2282 breakAgain(bview, refresh_row);
2283 updateCounters(bview);
2288 setCursorIntern(bview, cursor.par(), cursor.pos());
2290 if (selection.cursor.par() == old_cursor.par()
2291 && selection.cursor.pos() == old_cursor.pos()) {
2292 // correct selection
2293 selection.cursor = cursor;
2297 if (old_cursor.par()->stripLeadingSpaces()) {
2298 redoParagraphs(bview, old_cursor,
2299 old_cursor.par()->next());
2301 setCursorIntern(bview, cursor.par(), cursor.pos());
2302 selection.cursor = cursor;
2309 Paragraph * LyXText::ownerParagraph() const
2312 return inset_owner->paragraph();
2314 return &*(bv_owner->buffer()->paragraphs.begin());
2318 void LyXText::ownerParagraph(Paragraph * p) const
2321 inset_owner->paragraph(p);
2323 bv_owner->buffer()->paragraphs.set(p);
2328 void LyXText::ownerParagraph(int id, Paragraph * p) const
2330 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2331 if (op && op->inInset()) {
2332 static_cast<InsetText *>(op->inInset())->paragraph(p);
2339 LyXText::text_status LyXText::status() const
2345 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2347 LyXText * t = bview->text;
2349 // We should only go up with refreshing code so this means that if
2350 // we have a MORE refresh we should never set it to LITTLE if we still
2351 // didn't handle it (and then it will be UNCHANGED. Now as long as
2352 // we stay inside one LyXText this may work but we need to tell the
2353 // outermost LyXText that it should REALLY draw us if there is some
2354 // change in a Inset::LyXText. So you see that when we are inside a
2355 // inset's LyXText we give the LITTLE to the outermost LyXText to
2356 // tell'em that it should redraw the actual row (where the inset
2357 // resides! Capito?!
2359 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2361 if (inset_owner && st != UNCHANGED) {
2362 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2363 if (!t->refresh_row) {
2364 t->refresh_row = t->cursor.row();
2365 t->refresh_y = t->cursor.y() -
2366 t->cursor.row()->baseline();
2373 bool LyXText::isTopLevel() const
2375 /// only the top-level lyxtext has a non-null bv owner
2380 int defaultRowHeight()
2382 LyXFont const font(LyXFont::ALL_SANE);
2383 return int(font_metrics::maxAscent(font)
2384 + font_metrics::maxDescent(font) * 1.5);