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/insetbibitem.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), top_row_(0), top_row_offset_(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), top_row_(0), top_row_offset_(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();
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->beginningOfBody()) {
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->beginningOfBody()) {
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->beginningOfBody())
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 before 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
329 if (top_row_ == row) {
331 top_row_ = row->next();
332 top_row_offset_ -= row->height();
339 height -= row->height(); // the text becomes smaller
345 // remove all following rows of the paragraph of the specified row.
346 void LyXText::removeParagraph(Row * row) const
348 Paragraph * tmppar = row->par();
352 while (row && row->par() == tmppar) {
353 tmprow = row->next();
360 // insert the specified paragraph behind the specified row
361 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
364 // insert a new row, starting at position 0
365 insertRow(row, par, 0);
367 // and now append the whole paragraph before the new row
370 appendParagraph(bview, firstrow);
372 row->next()->height(0);
373 appendParagraph(bview, row->next());
378 Inset * LyXText::getInset() const
380 if (cursor.pos() < cursor.par()->size()
381 && cursor.par()->isInset(cursor.pos())) {
382 return cursor.par()->getInset(cursor.pos());
388 void LyXText::toggleInset(BufferView * bview)
390 Inset * inset = getInset();
391 // is there an editable inset at cursor position?
392 if (!isEditableInset(inset)) {
393 // No, try to see if we are inside a collapsable inset
394 if (inset_owner && inset_owner->owner()
395 && inset_owner->owner()->isOpen()) {
396 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
397 inset_owner->owner()->close(bview);
398 bview->getLyXText()->cursorRight(bview);
402 //bview->owner()->message(inset->editMessage());
404 // do we want to keep this?? (JMarc)
405 if (!isHighlyEditableInset(inset))
406 setCursorParUndo(bview);
408 if (inset->isOpen()) {
414 inset->open(bview, !inset->isOpen());
419 /* used in setlayout */
420 // Asger is not sure we want to do this...
421 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
424 LyXLayout_ptr const & layout = par.layout();
427 for (pos_type pos = 0; pos < par.size(); ++pos) {
428 if (pos < par.beginningOfBody())
429 layoutfont = layout->labelfont;
431 layoutfont = layout->font;
433 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
434 tmpfont.reduce(layoutfont);
435 par.setFont(pos, tmpfont);
440 Paragraph * LyXText::setLayout(BufferView * bview,
441 LyXCursor & cur, LyXCursor & sstart_cur,
442 LyXCursor & send_cur,
443 string const & layout)
445 Paragraph * endpar = send_cur.par()->next();
446 Paragraph * undoendpar = endpar;
448 if (endpar && endpar->getDepth()) {
449 while (endpar && endpar->getDepth()) {
450 endpar = endpar->next();
454 endpar = endpar->next(); // because of parindents etc.
457 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
459 // ok we have a selection. This is always between sstart_cur
460 // and sel_end cursor
462 Paragraph * par = sstart_cur.par();
463 Paragraph * epar = send_cur.par()->next();
465 LyXLayout_ptr const & lyxlayout =
466 bview->buffer()->params.getLyXTextClass()[layout];
469 par->applyLayout(lyxlayout);
470 makeFontEntriesLayoutSpecific(*bview->buffer(), *par);
471 Paragraph * fppar = par;
472 fppar->params().spaceTop(lyxlayout->fill_top ?
473 VSpace(VSpace::VFILL)
474 : VSpace(VSpace::NONE));
475 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
476 VSpace(VSpace::VFILL)
477 : VSpace(VSpace::NONE));
478 if (lyxlayout->margintype == MARGIN_MANUAL)
479 par->setLabelWidthString(lyxlayout->labelstring());
482 } while (par != epar);
488 // set layout over selection and make a total rebreak of those paragraphs
489 void LyXText::setLayout(BufferView * bview, string const & layout)
491 LyXCursor tmpcursor = cursor; /* store the current cursor */
493 // if there is no selection just set the layout
494 // of the current paragraph */
495 if (!selection.set()) {
496 selection.start = cursor; // dummy selection
497 selection.end = cursor;
499 Paragraph * endpar = setLayout(bview, cursor, selection.start,
500 selection.end, layout);
501 redoParagraphs(bview, selection.start, endpar);
503 // we have to reset the selection, because the
504 // geometry could have changed
505 setCursor(bview, selection.start.par(),
506 selection.start.pos(), false);
507 selection.cursor = cursor;
508 setCursor(bview, selection.end.par(), selection.end.pos(), false);
509 updateCounters(bview);
512 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
516 // increment depth over selection and
517 // make a total rebreak of those paragraphs
518 void LyXText::incDepth(BufferView * bview)
520 // If there is no selection, just use the current paragraph
521 if (!selection.set()) {
522 selection.start = cursor; // dummy selection
523 selection.end = cursor;
526 // We end at the next paragraph with depth 0
527 Paragraph * endpar = selection.end.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 selection.start.par(), undoendpar);
543 LyXCursor tmpcursor = cursor; // store the current cursor
545 // ok we have a selection. This is always between sel_start_cursor
546 // and sel_end cursor
547 cursor = selection.start;
550 // NOTE: you can't change the depth of a bibliography entry
551 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
552 Paragraph * prev = cursor.par()->previous();
555 if (cursor.par()->getDepth()
556 < prev->getMaxDepthAfter()) {
557 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
561 if (cursor.par() == selection.end.par())
563 cursor.par(cursor.par()->next());
566 redoParagraphs(bview, selection.start, endpar);
568 // we have to reset the selection, because the
569 // geometry could have changed
570 setCursor(bview, selection.start.par(), selection.start.pos());
571 selection.cursor = cursor;
572 setCursor(bview, selection.end.par(), selection.end.pos());
573 updateCounters(bview);
576 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
580 // decrement depth over selection and
581 // make a total rebreak of those paragraphs
582 void LyXText::decDepth(BufferView * bview)
584 // if there is no selection just set the layout
585 // of the current paragraph
586 if (!selection.set()) {
587 selection.start = cursor; // dummy selection
588 selection.end = cursor;
590 Paragraph * endpar = selection.end.par()->next();
591 Paragraph * undoendpar = endpar;
593 if (endpar && endpar->getDepth()) {
594 while (endpar && endpar->getDepth()) {
595 endpar = endpar->next();
599 endpar = endpar->next(); // because of parindents etc.
602 setUndo(bview, Undo::EDIT,
603 selection.start.par(), undoendpar);
605 LyXCursor tmpcursor = cursor; // store the current cursor
607 // ok we have a selection. This is always between sel_start_cursor
608 // and sel_end cursor
609 cursor = selection.start;
612 if (cursor.par()->params().depth()) {
613 cursor.par()->params()
614 .depth(cursor.par()->params().depth() - 1);
616 if (cursor.par() == selection.end.par()) {
619 cursor.par(cursor.par()->next());
622 redoParagraphs(bview, selection.start, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 setCursor(bview, selection.start.par(),
627 selection.start.pos());
628 selection.cursor = cursor;
629 setCursor(bview, selection.end.par(), selection.end.pos());
630 updateCounters(bview);
633 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
637 // set font over selection and make a total rebreak of those paragraphs
638 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
640 // if there is no selection just set the current_font
641 if (!selection.set()) {
642 // Determine basis font
644 if (cursor.pos() < cursor.par()->beginningOfBody()) {
645 layoutfont = getLabelFont(bview->buffer(),
648 layoutfont = getLayoutFont(bview->buffer(),
651 // Update current font
652 real_current_font.update(font,
653 bview->buffer()->params.language,
656 // Reduce to implicit settings
657 current_font = real_current_font;
658 current_font.reduce(layoutfont);
659 // And resolve it completely
660 real_current_font.realize(layoutfont);
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
670 setUndo(bview, Undo::EDIT,
671 selection.start.par(), selection.end.par()->next());
673 cursor = selection.start;
674 while (cursor.par() != selection.end.par() ||
675 cursor.pos() < selection.end.pos())
677 if (cursor.pos() < cursor.par()->size()) {
678 // an open footnote should behave like a closed one
679 setCharFont(bview, cursor.par(), cursor.pos(),
681 cursor.pos(cursor.pos() + 1);
684 cursor.par(cursor.par()->next());
689 redoParagraphs(bview, selection.start, selection.end.par()->next());
691 // we have to reset the selection, because the
692 // geometry could have changed, but we keep
693 // it for user convenience
694 setCursor(bview, selection.start.par(), selection.start.pos());
695 selection.cursor = cursor;
696 setCursor(bview, selection.end.par(), selection.end.pos());
698 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
699 tmpcursor.boundary());
703 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
705 Row * tmprow = cur.row();
706 int y = cur.y() - tmprow->baseline();
708 setHeightOfRow(bview, tmprow);
710 while (tmprow->previous()
711 && tmprow->previous()->par() == tmprow->par()) {
712 tmprow = tmprow->previous();
713 y -= tmprow->height();
714 setHeightOfRow(bview, tmprow);
717 // we can set the refreshing parameters now
718 status(bview, LyXText::NEED_MORE_REFRESH);
720 refresh_row = tmprow;
721 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
725 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
727 Row * tmprow = cur.row();
729 int y = cur.y() - tmprow->baseline();
730 setHeightOfRow(bview, tmprow);
732 while (tmprow->previous()
733 && tmprow->previous()->par() == tmprow->par()) {
734 tmprow = tmprow->previous();
735 y -= tmprow->height();
738 // we can set the refreshing parameters now
739 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
741 refresh_row = tmprow;
743 status(bview, LyXText::NEED_MORE_REFRESH);
744 setCursor(bview, cur.par(), cur.pos());
748 // deletes and inserts again all paragaphs between the cursor
749 // and the specified par
750 // This function is needed after SetLayout and SetFont etc.
751 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
752 Paragraph const * endpar) const
755 Paragraph * tmppar = 0;
756 Paragraph * first_phys_par = 0;
758 Row * tmprow = cur.row();
760 int y = cur.y() - tmprow->baseline();
762 if (!tmprow->previous()) {
763 // a trick/hack for UNDO
764 // This is needed because in an UNDO/REDO we could have changed
765 // the ownerParagrah() so the paragraph inside the row is NOT
766 // my really first par anymore. Got it Lars ;) (Jug 20011206)
767 first_phys_par = ownerParagraph();
769 first_phys_par = tmprow->par();
770 while (tmprow->previous()
771 && tmprow->previous()->par() == first_phys_par)
773 tmprow = tmprow->previous();
774 y -= tmprow->height();
778 // we can set the refreshing parameters now
779 status(bview, LyXText::NEED_MORE_REFRESH);
781 refresh_row = tmprow->previous(); /* the real refresh row will
782 be deleted, so I store
786 tmppar = tmprow->next()->par();
789 while (tmprow->next() && tmppar != endpar) {
790 removeRow(tmprow->next());
791 if (tmprow->next()) {
792 tmppar = tmprow->next()->par();
798 // remove the first one
799 tmprow2 = tmprow; /* this is because tmprow->previous()
801 tmprow = tmprow->previous();
804 tmppar = first_phys_par;
808 insertParagraph(bview, tmppar, tmprow);
812 while (tmprow->next()
813 && tmprow->next()->par() == tmppar) {
814 tmprow = tmprow->next();
816 tmppar = tmppar->next();
818 } while (tmppar && tmppar != endpar);
820 // this is because of layout changes
822 refresh_y -= refresh_row->height();
823 setHeightOfRow(bview, refresh_row);
825 refresh_row = firstrow;
827 setHeightOfRow(bview, refresh_row);
830 if (tmprow && tmprow->next())
831 setHeightOfRow(bview, tmprow->next());
832 updateCounters(bview);
836 void LyXText::fullRebreak(BufferView * bview)
842 if (need_break_row) {
843 breakAgain(bview, need_break_row);
850 // important for the screen
853 // the cursor set functions have a special mechanism. When they
854 // realize, that you left an empty paragraph, they will delete it.
855 // They also delete the corresponding row
857 // need the selection cursor:
858 void LyXText::setSelection(BufferView * bview)
860 bool const lsel = selection.set();
862 if (!selection.set()) {
863 last_sel_cursor = selection.cursor;
864 selection.start = selection.cursor;
865 selection.end = selection.cursor;
870 // first the toggling area
871 if (cursor.y() < last_sel_cursor.y()
872 || (cursor.y() == last_sel_cursor.y()
873 && cursor.x() < last_sel_cursor.x())) {
874 toggle_end_cursor = last_sel_cursor;
875 toggle_cursor = cursor;
877 toggle_end_cursor = cursor;
878 toggle_cursor = last_sel_cursor;
881 last_sel_cursor = cursor;
883 // and now the whole selection
885 if (selection.cursor.par() == cursor.par())
886 if (selection.cursor.pos() < cursor.pos()) {
887 selection.end = cursor;
888 selection.start = selection.cursor;
890 selection.end = selection.cursor;
891 selection.start = cursor;
893 else if (selection.cursor.y() < cursor.y() ||
894 (selection.cursor.y() == cursor.y()
895 && selection.cursor.x() < cursor.x())) {
896 selection.end = cursor;
897 selection.start = selection.cursor;
900 selection.end = selection.cursor;
901 selection.start = cursor;
904 // a selection with no contents is not a selection
905 if (selection.start.par() == selection.end.par() &&
906 selection.start.pos() == selection.end.pos())
907 selection.set(false);
909 if (inset_owner && (selection.set() || lsel))
910 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
914 string const LyXText::selectionAsString(Buffer const * buffer,
917 if (!selection.set()) return string();
919 // should be const ...
920 Paragraph * startpar(selection.start.par());
921 Paragraph * endpar(selection.end.par());
922 pos_type const startpos(selection.start.pos());
923 pos_type const endpos(selection.end.pos());
925 if (startpar == endpar) {
926 return startpar->asString(buffer, startpos, endpos, label);
931 // First paragraph in selection
932 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
934 // The paragraphs in between (if any)
935 LyXCursor tmpcur(selection.start);
936 tmpcur.par(tmpcur.par()->next());
937 while (tmpcur.par() != endpar) {
938 result += tmpcur.par()->asString(buffer, 0,
939 tmpcur.par()->size(),
941 tmpcur.par(tmpcur.par()->next());
944 // Last paragraph in selection
945 result += endpar->asString(buffer, 0, endpos, label);
951 void LyXText::clearSelection() const
953 selection.set(false);
954 selection.mark(false);
955 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
956 // reset this in the bv_owner!
957 if (bv_owner && bv_owner->text)
958 bv_owner->text->xsel_cache.set(false);
962 void LyXText::cursorHome(BufferView * bview) const
964 setCursor(bview, cursor.par(), cursor.row()->pos());
968 void LyXText::cursorEnd(BufferView * bview) const
970 if (!cursor.row()->next()
971 || cursor.row()->next()->par() != cursor.row()->par()) {
972 setCursor(bview, cursor.par(), cursor.row()->lastPos() + 1);
974 if (!cursor.par()->empty() &&
975 (cursor.par()->getChar(cursor.row()->lastPos()) == ' '
976 || cursor.par()->isNewline(cursor.row()->lastPos()))) {
977 setCursor(bview, cursor.par(), cursor.row()->lastPos());
979 setCursor(bview,cursor.par(),
980 cursor.row()->lastPos() + 1);
986 void LyXText::cursorTop(BufferView * bview) const
988 while (cursor.par()->previous())
989 cursor.par(cursor.par()->previous());
990 setCursor(bview, cursor.par(), 0);
994 void LyXText::cursorBottom(BufferView * bview) const
996 while (cursor.par()->next())
997 cursor.par(cursor.par()->next());
998 setCursor(bview, cursor.par(), cursor.par()->size());
1002 void LyXText::toggleFree(BufferView * bview,
1003 LyXFont const & font, bool toggleall)
1005 // If the mask is completely neutral, tell user
1006 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1007 // Could only happen with user style
1008 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1012 // Try implicit word selection
1013 // If there is a change in the language the implicit word selection
1015 LyXCursor resetCursor = cursor;
1016 bool implicitSelection = (font.language() == ignore_language
1017 && font.number() == LyXFont::IGNORE)
1018 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1021 setFont(bview, font, toggleall);
1023 // Implicit selections are cleared afterwards
1024 //and cursor is set to the original position.
1025 if (implicitSelection) {
1027 cursor = resetCursor;
1028 setCursor(bview, cursor.par(), cursor.pos());
1029 selection.cursor = cursor;
1032 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1036 string LyXText::getStringToIndex(BufferView * bview)
1038 // Try implicit word selection
1039 // If there is a change in the language the implicit word selection
1041 LyXCursor const reset_cursor = cursor;
1042 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1045 if (!selection.set())
1046 bview->owner()->message(_("Nothing to index!"));
1047 else if (selection.start.par() != selection.end.par())
1048 bview->owner()->message(_("Cannot index more than one paragraph!"));
1050 idxstring = selectionAsString(bview->buffer(), false);
1052 // Reset cursors to their original position.
1053 cursor = reset_cursor;
1054 setCursor(bview, cursor.par(), cursor.pos());
1055 selection.cursor = cursor;
1057 // Clear the implicit selection.
1058 if (implicitSelection)
1065 // the DTP switches for paragraphs. LyX will store them in the first
1066 // physicla paragraph. When a paragraph is broken, the top settings rest,
1067 // the bottom settings are given to the new one. So I can make shure,
1068 // they do not duplicate themself and you cannnot make dirty things with
1071 void LyXText::setParagraph(BufferView * bview,
1072 bool line_top, bool line_bottom,
1073 bool pagebreak_top, bool pagebreak_bottom,
1074 VSpace const & space_top,
1075 VSpace const & space_bottom,
1076 Spacing const & spacing,
1078 string labelwidthstring,
1081 LyXCursor tmpcursor = cursor;
1082 if (!selection.set()) {
1083 selection.start = cursor;
1084 selection.end = cursor;
1087 // make sure that the depth behind the selection are restored, too
1088 Paragraph * endpar = selection.end.par()->next();
1089 Paragraph * undoendpar = endpar;
1091 if (endpar && endpar->getDepth()) {
1092 while (endpar && endpar->getDepth()) {
1093 endpar = endpar->next();
1094 undoendpar = endpar;
1098 // because of parindents etc.
1099 endpar = endpar->next();
1102 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1105 Paragraph * tmppar = selection.end.par();
1107 while (tmppar != selection.start.par()->previous()) {
1108 setCursor(bview, tmppar, 0);
1109 status(bview, LyXText::NEED_MORE_REFRESH);
1110 refresh_row = cursor.row();
1111 refresh_y = cursor.y() - cursor.row()->baseline();
1112 cursor.par()->params().lineTop(line_top);
1113 cursor.par()->params().lineBottom(line_bottom);
1114 cursor.par()->params().pagebreakTop(pagebreak_top);
1115 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1116 cursor.par()->params().spaceTop(space_top);
1117 cursor.par()->params().spaceBottom(space_bottom);
1118 cursor.par()->params().spacing(spacing);
1119 // does the layout allow the new alignment?
1120 LyXLayout_ptr const & layout = cursor.par()->layout();
1122 if (align == LYX_ALIGN_LAYOUT)
1123 align = layout->align;
1124 if (align & layout->alignpossible) {
1125 if (align == layout->align)
1126 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1128 cursor.par()->params().align(align);
1130 cursor.par()->setLabelWidthString(labelwidthstring);
1131 cursor.par()->params().noindent(noindent);
1132 tmppar = cursor.par()->previous();
1135 redoParagraphs(bview, selection.start, endpar);
1138 setCursor(bview, selection.start.par(), selection.start.pos());
1139 selection.cursor = cursor;
1140 setCursor(bview, selection.end.par(), selection.end.pos());
1141 setSelection(bview);
1142 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1144 bview->updateInset(inset_owner, true);
1148 // set the counter of a paragraph. This includes the labels
1149 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1151 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1152 LyXLayout_ptr const & layout = par->layout();
1154 if (par->previous()) {
1156 par->params().appendix(par->previous()->params().appendix());
1157 if (!par->params().appendix() && par->params().startOfAppendix()) {
1158 par->params().appendix(true);
1159 textclass.counters().reset();
1161 par->enumdepth = par->previous()->enumdepth;
1162 par->itemdepth = par->previous()->itemdepth;
1164 par->params().appendix(par->params().startOfAppendix());
1169 /* Maybe we have to increment the enumeration depth.
1170 * BUT, enumeration in a footnote is considered in isolation from its
1171 * surrounding paragraph so don't increment if this is the
1172 * first line of the footnote
1173 * AND, bibliographies can't have their depth changed ie. they
1174 * are always of depth 0
1177 && par->previous()->getDepth() < par->getDepth()
1178 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1179 && par->enumdepth < 3
1180 && layout->labeltype != LABEL_BIBLIO) {
1184 // Maybe we have to decrement the enumeration depth, see note above
1186 && par->previous()->getDepth() > par->getDepth()
1187 && layout->labeltype != LABEL_BIBLIO) {
1188 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1191 if (!par->params().labelString().empty()) {
1192 par->params().labelString(string());
1195 if (layout->margintype == MARGIN_MANUAL) {
1196 if (par->params().labelWidthString().empty()) {
1197 par->setLabelWidthString(layout->labelstring());
1200 par->setLabelWidthString(string());
1203 // is it a layout that has an automatic label?
1204 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1205 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1209 if (i >= 0 && i <= buf->params.secnumdepth) {
1213 textclass.counters().step(layout->latexname());
1215 // Is there a label? Useful for Chapter layout
1216 if (!par->params().appendix()) {
1217 s << layout->labelstring();
1219 s << layout->labelstring_appendix();
1222 // Use of an integer is here less than elegant. For now.
1223 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1224 if (!par->params().appendix()) {
1225 numbertype = "sectioning";
1227 numbertype = "appendix";
1228 if (par->isRightToLeftPar(buf->params))
1229 langtype = "hebrew";
1234 s << textclass.counters()
1235 .numberLabel(layout->latexname(),
1236 numbertype, langtype, head);
1238 par->params().labelString(STRCONV(s.str()));
1240 // reset enum counters
1241 textclass.counters().reset("enum");
1242 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1243 textclass.counters().reset("enum");
1244 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1246 // Yes I know this is a really, really! bad solution
1248 string enumcounter("enum");
1250 switch (par->enumdepth) {
1259 enumcounter += "iv";
1262 // not a valid enumdepth...
1266 textclass.counters().step(enumcounter);
1268 s << textclass.counters()
1269 .numberLabel(enumcounter, "enumeration");
1270 par->params().labelString(STRCONV(s.str()));
1272 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1273 textclass.counters().step("bibitem");
1274 int number = textclass.counters().value("bibitem");
1275 if (par->bibitem()) {
1276 par->bibitem()->setCounter(number);
1277 par->params().labelString(layout->labelstring());
1279 // In biblio should't be following counters but...
1281 string s = layout->labelstring();
1283 // the caption hack:
1284 if (layout->labeltype == LABEL_SENSITIVE) {
1285 Paragraph * tmppar = par;
1288 while (tmppar && tmppar->inInset()
1289 // the single '=' is intended below
1290 && (in = tmppar->inInset()->owner())) {
1291 if (in->lyxCode() == Inset::FLOAT_CODE ||
1292 in->lyxCode() == Inset::WRAP_CODE) {
1296 tmppar = in->parOwner();
1302 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1304 textclass.counters().step(fl.type());
1306 // Doesn't work... yet.
1307 #warning use boost.format
1308 #if USE_BOOST_FORMAT
1309 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1310 // s << boost::format(_("%1$s %1$d:")
1312 // % buf->counters().value(fl.name());
1315 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1316 o << fl.name() << " #:";
1317 s = STRCONV(o.str());
1320 // par->SetLayout(0);
1321 // s = layout->labelstring;
1322 s = _("Senseless: ");
1325 par->params().labelString(s);
1327 // reset the enumeration counter. They are always reset
1328 // when there is any other layout between
1329 // Just fall-through between the cases so that all
1330 // enum counters deeper than enumdepth is also reset.
1331 switch (par->enumdepth) {
1333 textclass.counters().reset("enumi");
1335 textclass.counters().reset("enumii");
1337 textclass.counters().reset("enumiii");
1339 textclass.counters().reset("enumiv");
1345 // Updates all counters. Paragraphs with changed label string will be rebroken
1346 void LyXText::updateCounters(BufferView * bview) const
1348 Row * row = firstrow;
1349 Paragraph * par = row->par();
1351 // CHECK if this is really needed. (Lgb)
1352 bview->buffer()->params.getLyXTextClass().counters().reset();
1355 while (row->par() != par)
1358 string const oldLabel = par->params().labelString();
1360 // setCounter can potentially change the labelString.
1361 setCounter(bview->buffer(), par);
1363 string const & newLabel = par->params().labelString();
1365 if (oldLabel.empty() && !newLabel.empty()) {
1366 removeParagraph(row);
1367 appendParagraph(bview, row);
1375 void LyXText::insertInset(BufferView * bview, Inset * inset)
1377 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1379 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1381 cursor.par()->insertInset(cursor.pos(), inset);
1382 // Just to rebreak and refresh correctly.
1383 // The character will not be inserted a second time
1384 insertChar(bview, Paragraph::META_INSET);
1385 // If we enter a highly editable inset the cursor should be to before
1386 // the inset. This couldn't happen before as Undo was not handled inside
1387 // inset now after the Undo LyX tries to call inset->Edit(...) again
1388 // and cannot do this as the cursor is behind the inset and GetInset
1389 // does not return the inset!
1390 if (isHighlyEditableInset(inset)) {
1391 cursorLeft(bview, true);
1397 void LyXText::copyEnvironmentType()
1399 copylayouttype = cursor.par()->layout()->name();
1403 void LyXText::pasteEnvironmentType(BufferView * bview)
1405 // do nothing if there has been no previous copyEnvironmentType()
1406 if (!copylayouttype.empty())
1407 setLayout(bview, copylayouttype);
1411 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1413 // Stuff what we got on the clipboard. Even if there is no selection.
1415 // There is a problem with having the stuffing here in that the
1416 // larger the selection the slower LyX will get. This can be
1417 // solved by running the line below only when the selection has
1418 // finished. The solution used currently just works, to make it
1419 // faster we need to be more clever and probably also have more
1420 // calls to stuffClipboard. (Lgb)
1421 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1423 // This doesn't make sense, if there is no selection
1424 if (!selection.set())
1427 // OK, we have a selection. This is always between selection.start
1428 // and selection.end
1430 // make sure that the depth behind the selection are restored, too
1431 Paragraph * endpar = selection.end.par()->next();
1432 Paragraph * undoendpar = endpar;
1434 if (endpar && endpar->getDepth()) {
1435 while (endpar && endpar->getDepth()) {
1436 endpar = endpar->next();
1437 undoendpar = endpar;
1439 } else if (endpar) {
1440 endpar = endpar->next(); // because of parindents etc.
1443 setUndo(bview, Undo::DELETE,
1444 selection.start.par(), undoendpar);
1446 // there are two cases: cut only within one paragraph or
1447 // more than one paragraph
1448 if (selection.start.par() == selection.end.par()) {
1449 // only within one paragraph
1450 endpar = selection.end.par();
1451 int pos = selection.end.pos();
1452 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1453 selection.start.pos(), pos,
1454 bview->buffer()->params.textclass,
1456 selection.end.pos(pos);
1458 endpar = selection.end.par();
1459 int pos = selection.end.pos();
1460 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1461 selection.start.pos(), pos,
1462 bview->buffer()->params.textclass,
1465 selection.end.par(endpar);
1466 selection.end.pos(pos);
1467 cursor.pos(selection.end.pos());
1469 endpar = endpar->next();
1471 // sometimes necessary
1473 selection.start.par()->stripLeadingSpaces();
1475 redoParagraphs(bview, selection.start, endpar);
1477 // cutSelection can invalidate the cursor so we need to set
1479 // we prefer the end for when tracking changes
1480 cursor = selection.end;
1482 // need a valid cursor. (Lgb)
1485 setCursor(bview, cursor.par(), cursor.pos());
1486 selection.cursor = cursor;
1487 updateCounters(bview);
1491 void LyXText::copySelection(BufferView * bview)
1493 // stuff the selection onto the X clipboard, from an explicit copy request
1494 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1496 // this doesnt make sense, if there is no selection
1497 if (!selection.set())
1500 // ok we have a selection. This is always between selection.start
1501 // and sel_end cursor
1503 // copy behind a space if there is one
1504 while (selection.start.par()->size() > selection.start.pos()
1505 && selection.start.par()->isLineSeparator(selection.start.pos())
1506 && (selection.start.par() != selection.end.par()
1507 || selection.start.pos() < selection.end.pos()))
1508 selection.start.pos(selection.start.pos() + 1);
1510 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1511 selection.start.pos(), selection.end.pos(),
1512 bview->buffer()->params.textclass);
1516 void LyXText::pasteSelection(BufferView * bview)
1518 // this does not make sense, if there is nothing to paste
1519 if (!CutAndPaste::checkPastePossible())
1522 setUndo(bview, Undo::INSERT,
1523 cursor.par(), cursor.par()->next());
1526 Paragraph * actpar = cursor.par();
1527 int pos = cursor.pos();
1529 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1530 bview->buffer()->params.textclass);
1532 redoParagraphs(bview, cursor, endpar);
1534 setCursor(bview, cursor.par(), cursor.pos());
1537 selection.cursor = cursor;
1538 setCursor(bview, actpar, pos);
1539 setSelection(bview);
1540 updateCounters(bview);
1544 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1549 selection.cursor = cursor;
1552 setSelection(bview);
1556 // simple replacing. The font of the first selected character is used
1557 void LyXText::replaceSelectionWithString(BufferView * bview,
1560 setCursorParUndo(bview);
1563 if (!selection.set()) { // create a dummy selection
1564 selection.end = cursor;
1565 selection.start = cursor;
1568 // Get font setting before we cut
1569 pos_type pos = selection.end.pos();
1570 LyXFont const font = selection.start.par()
1571 ->getFontSettings(bview->buffer()->params,
1572 selection.start.pos());
1574 // Insert the new string
1575 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1576 selection.end.par()->insertChar(pos, (*cit), font);
1580 // Cut the selection
1581 cutSelection(bview, true, false);
1587 // needed to insert the selection
1588 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1590 Paragraph * par = cursor.par();
1591 pos_type pos = cursor.pos();
1592 Paragraph * endpar = cursor.par()->next();
1594 setCursorParUndo(bview);
1596 // only to be sure, should not be neccessary
1599 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1601 redoParagraphs(bview, cursor, endpar);
1602 setCursor(bview, cursor.par(), cursor.pos());
1603 selection.cursor = cursor;
1604 setCursor(bview, par, pos);
1605 setSelection(bview);
1609 // turns double-CR to single CR, others where converted into one
1610 // blank. Then InsertStringAsLines is called
1611 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1613 string linestr(str);
1614 bool newline_inserted = false;
1615 for (string::size_type i = 0; i < linestr.length(); ++i) {
1616 if (linestr[i] == '\n') {
1617 if (newline_inserted) {
1618 // we know that \r will be ignored by
1619 // InsertStringA. Of course, it is a dirty
1620 // trick, but it works...
1621 linestr[i - 1] = '\r';
1625 newline_inserted = true;
1627 } else if (IsPrintable(linestr[i])) {
1628 newline_inserted = false;
1631 insertStringAsLines(bview, linestr);
1635 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1638 LyXCursor tmpcursor;
1642 Row * row = getRow(par, pos, y);
1644 // is there a break one row above
1645 if (row->previous() && row->previous()->par() == row->par()) {
1646 z = rowBreakPoint(*bview, *row->previous());
1647 if (z >= row->pos()) {
1648 // set the dimensions of the row above
1649 y -= row->previous()->height();
1651 refresh_row = row->previous();
1652 status(bview, LyXText::NEED_MORE_REFRESH);
1654 breakAgain(bview, row->previous());
1656 // set the cursor again. Otherwise
1657 // dangling pointers are possible
1658 setCursor(bview, cursor.par(), cursor.pos(),
1659 false, cursor.boundary());
1660 selection.cursor = cursor;
1665 int const tmpheight = row->height();
1666 pos_type const tmplast = row->lastPos();
1670 breakAgain(bview, row);
1671 if (row->height() == tmpheight && row->lastPos() == tmplast)
1672 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1674 status(bview, LyXText::NEED_MORE_REFRESH);
1676 // check the special right address boxes
1677 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1684 redoDrawingOfParagraph(bview, tmpcursor);
1687 // set the cursor again. Otherwise dangling pointers are possible
1688 // also set the selection
1690 if (selection.set()) {
1692 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1693 false, selection.cursor.boundary());
1694 selection.cursor = cursor;
1695 setCursorIntern(bview, selection.start.par(),
1696 selection.start.pos(),
1697 false, selection.start.boundary());
1698 selection.start = cursor;
1699 setCursorIntern(bview, selection.end.par(),
1700 selection.end.pos(),
1701 false, selection.end.boundary());
1702 selection.end = cursor;
1703 setCursorIntern(bview, last_sel_cursor.par(),
1704 last_sel_cursor.pos(),
1705 false, last_sel_cursor.boundary());
1706 last_sel_cursor = cursor;
1709 setCursorIntern(bview, cursor.par(), cursor.pos(),
1710 false, cursor.boundary());
1714 // returns false if inset wasn't found
1715 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1717 // first check the current paragraph
1718 int pos = cursor.par()->getPositionOfInset(inset);
1720 checkParagraph(bview, cursor.par(), pos);
1724 // check every paragraph
1726 Paragraph * par = ownerParagraph();
1728 pos = par->getPositionOfInset(inset);
1730 checkParagraph(bview, par, pos);
1740 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1742 bool setfont, bool boundary) const
1744 LyXCursor old_cursor = cursor;
1745 setCursorIntern(bview, par, pos, setfont, boundary);
1746 return deleteEmptyParagraphMechanism(bview, old_cursor);
1750 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1751 pos_type pos, bool boundary) const
1758 cur.boundary(boundary);
1760 // get the cursor y position in text
1762 Row * row = getRow(par, pos, y);
1763 Row * old_row = row;
1765 // if we are before the first char of this row and are still in the
1766 // same paragraph and there is a previous row then put the cursor on
1767 // the end of the previous row
1768 cur.iy(y + row->baseline());
1770 if (row->previous() && pos &&
1771 row->previous()->par() == row->par() &&
1772 par->getChar(pos) == Paragraph::META_INSET &&
1773 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1775 row = row->previous();
1780 // y is now the beginning of the cursor row
1781 y += row->baseline();
1782 // y is now the cursor baseline
1785 pos_type last = old_row->lastPrintablePos();
1787 // None of these should happen, but we're scaredy-cats
1788 if (pos > par->size()) {
1791 } else if (pos > last + 1) {
1792 // This shouldn't happen.
1795 } else if (pos < row->pos()) {
1800 // now get the cursors x position
1801 float x = getCursorX(bview, row, pos, last, boundary);
1804 if (old_row != row) {
1805 x = getCursorX(bview, old_row, pos, last, boundary);
1812 float LyXText::getCursorX(BufferView * bview, Row * row,
1813 pos_type pos, pos_type last, bool boundary) const
1815 pos_type cursor_vpos = 0;
1817 float fill_separator;
1819 float fill_label_hfill;
1820 // This call HAS to be here because of the BidiTables!!!
1821 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1824 if (last < row->pos())
1825 cursor_vpos = row->pos();
1826 else if (pos > last && !boundary)
1827 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1828 ? row->pos() : last + 1;
1829 else if (pos > row->pos() &&
1830 (pos > last || boundary))
1831 /// Place cursor after char at (logical) position pos - 1
1832 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1833 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1835 /// Place cursor before char at (logical) position pos
1836 cursor_vpos = (bidi_level(pos) % 2 == 0)
1837 ? log2vis(pos) : log2vis(pos) + 1;
1839 pos_type body_pos = row->par()->beginningOfBody();
1840 if ((body_pos > 0) &&
1841 ((body_pos-1 > last) ||
1842 !row->par()->isLineSeparator(body_pos-1)))
1845 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1846 pos_type pos = vis2log(vpos);
1847 if (body_pos > 0 && pos == body_pos - 1) {
1848 x += fill_label_hfill +
1849 font_metrics::width(
1850 row->par()->layout()->labelsep,
1851 getLabelFont(bview->buffer(),
1853 if (row->par()->isLineSeparator(body_pos - 1))
1854 x -= singleWidth(bview,
1855 row->par(), body_pos - 1);
1857 if (row->hfillExpansion(pos)) {
1858 x += singleWidth(bview, row->par(), pos);
1859 if (pos >= body_pos)
1862 x += fill_label_hfill;
1863 } else if (row->par()->isSeparator(pos)) {
1864 x += singleWidth(bview, row->par(), pos);
1865 if (pos >= body_pos)
1866 x += fill_separator;
1868 x += singleWidth(bview, row->par(), pos);
1874 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1875 pos_type pos, bool setfont, bool boundary) const
1877 InsetText * it = static_cast<InsetText *>(par->inInset());
1879 if (it != inset_owner) {
1880 lyxerr[Debug::INSETS] << "InsetText is " << it
1882 << "inset_owner is "
1883 << inset_owner << endl;
1884 #ifdef WITH_WARNINGS
1885 #warning I believe this code is wrong. (Lgb)
1886 #warning Jürgen, have a look at this. (Lgb)
1887 #warning Hmmm, I guess you are right but we
1888 #warning should verify when this is needed
1890 // Jürgen, would you like to have a look?
1891 // I guess we need to move the outer cursor
1892 // and open and lock the inset (bla bla bla)
1893 // stuff I don't know... so can you have a look?
1895 // I moved the lyxerr stuff in here so we can see if
1896 // this is actually really needed and where!
1898 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1903 setCursor(bview, cursor, par, pos, boundary);
1905 setCurrentFont(bview);
1909 void LyXText::setCurrentFont(BufferView * bview) const
1911 pos_type pos = cursor.pos();
1912 if (cursor.boundary() && pos > 0)
1916 if (pos == cursor.par()->size())
1918 else // potentional bug... BUG (Lgb)
1919 if (cursor.par()->isSeparator(pos)) {
1920 if (pos > cursor.row()->pos() &&
1921 bidi_level(pos) % 2 ==
1922 bidi_level(pos - 1) % 2)
1924 else if (pos + 1 < cursor.par()->size())
1930 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1931 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1933 if (cursor.pos() == cursor.par()->size() &&
1934 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1935 !cursor.boundary()) {
1936 Language const * lang =
1937 cursor.par()->getParLanguage(bview->buffer()->params);
1938 current_font.setLanguage(lang);
1939 current_font.setNumber(LyXFont::OFF);
1940 real_current_font.setLanguage(lang);
1941 real_current_font.setNumber(LyXFont::OFF);
1946 // returns the column near the specified x-coordinate of the row
1947 // x is set to the real beginning of this column
1949 LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
1950 bool & boundary) const
1953 float fill_separator;
1955 float fill_label_hfill;
1957 prepareToPrint(bview, row, tmpx, fill_separator,
1958 fill_hfill, fill_label_hfill);
1960 pos_type vc = row->pos();
1961 pos_type last = row->lastPrintablePos();
1964 LyXLayout_ptr const & layout = row->par()->layout();
1966 bool left_side = false;
1968 pos_type body_pos = row->par()->beginningOfBody();
1969 float last_tmpx = tmpx;
1972 (body_pos - 1 > last ||
1973 !row->par()->isLineSeparator(body_pos - 1)))
1976 // check for empty row
1977 if (!row->par()->size()) {
1982 while (vc <= last && tmpx <= x) {
1985 if (body_pos > 0 && c == body_pos-1) {
1986 tmpx += fill_label_hfill +
1987 font_metrics::width(layout->labelsep,
1988 getLabelFont(bview->buffer(), row->par()));
1989 if (row->par()->isLineSeparator(body_pos - 1))
1990 tmpx -= singleWidth(bview, row->par(), body_pos-1);
1993 if (row->hfillExpansion(c)) {
1994 tmpx += singleWidth(bview, row->par(), c);
1998 tmpx += fill_label_hfill;
1999 } else if (row->par()->isSeparator(c)) {
2000 tmpx += singleWidth(bview, row->par(), c);
2002 tmpx+= fill_separator;
2004 tmpx += singleWidth(bview, row->par(), c);
2009 if ((tmpx + last_tmpx) / 2 > x) {
2014 if (vc > last + 1) // This shouldn't happen.
2018 bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
2019 // some speedup if rtl_support=false
2020 && (!row->next() || row->next()->par() != row->par());
2021 bool const rtl = (lastrow)
2022 ? row->par()->isRightToLeftPar(bview->buffer()->params)
2023 : false; // If lastrow is false, we don't need to compute
2024 // the value of rtl.
2027 ((rtl && left_side && vc == row->pos() && x < tmpx - 5) ||
2028 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
2030 else if (vc == row->pos()) {
2032 if (bidi_level(c) % 2 == 1)
2035 c = vis2log(vc - 1);
2036 bool const rtl = (bidi_level(c) % 2 == 1);
2037 if (left_side == rtl) {
2039 boundary = isBoundary(bview->buffer(), row->par(), c);
2043 if (row->pos() <= last && c > last
2044 && row->par()->isNewline(last)) {
2045 if (bidi_level(last) % 2 == 0)
2046 tmpx -= singleWidth(bview, row->par(), last);
2048 tmpx += singleWidth(bview, row->par(), last);
2058 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2060 LyXCursor old_cursor = cursor;
2062 setCursorFromCoordinates(bview, cursor, x, y);
2063 setCurrentFont(bview);
2064 deleteEmptyParagraphMechanism(bview, old_cursor);
2071 * return true if the cursor given is at the end of a row,
2072 * and the next row is filled by an inset that spans an entire
2075 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2078 Row const & next = *row.next();
2080 if (next.pos() != cur.pos() || next.par() != cur.par())
2082 if (!cur.par()->isInset(cur.pos()))
2084 Inset const * inset = cur.par()->getInset(cur.pos());
2085 if (inset->needFullRow() || inset->display())
2092 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2095 // Get the row first.
2097 Row * row = getRowNearY(y);
2099 pos_type const column = getColumnNearX(bview, row, x, bound);
2100 cur.par(row->par());
2101 cur.pos(row->pos() + column);
2103 cur.y(y + row->baseline());
2106 if (beforeFullRowInset(*row, cur)) {
2107 pos_type last = row->lastPrintablePos();
2108 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2110 cur.iy(y + row->height() + row->next()->baseline());
2111 cur.irow(row->next());
2117 cur.boundary(bound);
2121 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2123 if (cursor.pos() > 0) {
2124 bool boundary = cursor.boundary();
2125 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2126 if (!internal && !boundary &&
2127 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2128 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2129 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2130 Paragraph * par = cursor.par()->previous();
2131 setCursor(bview, par, par->size());
2136 void LyXText::cursorRight(BufferView * bview, bool internal) const
2138 if (!internal && cursor.boundary() &&
2139 !cursor.par()->isNewline(cursor.pos()))
2140 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2141 else if (cursor.pos() < cursor.par()->size()) {
2142 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2144 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2145 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2146 } else if (cursor.par()->next())
2147 setCursor(bview, cursor.par()->next(), 0);
2151 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2154 int x = cursor.x_fix();
2155 int y = cursor.y() - cursor.row()->baseline() - 1;
2156 setCursorFromCoordinates(bview, x, y);
2159 int y1 = cursor.iy() - topy;
2162 Inset * inset_hit = checkInsetHit(bview, x, y1);
2163 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2164 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2168 setCursorFromCoordinates(bview, cursor.x_fix(),
2169 cursor.y() - cursor.row()->baseline() - 1);
2174 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2177 int x = cursor.x_fix();
2178 int y = cursor.y() - cursor.row()->baseline() +
2179 cursor.row()->height() + 1;
2180 setCursorFromCoordinates(bview, x, y);
2181 if (!selecting && cursor.row() == cursor.irow()) {
2183 int y1 = cursor.iy() - topy;
2186 Inset * inset_hit = checkInsetHit(bview, x, y1);
2187 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2188 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2192 setCursorFromCoordinates(bview, cursor.x_fix(),
2193 cursor.y() - cursor.row()->baseline()
2194 + cursor.row()->height() + 1);
2199 void LyXText::cursorUpParagraph(BufferView * bview) const
2201 if (cursor.pos() > 0) {
2202 setCursor(bview, cursor.par(), 0);
2204 else if (cursor.par()->previous()) {
2205 setCursor(bview, cursor.par()->previous(), 0);
2210 void LyXText::cursorDownParagraph(BufferView * bview) const
2212 if (cursor.par()->next()) {
2213 setCursor(bview, cursor.par()->next(), 0);
2215 setCursor(bview, cursor.par(), cursor.par()->size());
2219 // fix the cursor `cur' after a characters has been deleted at `where'
2220 // position. Called by deleteEmptyParagraphMechanism
2221 void LyXText::fixCursorAfterDelete(BufferView * bview,
2223 LyXCursor const & where) const
2225 // if cursor is not in the paragraph where the delete occured,
2227 if (cur.par() != where.par())
2230 // if cursor position is after the place where the delete occured,
2232 if (cur.pos() > where.pos())
2233 cur.pos(cur.pos()-1);
2235 // check also if we don't want to set the cursor on a spot behind the
2236 // pagragraph because we erased the last character.
2237 if (cur.pos() > cur.par()->size())
2238 cur.pos(cur.par()->size());
2240 // recompute row et al. for this cursor
2241 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2245 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2246 LyXCursor const & old_cursor) const
2248 // Would be wrong to delete anything if we have a selection.
2249 if (selection.set())
2252 // We allow all kinds of "mumbo-jumbo" when freespacing.
2253 if (old_cursor.par()->layout()->free_spacing
2254 || old_cursor.par()->isFreeSpacing()) {
2258 /* Ok I'll put some comments here about what is missing.
2259 I have fixed BackSpace (and thus Delete) to not delete
2260 double-spaces automagically. I have also changed Cut,
2261 Copy and Paste to hopefully do some sensible things.
2262 There are still some small problems that can lead to
2263 double spaces stored in the document file or space at
2264 the beginning of paragraphs. This happens if you have
2265 the cursor betwenn to spaces and then save. Or if you
2266 cut and paste and the selection have a space at the
2267 beginning and then save right after the paste. I am
2268 sure none of these are very hard to fix, but I will
2269 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2270 that I can get some feedback. (Lgb)
2273 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2274 // delete the LineSeparator.
2277 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2278 // delete the LineSeparator.
2281 // If the pos around the old_cursor were spaces, delete one of them.
2282 if (old_cursor.par() != cursor.par()
2283 || old_cursor.pos() != cursor.pos()) {
2284 // Only if the cursor has really moved
2286 if (old_cursor.pos() > 0
2287 && old_cursor.pos() < old_cursor.par()->size()
2288 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2289 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2290 old_cursor.par()->erase(old_cursor.pos() - 1);
2291 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2293 #ifdef WITH_WARNINGS
2294 #warning This will not work anymore when we have multiple views of the same buffer
2295 // In this case, we will have to correct also the cursors held by
2296 // other bufferviews. It will probably be easier to do that in a more
2297 // automated way in LyXCursor code. (JMarc 26/09/2001)
2299 // correct all cursors held by the LyXText
2300 fixCursorAfterDelete(bview, cursor, old_cursor);
2301 fixCursorAfterDelete(bview, selection.cursor,
2303 fixCursorAfterDelete(bview, selection.start,
2305 fixCursorAfterDelete(bview, selection.end, old_cursor);
2306 fixCursorAfterDelete(bview, last_sel_cursor,
2308 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2309 fixCursorAfterDelete(bview, toggle_end_cursor,
2315 // don't delete anything if this is the ONLY paragraph!
2316 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2319 // Do not delete empty paragraphs with keepempty set.
2320 if (old_cursor.par()->layout()->keepempty)
2323 // only do our magic if we changed paragraph
2324 if (old_cursor.par() == cursor.par())
2327 // record if we have deleted a paragraph
2328 // we can't possibly have deleted a paragraph before this point
2329 bool deleted = false;
2331 if ((old_cursor.par()->empty()
2332 || (old_cursor.par()->size() == 1
2333 && old_cursor.par()->isLineSeparator(0)))) {
2334 // ok, we will delete anything
2335 LyXCursor tmpcursor;
2337 // make sure that you do not delete any environments
2338 status(bview, LyXText::NEED_MORE_REFRESH);
2341 if (old_cursor.row()->previous()) {
2342 refresh_row = old_cursor.row()->previous();
2343 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2345 cursor = old_cursor; // that undo can restore the right cursor position
2346 Paragraph * endpar = old_cursor.par()->next();
2347 if (endpar && endpar->getDepth()) {
2348 while (endpar && endpar->getDepth()) {
2349 endpar = endpar->next();
2352 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2356 removeRow(old_cursor.row());
2357 if (ownerParagraph() == old_cursor.par()) {
2358 ownerParagraph(ownerParagraph()->next());
2361 delete old_cursor.par();
2363 /* Breakagain the next par. Needed because of
2364 * the parindent that can occur or dissappear.
2365 * The next row can change its height, if
2366 * there is another layout before */
2367 if (refresh_row->next()) {
2368 breakAgain(bview, refresh_row->next());
2369 updateCounters(bview);
2371 setHeightOfRow(bview, refresh_row);
2373 refresh_row = old_cursor.row()->next();
2374 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2377 cursor = old_cursor; // that undo can restore the right cursor position
2378 Paragraph * endpar = old_cursor.par()->next();
2379 if (endpar && endpar->getDepth()) {
2380 while (endpar && endpar->getDepth()) {
2381 endpar = endpar->next();
2384 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2388 removeRow(old_cursor.row());
2390 if (ownerParagraph() == old_cursor.par()) {
2391 ownerParagraph(ownerParagraph()->next());
2394 delete old_cursor.par();
2396 /* Breakagain the next par. Needed because of
2397 the parindent that can occur or dissappear.
2398 The next row can change its height, if
2399 there is another layout before */
2401 breakAgain(bview, refresh_row);
2402 updateCounters(bview);
2407 setCursorIntern(bview, cursor.par(), cursor.pos());
2409 if (selection.cursor.par() == old_cursor.par()
2410 && selection.cursor.pos() == old_cursor.pos()) {
2411 // correct selection
2412 selection.cursor = cursor;
2416 if (old_cursor.par()->stripLeadingSpaces()) {
2417 redoParagraphs(bview, old_cursor,
2418 old_cursor.par()->next());
2420 setCursorIntern(bview, cursor.par(), cursor.pos());
2421 selection.cursor = cursor;
2428 Paragraph * LyXText::ownerParagraph() const
2431 return inset_owner->paragraph();
2433 return &*(bv_owner->buffer()->paragraphs.begin());
2437 void LyXText::ownerParagraph(Paragraph * p) const
2440 inset_owner->paragraph(p);
2442 bv_owner->buffer()->paragraphs.set(p);
2447 void LyXText::ownerParagraph(int id, Paragraph * p) const
2449 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2450 if (op && op->inInset()) {
2451 static_cast<InsetText *>(op->inInset())->paragraph(p);
2458 LyXText::text_status LyXText::status() const
2464 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2466 LyXText * t = bview->text;
2468 // We should only go up with refreshing code so this means that if
2469 // we have a MORE refresh we should never set it to LITTLE if we still
2470 // didn't handle it (and then it will be UNCHANGED. Now as long as
2471 // we stay inside one LyXText this may work but we need to tell the
2472 // outermost LyXText that it should REALLY draw us if there is some
2473 // change in a Inset::LyXText. So you see that when we are inside a
2474 // inset's LyXText we give the LITTLE to the outermost LyXText to
2475 // tell'em that it should redraw the actual row (where the inset
2476 // resides! Capito?!
2478 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2480 if (inset_owner && st != UNCHANGED) {
2481 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2482 if (!t->refresh_row) {
2483 t->refresh_row = t->cursor.row();
2484 t->refresh_y = t->cursor.y() -
2485 t->cursor.row()->baseline();
2492 bool LyXText::isTopLevel() const
2494 /// only the top-level lyxtext has a non-null bv owner
2499 bool LyXText::isInInset() const
2505 int defaultRowHeight()
2507 LyXFont const font(LyXFont::ALL_SANE);
2508 return int(font_metrics::maxAscent(font)
2509 + font_metrics::maxDescent(font) * 1.5);