1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "frontends/LyXView.h"
21 #include "undo_funcs.h"
23 #include "bufferparams.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
37 #include "insets/inseterror.h"
38 #include "insets/insetbib.h"
39 #include "insets/insetspecialchar.h"
40 #include "insets/insettext.h"
41 #include "insets/insetfloat.h"
43 #include "support/LAssert.h"
44 #include "support/textutils.h"
45 #include "support/lstrings.h"
55 LyXText::LyXText(BufferView * bv)
56 : number_of_rows(0), height(0), width(0), first_y(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
63 LyXText::LyXText(InsetText * inset)
64 : number_of_rows(0), height(0), width(0), first_y(0),
65 bv_owner(0), inset_owner(inset), the_locking_inset(0),
66 need_break_row(0), refresh_y(0), refresh_row(0),
67 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
71 void LyXText::init(BufferView * bview, bool reinit)
74 // Delete all rows, this does not touch the paragraphs!
75 Row * tmprow = firstrow;
77 tmprow = firstrow->next();
86 copylayouttype.erase();
87 number_of_rows = first_y = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
96 insertParagraph(bview, par, lastrow);
99 setCursorIntern(bview, firstrow->par(), 0);
100 selection.cursor = cursor;
106 // Delete all rows, this does not touch the paragraphs!
107 Row * tmprow = firstrow;
109 tmprow = firstrow->next();
118 LyXFont const realizeFont(LyXFont const & font,
122 LyXTextClass const & tclass = buf->params.getLyXTextClass();
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(tclass[par->layout()]->font,
134 buf->params.language);
136 par_depth = par->getDepth();
140 #ifndef INHERIT_LANGUAGE
141 tmpfont.realize(tclass.defaultfont());
143 tmpfont.realize(tclass.defaultfont(), buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout_ptr const & layout = par->layout();
166 // We specialize the 95% common case:
167 if (!par->getDepth()) {
168 if (layout->labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params, pos);
173 par->inInset()->getDrawFont(f);
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout->reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
182 par->inInset()->getDrawFont(f);
183 #ifndef INHERIT_LANGUAGE
184 return f.realize(layout->resfont);
186 return f.realize(layout.resfont, buf->params.language);
191 // The uncommon case need not be optimized as much
195 if (pos < beginningOfMainBody(buf, par)) {
197 layoutfont = layout->labelfont;
200 layoutfont = layout->font;
203 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
204 #ifndef INHERIT_LANGUAGE
205 tmpfont.realize(layoutfont);
207 tmpfont.realize(layoutfont, buf->params.language);
210 par->inInset()->getDrawFont(tmpfont);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout_ptr const & layout = par->layout();
220 if (!par->getDepth()) {
221 return layout->resfont;
224 return realizeFont(layout->font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout_ptr const & layout = par->layout();
232 if (!par->getDepth()) {
233 return layout->reslabelfont;
236 return realizeFont(layout->labelfont, buf, par);
240 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
241 pos_type pos, LyXFont const & fnt,
244 Buffer const * buf = bv->buffer();
245 LyXFont font = getFont(buf, par, pos);
246 font.update(fnt, buf->params.language, toggleall);
247 // Let the insets convert their font
248 if (par->isInset(pos)) {
249 Inset * inset = par->getInset(pos);
250 if (isEditableInset(inset)) {
251 UpdatableInset * uinset =
252 static_cast<UpdatableInset *>(inset);
253 uinset->setFont(bv, fnt, toggleall, true);
257 // Plug thru to version below:
258 setCharFont(buf, par, pos, font);
262 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
263 pos_type pos, LyXFont const & fnt)
267 LyXTextClass const & tclass = buf->params.getLyXTextClass();
268 LyXLayout_ptr const & layout = par->layout();
270 // Get concrete layout font to reduce against
273 if (pos < beginningOfMainBody(buf, par))
274 layoutfont = layout->labelfont;
276 layoutfont = layout->font;
278 // Realize against environment font information
279 if (par->getDepth()) {
280 Paragraph * tp = par;
281 while (!layoutfont.resolved() && tp && tp->getDepth()) {
282 tp = tp->outerHook();
284 #ifndef INHERIT_LANGUAGE
285 layoutfont.realize(tp->layout()->font);
287 layoutfont.realize(tclass[tp->layout()].font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(tclass.defaultfont());
296 layoutfont.realize(tclass.defaultfont(), buf->params.language);
299 // Now, reduce font against full layout font
300 font.reduce(layoutfont);
302 par->setFont(pos, font);
306 // inserts a new row behind the specified row, increments
307 // the touched counters
308 void LyXText::insertRow(Row * row, Paragraph * par,
311 Row * tmprow = new Row;
314 tmprow->next(firstrow);
317 tmprow->previous(row);
318 tmprow->next(row->next());
323 tmprow->next()->previous(tmprow);
325 if (tmprow->previous())
326 tmprow->previous()->next(tmprow);
338 // removes the row and reset the touched counters
339 void LyXText::removeRow(Row * row) const
341 Row * row_prev = row->previous();
343 row->next()->previous(row_prev);
345 firstrow = row->next();
346 // lyx::Assert(firstrow);
348 row_prev->next(row->next());
350 if (row == lastrow) {
351 lyx::Assert(!row->next());
354 if (refresh_row == row) {
355 refresh_row = row_prev ? row_prev : row->next();
356 // what about refresh_y, refresh_height
359 height -= row->height(); // the text becomes smaller
362 --number_of_rows; // one row less
366 // remove all following rows of the paragraph of the specified row.
367 void LyXText::removeParagraph(Row * row) const
369 Paragraph * tmppar = row->par();
373 while (row && row->par() == tmppar) {
374 tmprow = row->next();
381 // insert the specified paragraph behind the specified row
382 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
385 // insert a new row, starting at position 0
386 insertRow(row, par, 0);
389 setCounter(bview->buffer(), par);
391 // and now append the whole paragraph behind the new row
394 appendParagraph(bview, firstrow);
396 row->next()->height(0);
397 appendParagraph(bview, row->next());
402 Inset * LyXText::getInset() const
405 if (cursor.pos() == 0 && cursor.par()->bibkey) {
406 inset = cursor.par()->bibkey;
407 } else if (cursor.pos() < cursor.par()->size()
408 && cursor.par()->isInset(cursor.pos())) {
409 inset = cursor.par()->getInset(cursor.pos());
415 void LyXText::toggleInset(BufferView * bview)
417 Inset * inset = getInset();
418 // is there an editable inset at cursor position?
419 if (!isEditableInset(inset)) {
420 // No, try to see if we are inside a collapsable inset
421 if (inset_owner && inset_owner->owner()
422 && inset_owner->owner()->isOpen()) {
423 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
424 inset_owner->owner()->close(bview);
425 bview->getLyXText()->cursorRight(bview);
429 //bview->owner()->message(inset->editMessage());
431 // do we want to keep this?? (JMarc)
432 if (!isHighlyEditableInset(inset))
433 setCursorParUndo(bview);
435 if (inset->isOpen()) {
441 inset->open(bview, !inset->isOpen());
446 /* used in setlayout */
447 // Asger is not sure we want to do this...
448 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
451 LyXLayout_ptr const & layout = par->layout();
454 for (pos_type pos = 0; pos < par->size(); ++pos) {
455 if (pos < beginningOfMainBody(buf, par))
456 layoutfont = layout->labelfont;
458 layoutfont = layout->font;
460 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
461 tmpfont.reduce(layoutfont);
462 par->setFont(pos, tmpfont);
467 Paragraph * LyXText::setLayout(BufferView * bview,
468 LyXCursor & cur, LyXCursor & sstart_cur,
469 LyXCursor & send_cur,
470 string const & layout)
472 Paragraph * endpar = send_cur.par()->next();
473 Paragraph * undoendpar = endpar;
475 if (endpar && endpar->getDepth()) {
476 while (endpar && endpar->getDepth()) {
477 endpar = endpar->next();
481 endpar = endpar->next(); // because of parindents etc.
484 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
486 // ok we have a selection. This is always between sstart_cur
487 // and sel_end cursor
489 Paragraph * par = sstart_cur.par();
490 Paragraph * epar = send_cur.par()->next();
492 LyXLayout_ptr const & lyxlayout =
493 bview->buffer()->params.getLyXTextClass()[layout];
496 par->applyLayout(lyxlayout);
497 makeFontEntriesLayoutSpecific(bview->buffer(), par);
498 Paragraph * fppar = par;
499 fppar->params().spaceTop(lyxlayout->fill_top ?
500 VSpace(VSpace::VFILL)
501 : VSpace(VSpace::NONE));
502 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
503 VSpace(VSpace::VFILL)
504 : VSpace(VSpace::NONE));
505 if (lyxlayout->margintype == MARGIN_MANUAL)
506 par->setLabelWidthString(lyxlayout->labelstring());
507 if (lyxlayout->labeltype != LABEL_BIBLIO
509 delete fppar->bibkey;
514 } while (par != epar);
520 // set layout over selection and make a total rebreak of those paragraphs
521 void LyXText::setLayout(BufferView * bview, string const & layout)
523 LyXCursor tmpcursor = cursor; /* store the current cursor */
525 // if there is no selection just set the layout
526 // of the current paragraph */
527 if (!selection.set()) {
528 selection.start = cursor; // dummy selection
529 selection.end = cursor;
531 Paragraph * endpar = setLayout(bview, cursor, selection.start,
532 selection.end, layout);
533 redoParagraphs(bview, selection.start, endpar);
535 // we have to reset the selection, because the
536 // geometry could have changed
537 setCursor(bview, selection.start.par(),
538 selection.start.pos(), false);
539 selection.cursor = cursor;
540 setCursor(bview, selection.end.par(), selection.end.pos(), false);
541 updateCounters(bview);
544 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
548 // increment depth over selection and
549 // make a total rebreak of those paragraphs
550 void LyXText::incDepth(BufferView * bview)
552 // If there is no selection, just use the current paragraph
553 if (!selection.set()) {
554 selection.start = cursor; // dummy selection
555 selection.end = cursor;
558 // We end at the next paragraph with depth 0
559 Paragraph * endpar = selection.end.par()->next();
561 Paragraph * undoendpar = endpar;
563 if (endpar && endpar->getDepth()) {
564 while (endpar && endpar->getDepth()) {
565 endpar = endpar->next();
569 endpar = endpar->next(); // because of parindents etc.
572 setUndo(bview, Undo::EDIT,
573 selection.start.par(), undoendpar);
575 LyXCursor tmpcursor = cursor; // store the current cursor
577 // ok we have a selection. This is always between sel_start_cursor
578 // and sel_end cursor
579 cursor = selection.start;
581 bool anything_changed = false;
584 // NOTE: you can't change the depth of a bibliography entry
585 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
586 Paragraph * prev = cursor.par()->previous();
589 if (cursor.par()->getDepth()
590 < prev->getMaxDepthAfter()) {
591 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
592 anything_changed = true;
596 if (cursor.par() == selection.end.par())
598 cursor.par(cursor.par()->next());
601 // if nothing changed set all depth to 0
602 if (!anything_changed) {
603 cursor = selection.start;
604 while (cursor.par() != selection.end.par()) {
605 cursor.par()->params().depth(0);
606 cursor.par(cursor.par()->next());
608 cursor.par()->params().depth(0);
611 redoParagraphs(bview, selection.start, endpar);
613 // we have to reset the selection, because the
614 // geometry could have changed
615 setCursor(bview, selection.start.par(), selection.start.pos());
616 selection.cursor = cursor;
617 setCursor(bview, selection.end.par(), selection.end.pos());
618 updateCounters(bview);
621 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
625 // decrement depth over selection and
626 // make a total rebreak of those paragraphs
627 void LyXText::decDepth(BufferView * bview)
629 // if there is no selection just set the layout
630 // of the current paragraph
631 if (!selection.set()) {
632 selection.start = cursor; // dummy selection
633 selection.end = cursor;
635 Paragraph * endpar = selection.end.par()->next();
636 Paragraph * undoendpar = endpar;
638 if (endpar && endpar->getDepth()) {
639 while (endpar && endpar->getDepth()) {
640 endpar = endpar->next();
644 endpar = endpar->next(); // because of parindents etc.
647 setUndo(bview, Undo::EDIT,
648 selection.start.par(), undoendpar);
650 LyXCursor tmpcursor = cursor; // store the current cursor
652 // ok we have a selection. This is always between sel_start_cursor
653 // and sel_end cursor
654 cursor = selection.start;
657 if (cursor.par()->params().depth()) {
658 cursor.par()->params()
659 .depth(cursor.par()->params().depth() - 1);
661 if (cursor.par() == selection.end.par()) {
664 cursor.par(cursor.par()->next());
667 redoParagraphs(bview, selection.start, endpar);
669 // we have to reset the selection, because the
670 // geometry could have changed
671 setCursor(bview, selection.start.par(),
672 selection.start.pos());
673 selection.cursor = cursor;
674 setCursor(bview, selection.end.par(), selection.end.pos());
675 updateCounters(bview);
678 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
682 // set font over selection and make a total rebreak of those paragraphs
683 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
685 // if there is no selection just set the current_font
686 if (!selection.set()) {
687 // Determine basis font
689 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
691 layoutfont = getLabelFont(bview->buffer(),
694 layoutfont = getLayoutFont(bview->buffer(),
697 // Update current font
698 real_current_font.update(font,
699 bview->buffer()->params.language,
702 // Reduce to implicit settings
703 current_font = real_current_font;
704 current_font.reduce(layoutfont);
705 // And resolve it completely
706 #ifndef INHERIT_LANGUAGE
707 real_current_font.realize(layoutfont);
709 real_current_font.realize(layoutfont,
710 bview->buffer()->params.language);
715 LyXCursor tmpcursor = cursor; // store the current cursor
717 // ok we have a selection. This is always between sel_start_cursor
718 // and sel_end cursor
720 setUndo(bview, Undo::EDIT,
721 selection.start.par(), selection.end.par()->next());
723 cursor = selection.start;
724 while (cursor.par() != selection.end.par() ||
725 cursor.pos() < selection.end.pos())
727 if (cursor.pos() < cursor.par()->size()) {
728 // an open footnote should behave like a closed one
729 setCharFont(bview, cursor.par(), cursor.pos(),
731 cursor.pos(cursor.pos() + 1);
734 cursor.par(cursor.par()->next());
739 redoParagraphs(bview, selection.start, selection.end.par()->next());
741 // we have to reset the selection, because the
742 // geometry could have changed, but we keep
743 // it for user convenience
744 setCursor(bview, selection.start.par(), selection.start.pos());
745 selection.cursor = cursor;
746 setCursor(bview, selection.end.par(), selection.end.pos());
748 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
749 tmpcursor.boundary());
753 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
755 Row * tmprow = cur.row();
756 int y = cur.y() - tmprow->baseline();
758 setHeightOfRow(bview, tmprow);
760 while (tmprow->previous()
761 && tmprow->previous()->par() == tmprow->par()) {
762 tmprow = tmprow->previous();
763 y -= tmprow->height();
764 setHeightOfRow(bview, tmprow);
767 // we can set the refreshing parameters now
768 status(bview, LyXText::NEED_MORE_REFRESH);
770 refresh_row = tmprow;
771 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
775 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
777 Row * tmprow = cur.row();
779 int y = cur.y() - tmprow->baseline();
780 setHeightOfRow(bview, tmprow);
782 while (tmprow->previous()
783 && tmprow->previous()->par() == tmprow->par()) {
784 tmprow = tmprow->previous();
785 y -= tmprow->height();
788 // we can set the refreshing parameters now
789 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
791 refresh_row = tmprow;
793 status(bview, LyXText::NEED_MORE_REFRESH);
794 setCursor(bview, cur.par(), cur.pos());
798 // deletes and inserts again all paragaphs between the cursor
799 // and the specified par
800 // This function is needed after SetLayout and SetFont etc.
801 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
802 Paragraph const * endpar) const
805 Paragraph * tmppar = 0;
806 Paragraph * first_phys_par = 0;
808 Row * tmprow = cur.row();
810 int y = cur.y() - tmprow->baseline();
812 if (!tmprow->previous()) {
813 // a trick/hack for UNDO
814 // This is needed because in an UNDO/REDO we could have changed
815 // the ownerParagrah() so the paragraph inside the row is NOT
816 // my really first par anymore. Got it Lars ;) (Jug 20011206)
817 first_phys_par = ownerParagraph();
819 first_phys_par = tmprow->par();
820 while (tmprow->previous()
821 && tmprow->previous()->par() == first_phys_par)
823 tmprow = tmprow->previous();
824 y -= tmprow->height();
828 // we can set the refreshing parameters now
829 status(bview, LyXText::NEED_MORE_REFRESH);
831 refresh_row = tmprow->previous(); /* the real refresh row will
832 be deleted, so I store
836 tmppar = tmprow->next()->par();
839 while (tmprow->next() && tmppar != endpar) {
840 removeRow(tmprow->next());
841 if (tmprow->next()) {
842 tmppar = tmprow->next()->par();
848 // remove the first one
849 tmprow2 = tmprow; /* this is because tmprow->previous()
851 tmprow = tmprow->previous();
854 tmppar = first_phys_par;
858 insertParagraph(bview, tmppar, tmprow);
862 while (tmprow->next()
863 && tmprow->next()->par() == tmppar) {
864 tmprow = tmprow->next();
866 tmppar = tmppar->next();
868 } while (tmppar && tmppar != endpar);
870 // this is because of layout changes
872 refresh_y -= refresh_row->height();
873 setHeightOfRow(bview, refresh_row);
875 refresh_row = firstrow;
877 setHeightOfRow(bview, refresh_row);
880 if (tmprow && tmprow->next())
881 setHeightOfRow(bview, tmprow->next());
882 updateCounters(bview);
886 void LyXText::fullRebreak(BufferView * bview)
892 if (need_break_row) {
893 breakAgain(bview, need_break_row);
900 // important for the screen
903 // the cursor set functions have a special mechanism. When they
904 // realize, that you left an empty paragraph, they will delete it.
905 // They also delete the corresponding row
907 // need the selection cursor:
908 void LyXText::setSelection(BufferView * bview)
910 bool const lsel = selection.set();
912 if (!selection.set()) {
913 last_sel_cursor = selection.cursor;
914 selection.start = selection.cursor;
915 selection.end = selection.cursor;
920 // first the toggling area
921 if (cursor.y() < last_sel_cursor.y()
922 || (cursor.y() == last_sel_cursor.y()
923 && cursor.x() < last_sel_cursor.x())) {
924 toggle_end_cursor = last_sel_cursor;
925 toggle_cursor = cursor;
927 toggle_end_cursor = cursor;
928 toggle_cursor = last_sel_cursor;
931 last_sel_cursor = cursor;
933 // and now the whole selection
935 if (selection.cursor.par() == cursor.par())
936 if (selection.cursor.pos() < cursor.pos()) {
937 selection.end = cursor;
938 selection.start = selection.cursor;
940 selection.end = selection.cursor;
941 selection.start = cursor;
943 else if (selection.cursor.y() < cursor.y() ||
944 (selection.cursor.y() == cursor.y()
945 && selection.cursor.x() < cursor.x())) {
946 selection.end = cursor;
947 selection.start = selection.cursor;
950 selection.end = selection.cursor;
951 selection.start = cursor;
954 // a selection with no contents is not a selection
955 if (selection.start.par() == selection.end.par() &&
956 selection.start.pos() == selection.end.pos())
957 selection.set(false);
959 if (inset_owner && (selection.set() || lsel))
960 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
964 string const LyXText::selectionAsString(Buffer const * buffer,
967 if (!selection.set()) return string();
969 // should be const ...
970 Paragraph * startpar(selection.start.par());
971 Paragraph * endpar(selection.end.par());
972 pos_type const startpos(selection.start.pos());
973 pos_type const endpos(selection.end.pos());
975 if (startpar == endpar) {
976 return startpar->asString(buffer, startpos, endpos, label);
981 // First paragraph in selection
982 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
984 // The paragraphs in between (if any)
985 LyXCursor tmpcur(selection.start);
986 tmpcur.par(tmpcur.par()->next());
987 while (tmpcur.par() != endpar) {
988 result += tmpcur.par()->asString(buffer, 0,
989 tmpcur.par()->size(),
991 tmpcur.par(tmpcur.par()->next());
994 // Last paragraph in selection
995 result += endpar->asString(buffer, 0, endpos, label);
1001 void LyXText::clearSelection() const
1003 selection.set(false);
1004 selection.mark(false);
1005 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1006 // reset this in the bv_owner!
1007 if (bv_owner && bv_owner->text)
1008 bv_owner->text->xsel_cache.set(false);
1012 void LyXText::cursorHome(BufferView * bview) const
1014 setCursor(bview, cursor.par(), cursor.row()->pos());
1018 void LyXText::cursorEnd(BufferView * bview) const
1020 if (!cursor.row()->next()
1021 || cursor.row()->next()->par() != cursor.row()->par()) {
1022 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1024 if (!cursor.par()->empty() &&
1025 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1026 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1027 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1029 setCursor(bview,cursor.par(),
1030 rowLast(cursor.row()) + 1);
1036 void LyXText::cursorTop(BufferView * bview) const
1038 while (cursor.par()->previous())
1039 cursor.par(cursor.par()->previous());
1040 setCursor(bview, cursor.par(), 0);
1044 void LyXText::cursorBottom(BufferView * bview) const
1046 while (cursor.par()->next())
1047 cursor.par(cursor.par()->next());
1048 setCursor(bview, cursor.par(), cursor.par()->size());
1052 void LyXText::toggleFree(BufferView * bview,
1053 LyXFont const & font, bool toggleall)
1055 // If the mask is completely neutral, tell user
1056 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1057 // Could only happen with user style
1058 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1062 // Try implicit word selection
1063 // If there is a change in the language the implicit word selection
1065 LyXCursor resetCursor = cursor;
1066 bool implicitSelection = (font.language() == ignore_language
1067 && font.number() == LyXFont::IGNORE)
1068 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1071 setFont(bview, font, toggleall);
1073 // Implicit selections are cleared afterwards
1074 //and cursor is set to the original position.
1075 if (implicitSelection) {
1077 cursor = resetCursor;
1078 setCursor(bview, cursor.par(), cursor.pos());
1079 selection.cursor = cursor;
1082 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1086 string LyXText::getStringToIndex(BufferView * bview)
1090 // Try implicit word selection
1091 // If there is a change in the language the implicit word selection
1093 LyXCursor const reset_cursor = cursor;
1094 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1096 if (!selection.set()) {
1097 bview->owner()->message(_("Nothing to index!"));
1100 if (selection.start.par() != selection.end.par()) {
1101 bview->owner()->message(_("Cannot index more than one paragraph!"));
1105 idxstring = selectionAsString(bview->buffer(), false);
1107 // Implicit selections are cleared afterwards
1108 //and cursor is set to the original position.
1109 if (implicitSelection) {
1111 cursor = reset_cursor;
1112 setCursor(bview, cursor.par(), cursor.pos());
1113 selection.cursor = cursor;
1119 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1120 Paragraph const * par) const
1122 if (par->layout()->labeltype != LABEL_MANUAL)
1125 return par->beginningOfMainBody();
1129 // the DTP switches for paragraphs. LyX will store them in the first
1130 // physicla paragraph. When a paragraph is broken, the top settings rest,
1131 // the bottom settings are given to the new one. So I can make shure,
1132 // they do not duplicate themself and you cannnot make dirty things with
1135 void LyXText::setParagraph(BufferView * bview,
1136 bool line_top, bool line_bottom,
1137 bool pagebreak_top, bool pagebreak_bottom,
1138 VSpace const & space_top,
1139 VSpace const & space_bottom,
1140 Spacing const & spacing,
1142 string labelwidthstring,
1145 LyXCursor tmpcursor = cursor;
1146 if (!selection.set()) {
1147 selection.start = cursor;
1148 selection.end = cursor;
1151 // make sure that the depth behind the selection are restored, too
1152 Paragraph * endpar = selection.end.par()->next();
1153 Paragraph * undoendpar = endpar;
1155 if (endpar && endpar->getDepth()) {
1156 while (endpar && endpar->getDepth()) {
1157 endpar = endpar->next();
1158 undoendpar = endpar;
1162 // because of parindents etc.
1163 endpar = endpar->next();
1166 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1169 Paragraph * tmppar = selection.end.par();
1171 while (tmppar != selection.start.par()->previous()) {
1172 setCursor(bview, tmppar, 0);
1173 status(bview, LyXText::NEED_MORE_REFRESH);
1174 refresh_row = cursor.row();
1175 refresh_y = cursor.y() - cursor.row()->baseline();
1176 cursor.par()->params().lineTop(line_top);
1177 cursor.par()->params().lineBottom(line_bottom);
1178 cursor.par()->params().pagebreakTop(pagebreak_top);
1179 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1180 cursor.par()->params().spaceTop(space_top);
1181 cursor.par()->params().spaceBottom(space_bottom);
1182 cursor.par()->params().spacing(spacing);
1183 // does the layout allow the new alignment?
1184 LyXLayout_ptr const & layout = cursor.par()->layout();
1186 if (align == LYX_ALIGN_LAYOUT)
1187 align = layout->align;
1188 if (align & layout->alignpossible) {
1189 if (align == layout->align)
1190 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1192 cursor.par()->params().align(align);
1194 cursor.par()->setLabelWidthString(labelwidthstring);
1195 cursor.par()->params().noindent(noindent);
1196 tmppar = cursor.par()->previous();
1199 redoParagraphs(bview, selection.start, endpar);
1202 setCursor(bview, selection.start.par(), selection.start.pos());
1203 selection.cursor = cursor;
1204 setCursor(bview, selection.end.par(), selection.end.pos());
1205 setSelection(bview);
1206 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1208 bview->updateInset(inset_owner, true);
1212 // set the counter of a paragraph. This includes the labels
1213 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1215 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1216 LyXLayout_ptr const & layout = par->layout();
1218 if (par->previous()) {
1220 par->params().appendix(par->previous()->params().appendix());
1221 if (!par->params().appendix() && par->params().startOfAppendix()) {
1222 par->params().appendix(true);
1223 buf->counters().reset();
1225 par->enumdepth = par->previous()->enumdepth;
1226 par->itemdepth = par->previous()->itemdepth;
1228 par->params().appendix(par->params().startOfAppendix());
1233 /* Maybe we have to increment the enumeration depth.
1234 * BUT, enumeration in a footnote is considered in isolation from its
1235 * surrounding paragraph so don't increment if this is the
1236 * first line of the footnote
1237 * AND, bibliographies can't have their depth changed ie. they
1238 * are always of depth 0
1241 && par->previous()->getDepth() < par->getDepth()
1242 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1243 && par->enumdepth < 3
1244 && layout->labeltype != LABEL_BIBLIO) {
1248 // Maybe we have to decrement the enumeration depth, see note above
1250 && par->previous()->getDepth() > par->getDepth()
1251 && layout->labeltype != LABEL_BIBLIO) {
1252 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1255 if (!par->params().labelString().empty()) {
1256 par->params().labelString(string());
1259 if (layout->margintype == MARGIN_MANUAL) {
1260 if (par->params().labelWidthString().empty()) {
1261 par->setLabelWidthString(layout->labelstring());
1264 par->setLabelWidthString(string());
1267 // is it a layout that has an automatic label?
1268 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1270 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1271 string numbertype, langtype;
1274 if (i >= 0 && i <= buf->params.secnumdepth) {
1276 buf->counters().step(buf->counters().sects[i]);
1278 // Is there a label? Useful for Chapter layout
1279 if (!par->params().appendix()) {
1280 if (!layout->labelstring().empty())
1281 par->params().labelString(layout->labelstring());
1283 par->params().labelString(string());
1285 if (!layout->labelstring_appendix().empty())
1286 par->params().labelString(layout->labelstring_appendix());
1288 par->params().labelString(string());
1291 // Use if an integer is here less than elegant. For now.
1292 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1293 if (!par->params().appendix()) {
1294 numbertype = "sectioning";
1296 numbertype = "appendix";
1297 if (par->isRightToLeftPar(buf->params))
1298 langtype = "hebrew";
1303 s << buf->counters().numberLabel(buf->counters().sects[i],
1304 numbertype, langtype, head);
1306 par->params().labelString(par->params().labelString() + s.str().c_str());
1307 // We really want to remove the c_str as soon as
1310 // reset enum counters
1311 buf->counters().reset("enum");
1312 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1313 buf->counters().reset("enum");
1314 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1315 buf->counters().step(buf->counters().enums[par->enumdepth]);
1317 s << buf->counters().numberLabel(buf->counters().enums[par->enumdepth],
1318 "enumeration", langtype);
1319 par->params().labelString(s.str().c_str());
1322 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1323 buf->counters().step("bibitem");
1324 int number = buf->counters().value("bibitem");
1326 InsetCommandParams p("bibitem" );
1327 par->bibkey = new InsetBibKey(p);
1329 par->bibkey->setCounter(number);
1330 par->params().labelString(layout->labelstring());
1332 // In biblio should't be following counters but...
1334 string s = layout->labelstring();
1336 // the caption hack:
1337 if (layout->labeltype == LABEL_SENSITIVE) {
1338 Paragraph * tmppar = par;
1341 while (tmppar && tmppar->inInset()
1342 // the single '=' is intended below
1343 && (in = tmppar->inInset()->owner())) {
1344 if (in->lyxCode() == Inset::FLOAT_CODE) {
1348 tmppar = in->parOwner();
1354 = floatList.getType(static_cast<InsetFloat*>(in)->type());
1356 buf->counters().step(fl.name());
1358 // Doesn't work... yet.
1360 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1361 o << fl.name() << " #:";
1364 // par->SetLayout(0);
1365 // s = layout->labelstring;
1366 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1367 ? " :úåòîùî øñç" : "Senseless: ";
1370 par->params().labelString(s);
1372 // reset the enumeration counter. They are always resetted
1373 // when there is any other layout between
1374 for (int i = par->enumdepth; i < 4; ++i) {
1375 buf->counters().set(buf->counters().enums[i], 0);
1381 // Updates all counters BEHIND the row. Changed paragraphs
1382 // with a dynamic left margin will be rebroken.
1383 void LyXText::updateCounters(BufferView * bview) const
1387 Row * row = firstrow;
1390 bview->buffer()->counters().reset();
1392 while (row->par() != par)
1395 setCounter(bview->buffer(), par);
1397 // now check for the headline layouts. remember that they
1398 // have a dynamic left margin
1399 LyXLayout_ptr const & layout = par->layout();
1401 if (layout->margintype == MARGIN_DYNAMIC
1402 || layout->labeltype == LABEL_SENSITIVE) {
1403 // Rebreak the paragraph
1404 removeParagraph(row);
1405 appendParagraph(bview, row);
1412 void LyXText::insertInset(BufferView * bview, Inset * inset)
1414 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1416 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1418 cursor.par()->insertInset(cursor.pos(), inset);
1419 // Just to rebreak and refresh correctly.
1420 // The character will not be inserted a second time
1421 insertChar(bview, Paragraph::META_INSET);
1422 // If we enter a highly editable inset the cursor should be to before
1423 // the inset. This couldn't happen before as Undo was not handled inside
1424 // inset now after the Undo LyX tries to call inset->Edit(...) again
1425 // and cannot do this as the cursor is behind the inset and GetInset
1426 // does not return the inset!
1427 if (isHighlyEditableInset(inset)) {
1428 cursorLeft(bview, true);
1434 void LyXText::copyEnvironmentType()
1436 copylayouttype = cursor.par()->layout()->name();
1440 void LyXText::pasteEnvironmentType(BufferView * bview)
1442 setLayout(bview, copylayouttype);
1446 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1448 // Stuff what we got on the clipboard. Even if there is no selection.
1450 // There is a problem with having the stuffing here in that the
1451 // larger the selection the slower LyX will get. This can be
1452 // solved by running the line below only when the selection has
1453 // finished. The solution used currently just works, to make it
1454 // faster we need to be more clever and probably also have more
1455 // calls to stuffClipboard. (Lgb)
1456 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1458 // This doesn't make sense, if there is no selection
1459 if (!selection.set())
1462 // OK, we have a selection. This is always between selection.start
1463 // and selection.end
1465 // make sure that the depth behind the selection are restored, too
1466 Paragraph * endpar = selection.end.par()->next();
1467 Paragraph * undoendpar = endpar;
1469 if (endpar && endpar->getDepth()) {
1470 while (endpar && endpar->getDepth()) {
1471 endpar = endpar->next();
1472 undoendpar = endpar;
1474 } else if (endpar) {
1475 endpar = endpar->next(); // because of parindents etc.
1478 setUndo(bview, Undo::DELETE,
1479 selection.start.par(), undoendpar);
1481 // there are two cases: cut only within one paragraph or
1482 // more than one paragraph
1483 if (selection.start.par() == selection.end.par()) {
1484 // only within one paragraph
1485 endpar = selection.end.par();
1486 int pos = selection.end.pos();
1487 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1488 selection.start.pos(), pos,
1489 bview->buffer()->params.textclass,
1491 selection.end.pos(pos);
1493 endpar = selection.end.par();
1494 int pos = selection.end.pos();
1495 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1496 selection.start.pos(), pos,
1497 bview->buffer()->params.textclass,
1500 selection.end.par(endpar);
1501 selection.end.pos(pos);
1502 cursor.pos(selection.end.pos());
1504 endpar = endpar->next();
1506 // sometimes necessary
1508 selection.start.par()->stripLeadingSpaces();
1510 redoParagraphs(bview, selection.start, endpar);
1512 // cutSelection can invalidate the cursor so we need to set
1514 cursor = selection.start;
1516 // need a valid cursor. (Lgb)
1519 setCursor(bview, cursor.par(), cursor.pos());
1520 selection.cursor = cursor;
1521 updateCounters(bview);
1525 void LyXText::copySelection(BufferView * bview)
1527 // stuff the selection onto the X clipboard, from an explicit copy request
1528 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1530 // this doesnt make sense, if there is no selection
1531 if (!selection.set())
1534 // ok we have a selection. This is always between selection.start
1535 // and sel_end cursor
1537 // copy behind a space if there is one
1538 while (selection.start.par()->size() > selection.start.pos()
1539 && selection.start.par()->isLineSeparator(selection.start.pos())
1540 && (selection.start.par() != selection.end.par()
1541 || selection.start.pos() < selection.end.pos()))
1542 selection.start.pos(selection.start.pos() + 1);
1544 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1545 selection.start.pos(), selection.end.pos(),
1546 bview->buffer()->params.textclass);
1550 void LyXText::pasteSelection(BufferView * bview)
1552 // this does not make sense, if there is nothing to paste
1553 if (!CutAndPaste::checkPastePossible(cursor.par()))
1556 setUndo(bview, Undo::INSERT,
1557 cursor.par(), cursor.par()->next());
1560 Paragraph * actpar = cursor.par();
1561 int pos = cursor.pos();
1563 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1564 bview->buffer()->params.textclass);
1566 redoParagraphs(bview, cursor, endpar);
1568 setCursor(bview, cursor.par(), cursor.pos());
1571 selection.cursor = cursor;
1572 setCursor(bview, actpar, pos);
1573 setSelection(bview);
1574 updateCounters(bview);
1578 // sets the selection over the number of characters of string, no check!!
1579 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1584 selection.cursor = cursor;
1585 for (string::size_type i = 0; i < str.length(); ++i)
1587 setSelection(bview);
1591 // simple replacing. The font of the first selected character is used
1592 void LyXText::replaceSelectionWithString(BufferView * bview,
1595 setCursorParUndo(bview);
1598 if (!selection.set()) { // create a dummy selection
1599 selection.end = cursor;
1600 selection.start = cursor;
1603 // Get font setting before we cut
1604 pos_type pos = selection.end.pos();
1605 LyXFont const font = selection.start.par()
1606 ->getFontSettings(bview->buffer()->params,
1607 selection.start.pos());
1609 // Insert the new string
1610 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1611 selection.end.par()->insertChar(pos, (*cit), font);
1615 // Cut the selection
1616 cutSelection(bview, true, false);
1622 // needed to insert the selection
1623 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1625 Paragraph * par = cursor.par();
1626 pos_type pos = cursor.pos();
1627 Paragraph * endpar = cursor.par()->next();
1629 setCursorParUndo(bview);
1631 // only to be sure, should not be neccessary
1634 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1636 redoParagraphs(bview, cursor, endpar);
1637 setCursor(bview, cursor.par(), cursor.pos());
1638 selection.cursor = cursor;
1639 setCursor(bview, par, pos);
1640 setSelection(bview);
1644 // turns double-CR to single CR, others where converted into one
1645 // blank. Then InsertStringAsLines is called
1646 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1648 string linestr(str);
1649 bool newline_inserted = false;
1650 for (string::size_type i = 0; i < linestr.length(); ++i) {
1651 if (linestr[i] == '\n') {
1652 if (newline_inserted) {
1653 // we know that \r will be ignored by
1654 // InsertStringA. Of course, it is a dirty
1655 // trick, but it works...
1656 linestr[i - 1] = '\r';
1660 newline_inserted = true;
1662 } else if (IsPrintable(linestr[i])) {
1663 newline_inserted = false;
1666 insertStringAsLines(bview, linestr);
1670 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1673 LyXCursor tmpcursor;
1677 Row * row = getRow(par, pos, y);
1679 // is there a break one row above
1680 if (row->previous() && row->previous()->par() == row->par()) {
1681 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1682 if (z >= row->pos()) {
1683 // set the dimensions of the row above
1684 y -= row->previous()->height();
1686 refresh_row = row->previous();
1687 status(bview, LyXText::NEED_MORE_REFRESH);
1689 breakAgain(bview, row->previous());
1691 // set the cursor again. Otherwise
1692 // dangling pointers are possible
1693 setCursor(bview, cursor.par(), cursor.pos(),
1694 false, cursor.boundary());
1695 selection.cursor = cursor;
1700 int const tmpheight = row->height();
1701 pos_type const tmplast = rowLast(row);
1705 breakAgain(bview, row);
1706 if (row->height() == tmpheight && rowLast(row) == tmplast)
1707 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1709 status(bview, LyXText::NEED_MORE_REFRESH);
1711 // check the special right address boxes
1712 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1719 redoDrawingOfParagraph(bview, tmpcursor);
1722 // set the cursor again. Otherwise dangling pointers are possible
1723 // also set the selection
1725 if (selection.set()) {
1727 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1728 false, selection.cursor.boundary());
1729 selection.cursor = cursor;
1730 setCursorIntern(bview, selection.start.par(),
1731 selection.start.pos(),
1732 false, selection.start.boundary());
1733 selection.start = cursor;
1734 setCursorIntern(bview, selection.end.par(),
1735 selection.end.pos(),
1736 false, selection.end.boundary());
1737 selection.end = cursor;
1738 setCursorIntern(bview, last_sel_cursor.par(),
1739 last_sel_cursor.pos(),
1740 false, last_sel_cursor.boundary());
1741 last_sel_cursor = cursor;
1744 setCursorIntern(bview, cursor.par(), cursor.pos(),
1745 false, cursor.boundary());
1749 // returns false if inset wasn't found
1750 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1752 // first check the current paragraph
1753 int pos = cursor.par()->getPositionOfInset(inset);
1755 checkParagraph(bview, cursor.par(), pos);
1759 // check every paragraph
1761 Paragraph * par = ownerParagraph();
1763 pos = par->getPositionOfInset(inset);
1765 checkParagraph(bview, par, pos);
1775 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1777 bool setfont, bool boundary) const
1779 LyXCursor old_cursor = cursor;
1780 setCursorIntern(bview, par, pos, setfont, boundary);
1781 return deleteEmptyParagraphMechanism(bview, old_cursor);
1785 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1786 pos_type pos, bool boundary) const
1793 cur.boundary(boundary);
1795 // get the cursor y position in text
1797 Row * row = getRow(par, pos, y);
1798 Row * old_row = row;
1800 // if we are before the first char of this row and are still in the
1801 // same paragraph and there is a previous row then put the cursor on
1802 // the end of the previous row
1803 cur.iy(y + row->baseline());
1805 if (row->previous() && pos &&
1806 row->previous()->par() == row->par() &&
1807 par->getChar(pos) == Paragraph::META_INSET &&
1808 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1810 row = row->previous();
1815 // y is now the beginning of the cursor row
1816 y += row->baseline();
1817 // y is now the cursor baseline
1820 pos_type last = rowLastPrintable(old_row);
1822 if (pos > last + 1) {
1823 // This shouldn't happen.
1826 } else if (pos < row->pos()) {
1831 // now get the cursors x position
1832 float x = getCursorX(bview, row, pos, last, boundary);
1835 if (old_row != row) {
1836 x = getCursorX(bview, old_row, pos, last, boundary);
1843 float LyXText::getCursorX(BufferView * bview, Row * row,
1844 pos_type pos, pos_type last, bool boundary) const
1846 pos_type cursor_vpos = 0;
1848 float fill_separator;
1850 float fill_label_hfill;
1851 // This call HAS to be here because of the BidiTables!!!
1852 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1855 if (last < row->pos())
1856 cursor_vpos = row->pos();
1857 else if (pos > last && !boundary)
1858 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1859 ? row->pos() : last + 1;
1860 else if (pos > row->pos() &&
1861 (pos > last || boundary))
1862 /// Place cursor after char at (logical) position pos - 1
1863 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1864 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1866 /// Place cursor before char at (logical) position pos
1867 cursor_vpos = (bidi_level(pos) % 2 == 0)
1868 ? log2vis(pos) : log2vis(pos) + 1;
1870 pos_type main_body =
1871 beginningOfMainBody(bview->buffer(), row->par());
1872 if ((main_body > 0) &&
1873 ((main_body-1 > last) ||
1874 !row->par()->isLineSeparator(main_body-1)))
1877 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1878 pos_type pos = vis2log(vpos);
1879 if (main_body > 0 && pos == main_body - 1) {
1880 x += fill_label_hfill +
1881 font_metrics::width(
1882 row->par()->layout()->labelsep,
1883 getLabelFont(bview->buffer(),
1885 if (row->par()->isLineSeparator(main_body - 1))
1886 x -= singleWidth(bview,
1887 row->par(), main_body - 1);
1889 if (hfillExpansion(bview->buffer(), row, pos)) {
1890 x += singleWidth(bview, row->par(), pos);
1891 if (pos >= main_body)
1894 x += fill_label_hfill;
1895 } else if (row->par()->isSeparator(pos)) {
1896 x += singleWidth(bview, row->par(), pos);
1897 if (pos >= main_body)
1898 x += fill_separator;
1900 x += singleWidth(bview, row->par(), pos);
1906 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1907 pos_type pos, bool setfont, bool boundary) const
1909 InsetText * it = static_cast<InsetText *>(par->inInset());
1911 if (it != inset_owner) {
1912 lyxerr[Debug::INSETS] << "InsetText is " << it
1914 << "inset_owner is "
1915 << inset_owner << endl;
1916 #ifdef WITH_WARNINGS
1917 #warning I believe this code is wrong. (Lgb)
1918 #warning Jürgen, have a look at this. (Lgb)
1919 #warning Hmmm, I guess you are right but we
1920 #warning should verify when this is needed
1922 // Jürgen, would you like to have a look?
1923 // I guess we need to move the outer cursor
1924 // and open and lock the inset (bla bla bla)
1925 // stuff I don't know... so can you have a look?
1927 // I moved the lyxerr stuff in here so we can see if
1928 // this is actually really needed and where!
1930 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1935 setCursor(bview, cursor, par, pos, boundary);
1937 setCurrentFont(bview);
1941 void LyXText::setCurrentFont(BufferView * bview) const
1943 pos_type pos = cursor.pos();
1944 if (cursor.boundary() && pos > 0)
1948 if (pos == cursor.par()->size())
1950 else // potentional bug... BUG (Lgb)
1951 if (cursor.par()->isSeparator(pos)) {
1952 if (pos > cursor.row()->pos() &&
1953 bidi_level(pos) % 2 ==
1954 bidi_level(pos - 1) % 2)
1956 else if (pos + 1 < cursor.par()->size())
1962 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1963 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1965 if (cursor.pos() == cursor.par()->size() &&
1966 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1967 !cursor.boundary()) {
1968 Language const * lang =
1969 cursor.par()->getParLanguage(bview->buffer()->params);
1970 current_font.setLanguage(lang);
1971 current_font.setNumber(LyXFont::OFF);
1972 real_current_font.setLanguage(lang);
1973 real_current_font.setNumber(LyXFont::OFF);
1978 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1980 LyXCursor old_cursor = cursor;
1982 setCursorFromCoordinates(bview, cursor, x, y);
1983 setCurrentFont(bview);
1984 deleteEmptyParagraphMechanism(bview, old_cursor);
1991 * return true if the cursor given is at the end of a row,
1992 * and the next row is filled by an inset that spans an entire
1995 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1998 Row const & next = *row.next();
2000 if (next.pos() != cur.pos() || next.par() != cur.par())
2002 if (!cur.par()->isInset(cur.pos()))
2004 Inset const * inset = cur.par()->getInset(cur.pos());
2005 if (inset->needFullRow() || inset->display())
2012 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2015 // Get the row first.
2017 Row * row = getRowNearY(y);
2019 pos_type const column = getColumnNearX(bview, row, x, bound);
2020 cur.par(row->par());
2021 cur.pos(row->pos() + column);
2023 cur.y(y + row->baseline());
2026 if (beforeFullRowInset(*row, cur)) {
2027 pos_type last = rowLastPrintable(row);
2028 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2030 cur.iy(y + row->height() + row->next()->baseline());
2031 cur.irow(row->next());
2037 cur.boundary(bound);
2041 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2043 if (cursor.pos() > 0) {
2044 bool boundary = cursor.boundary();
2045 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2046 if (!internal && !boundary &&
2047 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2048 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2049 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2050 Paragraph * par = cursor.par()->previous();
2051 setCursor(bview, par, par->size());
2056 void LyXText::cursorRight(BufferView * bview, bool internal) const
2058 if (!internal && cursor.boundary() &&
2059 !cursor.par()->isNewline(cursor.pos()))
2060 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2061 else if (cursor.pos() < cursor.par()->size()) {
2062 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2064 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2065 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2066 } else if (cursor.par()->next())
2067 setCursor(bview, cursor.par()->next(), 0);
2071 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2074 int x = cursor.x_fix();
2075 int y = cursor.y() - cursor.row()->baseline() - 1;
2076 setCursorFromCoordinates(bview, x, y);
2078 int y1 = cursor.iy() - first_y;
2082 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2083 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2084 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2088 setCursorFromCoordinates(bview, cursor.x_fix(),
2089 cursor.y() - cursor.row()->baseline() - 1);
2094 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2097 int x = cursor.x_fix();
2098 int y = cursor.y() - cursor.row()->baseline() +
2099 cursor.row()->height() + 1;
2100 setCursorFromCoordinates(bview, x, y);
2101 if (!selecting && cursor.row() == cursor.irow()) {
2102 int y1 = cursor.iy() - first_y;
2106 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2107 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2108 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2112 setCursorFromCoordinates(bview, cursor.x_fix(),
2113 cursor.y() - cursor.row()->baseline()
2114 + cursor.row()->height() + 1);
2119 void LyXText::cursorUpParagraph(BufferView * bview) const
2121 if (cursor.pos() > 0) {
2122 setCursor(bview, cursor.par(), 0);
2124 else if (cursor.par()->previous()) {
2125 setCursor(bview, cursor.par()->previous(), 0);
2130 void LyXText::cursorDownParagraph(BufferView * bview) const
2132 if (cursor.par()->next()) {
2133 setCursor(bview, cursor.par()->next(), 0);
2135 setCursor(bview, cursor.par(), cursor.par()->size());
2139 // fix the cursor `cur' after a characters has been deleted at `where'
2140 // position. Called by deleteEmptyParagraphMechanism
2141 void LyXText::fixCursorAfterDelete(BufferView * bview,
2143 LyXCursor const & where) const
2145 // if cursor is not in the paragraph where the delete occured,
2147 if (cur.par() != where.par())
2150 // if cursor position is after the place where the delete occured,
2152 if (cur.pos() > where.pos())
2153 cur.pos(cur.pos()-1);
2155 // check also if we don't want to set the cursor on a spot behind the
2156 // pagragraph because we erased the last character.
2157 if (cur.pos() > cur.par()->size())
2158 cur.pos(cur.par()->size());
2160 // recompute row et al. for this cursor
2161 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2165 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2166 LyXCursor const & old_cursor) const
2168 // Would be wrong to delete anything if we have a selection.
2169 if (selection.set())
2172 // We allow all kinds of "mumbo-jumbo" when freespacing.
2173 if (old_cursor.par()->layout()->free_spacing
2174 || old_cursor.par()->isFreeSpacing()) {
2178 /* Ok I'll put some comments here about what is missing.
2179 I have fixed BackSpace (and thus Delete) to not delete
2180 double-spaces automagically. I have also changed Cut,
2181 Copy and Paste to hopefully do some sensible things.
2182 There are still some small problems that can lead to
2183 double spaces stored in the document file or space at
2184 the beginning of paragraphs. This happens if you have
2185 the cursor betwenn to spaces and then save. Or if you
2186 cut and paste and the selection have a space at the
2187 beginning and then save right after the paste. I am
2188 sure none of these are very hard to fix, but I will
2189 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2190 that I can get some feedback. (Lgb)
2193 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2194 // delete the LineSeparator.
2197 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2198 // delete the LineSeparator.
2201 // If the pos around the old_cursor were spaces, delete one of them.
2202 if (old_cursor.par() != cursor.par()
2203 || old_cursor.pos() != cursor.pos()) {
2204 // Only if the cursor has really moved
2206 if (old_cursor.pos() > 0
2207 && old_cursor.pos() < old_cursor.par()->size()
2208 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2209 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2210 old_cursor.par()->erase(old_cursor.pos() - 1);
2211 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2213 #ifdef WITH_WARNINGS
2214 #warning This will not work anymore when we have multiple views of the same buffer
2215 // In this case, we will have to correct also the cursors held by
2216 // other bufferviews. It will probably be easier to do that in a more
2217 // automated way in LyXCursor code. (JMarc 26/09/2001)
2219 // correct all cursors held by the LyXText
2220 fixCursorAfterDelete(bview, cursor, old_cursor);
2221 fixCursorAfterDelete(bview, selection.cursor,
2223 fixCursorAfterDelete(bview, selection.start,
2225 fixCursorAfterDelete(bview, selection.end, old_cursor);
2226 fixCursorAfterDelete(bview, last_sel_cursor,
2228 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2229 fixCursorAfterDelete(bview, toggle_end_cursor,
2235 // don't delete anything if this is the ONLY paragraph!
2236 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2239 // Do not delete empty paragraphs with keepempty set.
2240 if (old_cursor.par()->layout()->keepempty)
2243 // only do our magic if we changed paragraph
2244 if (old_cursor.par() == cursor.par())
2247 // record if we have deleted a paragraph
2248 // we can't possibly have deleted a paragraph before this point
2249 bool deleted = false;
2251 if ((old_cursor.par()->empty()
2252 || (old_cursor.par()->size() == 1
2253 && old_cursor.par()->isLineSeparator(0)))) {
2254 // ok, we will delete anything
2255 LyXCursor tmpcursor;
2257 // make sure that you do not delete any environments
2258 status(bview, LyXText::NEED_MORE_REFRESH);
2261 if (old_cursor.row()->previous()) {
2262 refresh_row = old_cursor.row()->previous();
2263 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2265 cursor = old_cursor; // that undo can restore the right cursor position
2266 Paragraph * endpar = old_cursor.par()->next();
2267 if (endpar && endpar->getDepth()) {
2268 while (endpar && endpar->getDepth()) {
2269 endpar = endpar->next();
2272 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2276 removeRow(old_cursor.row());
2277 if (ownerParagraph() == old_cursor.par()) {
2278 ownerParagraph(ownerParagraph()->next());
2281 delete old_cursor.par();
2283 /* Breakagain the next par. Needed because of
2284 * the parindent that can occur or dissappear.
2285 * The next row can change its height, if
2286 * there is another layout before */
2287 if (refresh_row->next()) {
2288 breakAgain(bview, refresh_row->next());
2289 updateCounters(bview);
2291 setHeightOfRow(bview, refresh_row);
2293 refresh_row = old_cursor.row()->next();
2294 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2297 cursor = old_cursor; // that undo can restore the right cursor position
2298 Paragraph * endpar = old_cursor.par()->next();
2299 if (endpar && endpar->getDepth()) {
2300 while (endpar && endpar->getDepth()) {
2301 endpar = endpar->next();
2304 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2308 removeRow(old_cursor.row());
2310 if (ownerParagraph() == old_cursor.par()) {
2311 ownerParagraph(ownerParagraph()->next());
2314 delete old_cursor.par();
2316 /* Breakagain the next par. Needed because of
2317 the parindent that can occur or dissappear.
2318 The next row can change its height, if
2319 there is another layout before */
2321 breakAgain(bview, refresh_row);
2322 updateCounters(bview);
2327 setCursorIntern(bview, cursor.par(), cursor.pos());
2329 if (selection.cursor.par() == old_cursor.par()
2330 && selection.cursor.pos() == old_cursor.pos()) {
2331 // correct selection
2332 selection.cursor = cursor;
2336 if (old_cursor.par()->stripLeadingSpaces()) {
2337 redoParagraphs(bview, old_cursor,
2338 old_cursor.par()->next());
2340 setCursorIntern(bview, cursor.par(), cursor.pos());
2341 selection.cursor = cursor;
2348 Paragraph * LyXText::ownerParagraph() const
2351 return inset_owner->paragraph();
2353 return &*(bv_owner->buffer()->paragraphs.begin());
2357 void LyXText::ownerParagraph(Paragraph * p) const
2360 inset_owner->paragraph(p);
2362 bv_owner->buffer()->paragraphs.set(p);
2367 void LyXText::ownerParagraph(int id, Paragraph * p) const
2369 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2370 if (op && op->inInset()) {
2371 static_cast<InsetText *>(op->inInset())->paragraph(p);
2378 LyXText::text_status LyXText::status() const
2384 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2386 LyXText * t = bview->text;
2388 // We should only go up with refreshing code so this means that if
2389 // we have a MORE refresh we should never set it to LITTLE if we still
2390 // didn't handle it (and then it will be UNCHANGED. Now as long as
2391 // we stay inside one LyXText this may work but we need to tell the
2392 // outermost LyXText that it should REALLY draw us if there is some
2393 // change in a Inset::LyXText. So you see that when we are inside a
2394 // inset's LyXText we give the LITTLE to the outermost LyXText to
2395 // tell'em that it should redraw the actual row (where the inset
2396 // resides! Capito?!
2398 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2400 if (inset_owner && st != UNCHANGED) {
2401 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2402 if (!t->refresh_row) {
2403 t->refresh_row = t->cursor.row();
2404 t->refresh_y = t->cursor.y() -
2405 t->cursor.row()->baseline();