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 Paragraph::depth_type par_depth = par->getDepth();
167 // We specialize the 95% common case:
169 if (layout->labeltype == LABEL_MANUAL
170 && pos < beginningOfMainBody(buf, par)) {
172 LyXFont f = par->getFontSettings(buf->params, pos);
174 par->inInset()->getDrawFont(f);
175 #ifndef INHERIT_LANGUAGE
176 return f.realize(layout->reslabelfont);
178 return f.realize(layout.reslabelfont, buf->params.language);
181 LyXFont f = par->getFontSettings(buf->params, pos);
183 par->inInset()->getDrawFont(f);
184 #ifndef INHERIT_LANGUAGE
185 return f.realize(layout->resfont);
187 return f.realize(layout.resfont, buf->params.language);
192 // The uncommon case need not be optimized as much
196 if (pos < beginningOfMainBody(buf, par)) {
198 layoutfont = layout->labelfont;
201 layoutfont = layout->font;
204 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
205 #ifndef INHERIT_LANGUAGE
206 tmpfont.realize(layoutfont);
208 tmpfont.realize(layoutfont, buf->params.language);
211 par->inInset()->getDrawFont(tmpfont);
213 return realizeFont(tmpfont, buf, par);
217 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
219 LyXLayout_ptr const & layout = par->layout();
221 Paragraph::depth_type par_depth = par->getDepth();
224 return layout->resfont;
227 return realizeFont(layout->font, buf, par);
231 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
233 LyXLayout_ptr const & layout = par->layout();
235 Paragraph::depth_type par_depth = par->getDepth();
238 return layout->reslabelfont;
241 return realizeFont(layout->labelfont, buf, par);
245 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
246 pos_type pos, LyXFont const & fnt,
249 Buffer const * buf = bv->buffer();
250 LyXFont font = getFont(buf, par, pos);
251 font.update(fnt, buf->params.language, toggleall);
252 // Let the insets convert their font
253 if (par->isInset(pos)) {
254 Inset * inset = par->getInset(pos);
255 if (isEditableInset(inset)) {
256 UpdatableInset * uinset =
257 static_cast<UpdatableInset *>(inset);
258 uinset->setFont(bv, fnt, toggleall, true);
262 // Plug thru to version below:
263 setCharFont(buf, par, pos, font);
267 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
268 pos_type pos, LyXFont const & fnt)
272 LyXTextClass const & tclass = buf->params.getLyXTextClass();
273 LyXLayout_ptr const & layout = par->layout();
275 // Get concrete layout font to reduce against
278 if (pos < beginningOfMainBody(buf, par))
279 layoutfont = layout->labelfont;
281 layoutfont = layout->font;
283 // Realize against environment font information
284 if (par->getDepth()) {
285 Paragraph * tp = par;
286 while (!layoutfont.resolved() && tp && tp->getDepth()) {
287 tp = tp->outerHook();
289 #ifndef INHERIT_LANGUAGE
290 layoutfont.realize(tp->layout()->font);
292 layoutfont.realize(tclass[tp->layout()].font,
293 buf->params.language);
298 #ifndef INHERIT_LANGUAGE
299 layoutfont.realize(tclass.defaultfont());
301 layoutfont.realize(tclass.defaultfont(), buf->params.language);
304 // Now, reduce font against full layout font
305 font.reduce(layoutfont);
307 par->setFont(pos, font);
311 // inserts a new row behind the specified row, increments
312 // the touched counters
313 void LyXText::insertRow(Row * row, Paragraph * par,
316 Row * tmprow = new Row;
319 tmprow->next(firstrow);
322 tmprow->previous(row);
323 tmprow->next(row->next());
328 tmprow->next()->previous(tmprow);
330 if (tmprow->previous())
331 tmprow->previous()->next(tmprow);
343 // removes the row and reset the touched counters
344 void LyXText::removeRow(Row * row) const
346 Row * row_prev = row->previous();
348 row->next()->previous(row_prev);
350 firstrow = row->next();
351 // lyx::Assert(firstrow);
353 row_prev->next(row->next());
355 if (row == lastrow) {
356 lyx::Assert(!row->next());
359 if (refresh_row == row) {
360 refresh_row = row_prev ? row_prev : row->next();
361 // what about refresh_y, refresh_height
364 height -= row->height(); // the text becomes smaller
367 --number_of_rows; // one row less
371 // remove all following rows of the paragraph of the specified row.
372 void LyXText::removeParagraph(Row * row) const
374 Paragraph * tmppar = row->par();
378 while (row && row->par() == tmppar) {
379 tmprow = row->next();
386 // insert the specified paragraph behind the specified row
387 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
390 insertRow(row, par, 0); /* insert a new row, starting
393 setCounter(bview->buffer(), par); // set the counters
395 // and now append the whole paragraph behind the new row
398 appendParagraph(bview, firstrow);
400 row->next()->height(0);
401 appendParagraph(bview, row->next());
406 Inset * LyXText::getInset() const
409 if (cursor.pos() == 0 && cursor.par()->bibkey) {
410 inset = cursor.par()->bibkey;
411 } else if (cursor.pos() < cursor.par()->size()
412 && cursor.par()->isInset(cursor.pos())) {
413 inset = cursor.par()->getInset(cursor.pos());
419 void LyXText::toggleInset(BufferView * bview)
421 Inset * inset = getInset();
422 // is there an editable inset at cursor position?
423 if (!isEditableInset(inset)) {
424 // No, try to see if we are inside a collapsable inset
425 if (inset_owner && inset_owner->owner()
426 && inset_owner->owner()->isOpen()) {
427 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
428 inset_owner->owner()->close(bview);
429 bview->getLyXText()->cursorRight(bview);
433 //bview->owner()->message(inset->editMessage());
435 // do we want to keep this?? (JMarc)
436 if (!isHighlyEditableInset(inset))
437 setCursorParUndo(bview);
439 if (inset->isOpen()) {
445 inset->open(bview, !inset->isOpen());
450 /* used in setlayout */
451 // Asger is not sure we want to do this...
452 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
455 LyXLayout_ptr const & layout = par->layout();
458 for (pos_type pos = 0; pos < par->size(); ++pos) {
459 if (pos < beginningOfMainBody(buf, par))
460 layoutfont = layout->labelfont;
462 layoutfont = layout->font;
464 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
465 tmpfont.reduce(layoutfont);
466 par->setFont(pos, tmpfont);
471 Paragraph * LyXText::setLayout(BufferView * bview,
472 LyXCursor & cur, LyXCursor & sstart_cur,
473 LyXCursor & send_cur,
474 string const & layout)
476 Paragraph * endpar = send_cur.par()->next();
477 Paragraph * undoendpar = endpar;
479 if (endpar && endpar->getDepth()) {
480 while (endpar && endpar->getDepth()) {
481 endpar = endpar->next();
485 endpar = endpar->next(); // because of parindents etc.
488 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
490 // ok we have a selection. This is always between sstart_cur
491 // and sel_end cursor
493 Paragraph * par = sstart_cur.par();
494 Paragraph * epar = send_cur.par()->next();
496 LyXLayout_ptr const & lyxlayout =
497 bview->buffer()->params.getLyXTextClass()[layout];
500 par->applyLayout(lyxlayout);
501 makeFontEntriesLayoutSpecific(bview->buffer(), par);
502 Paragraph * fppar = par;
503 fppar->params().spaceTop(lyxlayout->fill_top ?
504 VSpace(VSpace::VFILL)
505 : VSpace(VSpace::NONE));
506 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
507 VSpace(VSpace::VFILL)
508 : VSpace(VSpace::NONE));
509 if (lyxlayout->margintype == MARGIN_MANUAL)
510 par->setLabelWidthString(lyxlayout->labelstring());
511 if (lyxlayout->labeltype != LABEL_BIBLIO
513 delete fppar->bibkey;
518 } while (par != epar);
524 // set layout over selection and make a total rebreak of those paragraphs
525 void LyXText::setLayout(BufferView * bview, string const & layout)
527 LyXCursor tmpcursor = cursor; /* store the current cursor */
529 // if there is no selection just set the layout
530 // of the current paragraph */
531 if (!selection.set()) {
532 selection.start = cursor; // dummy selection
533 selection.end = cursor;
535 Paragraph * endpar = setLayout(bview, cursor, selection.start,
536 selection.end, layout);
537 redoParagraphs(bview, selection.start, endpar);
539 // we have to reset the selection, because the
540 // geometry could have changed
541 setCursor(bview, selection.start.par(),
542 selection.start.pos(), false);
543 selection.cursor = cursor;
544 setCursor(bview, selection.end.par(), selection.end.pos(), false);
545 updateCounters(bview);
548 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
552 // increment depth over selection and
553 // make a total rebreak of those paragraphs
554 void LyXText::incDepth(BufferView * bview)
556 // If there is no selection, just use the current paragraph
557 if (!selection.set()) {
558 selection.start = cursor; // dummy selection
559 selection.end = cursor;
562 // We end at the next paragraph with depth 0
563 Paragraph * endpar = selection.end.par()->next();
565 Paragraph * undoendpar = endpar;
567 if (endpar && endpar->getDepth()) {
568 while (endpar && endpar->getDepth()) {
569 endpar = endpar->next();
573 endpar = endpar->next(); // because of parindents etc.
576 setUndo(bview, Undo::EDIT,
577 selection.start.par(), undoendpar);
579 LyXCursor tmpcursor = cursor; // store the current cursor
581 // ok we have a selection. This is always between sel_start_cursor
582 // and sel_end cursor
583 cursor = selection.start;
585 bool anything_changed = false;
588 // NOTE: you can't change the depth of a bibliography entry
589 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
590 Paragraph * prev = cursor.par()->previous();
593 if (cursor.par()->getDepth()
594 < prev->getMaxDepthAfter()) {
595 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
596 anything_changed = true;
600 if (cursor.par() == selection.end.par())
602 cursor.par(cursor.par()->next());
605 // if nothing changed set all depth to 0
606 if (!anything_changed) {
607 cursor = selection.start;
608 while (cursor.par() != selection.end.par()) {
609 cursor.par()->params().depth(0);
610 cursor.par(cursor.par()->next());
612 cursor.par()->params().depth(0);
615 redoParagraphs(bview, selection.start, endpar);
617 // we have to reset the selection, because the
618 // geometry could have changed
619 setCursor(bview, selection.start.par(), selection.start.pos());
620 selection.cursor = cursor;
621 setCursor(bview, selection.end.par(), selection.end.pos());
622 updateCounters(bview);
625 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
629 // decrement depth over selection and
630 // make a total rebreak of those paragraphs
631 void LyXText::decDepth(BufferView * bview)
633 // if there is no selection just set the layout
634 // of the current paragraph
635 if (!selection.set()) {
636 selection.start = cursor; // dummy selection
637 selection.end = cursor;
639 Paragraph * endpar = selection.end.par()->next();
640 Paragraph * undoendpar = endpar;
642 if (endpar && endpar->getDepth()) {
643 while (endpar && endpar->getDepth()) {
644 endpar = endpar->next();
648 endpar = endpar->next(); // because of parindents etc.
651 setUndo(bview, Undo::EDIT,
652 selection.start.par(), undoendpar);
654 LyXCursor tmpcursor = cursor; // store the current cursor
656 // ok we have a selection. This is always between sel_start_cursor
657 // and sel_end cursor
658 cursor = selection.start;
661 if (cursor.par()->params().depth()) {
662 cursor.par()->params()
663 .depth(cursor.par()->params().depth() - 1);
665 if (cursor.par() == selection.end.par()) {
668 cursor.par(cursor.par()->next());
671 redoParagraphs(bview, selection.start, endpar);
673 // we have to reset the selection, because the
674 // geometry could have changed
675 setCursor(bview, selection.start.par(),
676 selection.start.pos());
677 selection.cursor = cursor;
678 setCursor(bview, selection.end.par(), selection.end.pos());
679 updateCounters(bview);
682 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
686 // set font over selection and make a total rebreak of those paragraphs
687 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
689 // if there is no selection just set the current_font
690 if (!selection.set()) {
691 // Determine basis font
693 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
695 layoutfont = getLabelFont(bview->buffer(),
698 layoutfont = getLayoutFont(bview->buffer(),
701 // Update current font
702 real_current_font.update(font,
703 bview->buffer()->params.language,
706 // Reduce to implicit settings
707 current_font = real_current_font;
708 current_font.reduce(layoutfont);
709 // And resolve it completely
710 #ifndef INHERIT_LANGUAGE
711 real_current_font.realize(layoutfont);
713 real_current_font.realize(layoutfont,
714 bview->buffer()->params.language);
719 LyXCursor tmpcursor = cursor; // store the current cursor
721 // ok we have a selection. This is always between sel_start_cursor
722 // and sel_end cursor
724 setUndo(bview, Undo::EDIT,
725 selection.start.par(), selection.end.par()->next());
727 cursor = selection.start;
728 while (cursor.par() != selection.end.par() ||
729 cursor.pos() < selection.end.pos())
731 if (cursor.pos() < cursor.par()->size()) {
732 // an open footnote should behave like a closed one
733 setCharFont(bview, cursor.par(), cursor.pos(),
735 cursor.pos(cursor.pos() + 1);
738 cursor.par(cursor.par()->next());
743 redoParagraphs(bview, selection.start, selection.end.par()->next());
745 // we have to reset the selection, because the
746 // geometry could have changed, but we keep
747 // it for user convenience
748 setCursor(bview, selection.start.par(), selection.start.pos());
749 selection.cursor = cursor;
750 setCursor(bview, selection.end.par(), selection.end.pos());
752 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
753 tmpcursor.boundary());
757 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
759 Row * tmprow = cur.row();
760 int y = cur.y() - tmprow->baseline();
762 setHeightOfRow(bview, tmprow);
764 while (tmprow->previous()
765 && tmprow->previous()->par() == tmprow->par()) {
766 tmprow = tmprow->previous();
767 y -= tmprow->height();
768 setHeightOfRow(bview, tmprow);
771 // we can set the refreshing parameters now
772 status(bview, LyXText::NEED_MORE_REFRESH);
774 refresh_row = tmprow;
775 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
779 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
781 Row * tmprow = cur.row();
783 int y = cur.y() - tmprow->baseline();
784 setHeightOfRow(bview, tmprow);
786 while (tmprow->previous()
787 && tmprow->previous()->par() == tmprow->par()) {
788 tmprow = tmprow->previous();
789 y -= tmprow->height();
792 // we can set the refreshing parameters now
793 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
795 refresh_row = tmprow;
797 status(bview, LyXText::NEED_MORE_REFRESH);
798 setCursor(bview, cur.par(), cur.pos());
802 // deletes and inserts again all paragaphs between the cursor
803 // and the specified par
804 // This function is needed after SetLayout and SetFont etc.
805 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
806 Paragraph const * endpar) const
809 Paragraph * tmppar = 0;
810 Paragraph * first_phys_par = 0;
812 Row * tmprow = cur.row();
814 int y = cur.y() - tmprow->baseline();
816 if (!tmprow->previous()) {
817 // a trick/hack for UNDO
818 // This is needed because in an UNDO/REDO we could have changed
819 // the ownerParagrah() so the paragraph inside the row is NOT
820 // my really first par anymore. Got it Lars ;) (Jug 20011206)
821 first_phys_par = ownerParagraph();
823 first_phys_par = tmprow->par();
824 while (tmprow->previous()
825 && tmprow->previous()->par() == first_phys_par)
827 tmprow = tmprow->previous();
828 y -= tmprow->height();
832 // we can set the refreshing parameters now
833 status(bview, LyXText::NEED_MORE_REFRESH);
835 refresh_row = tmprow->previous(); /* the real refresh row will
836 be deleted, so I store
840 tmppar = tmprow->next()->par();
843 while (tmprow->next() && tmppar != endpar) {
844 removeRow(tmprow->next());
845 if (tmprow->next()) {
846 tmppar = tmprow->next()->par();
852 // remove the first one
853 tmprow2 = tmprow; /* this is because tmprow->previous()
855 tmprow = tmprow->previous();
858 tmppar = first_phys_par;
862 insertParagraph(bview, tmppar, tmprow);
866 while (tmprow->next()
867 && tmprow->next()->par() == tmppar) {
868 tmprow = tmprow->next();
870 tmppar = tmppar->next();
872 } while (tmppar && tmppar != endpar);
874 // this is because of layout changes
876 refresh_y -= refresh_row->height();
877 setHeightOfRow(bview, refresh_row);
879 refresh_row = firstrow;
881 setHeightOfRow(bview, refresh_row);
884 if (tmprow && tmprow->next())
885 setHeightOfRow(bview, tmprow->next());
886 updateCounters(bview);
890 void LyXText::fullRebreak(BufferView * bview)
896 if (need_break_row) {
897 breakAgain(bview, need_break_row);
904 // important for the screen
907 // the cursor set functions have a special mechanism. When they
908 // realize, that you left an empty paragraph, they will delete it.
909 // They also delete the corresponding row
911 // need the selection cursor:
912 void LyXText::setSelection(BufferView * bview)
914 bool const lsel = selection.set();
916 if (!selection.set()) {
917 last_sel_cursor = selection.cursor;
918 selection.start = selection.cursor;
919 selection.end = selection.cursor;
924 // first the toggling area
925 if (cursor.y() < last_sel_cursor.y()
926 || (cursor.y() == last_sel_cursor.y()
927 && cursor.x() < last_sel_cursor.x())) {
928 toggle_end_cursor = last_sel_cursor;
929 toggle_cursor = cursor;
931 toggle_end_cursor = cursor;
932 toggle_cursor = last_sel_cursor;
935 last_sel_cursor = cursor;
937 // and now the whole selection
939 if (selection.cursor.par() == cursor.par())
940 if (selection.cursor.pos() < cursor.pos()) {
941 selection.end = cursor;
942 selection.start = selection.cursor;
944 selection.end = selection.cursor;
945 selection.start = cursor;
947 else if (selection.cursor.y() < cursor.y() ||
948 (selection.cursor.y() == cursor.y()
949 && selection.cursor.x() < cursor.x())) {
950 selection.end = cursor;
951 selection.start = selection.cursor;
954 selection.end = selection.cursor;
955 selection.start = cursor;
958 // a selection with no contents is not a selection
959 if (selection.start.par() == selection.end.par() &&
960 selection.start.pos() == selection.end.pos())
961 selection.set(false);
963 if (inset_owner && (selection.set() || lsel))
964 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
968 string const LyXText::selectionAsString(Buffer const * buffer,
971 if (!selection.set()) return string();
973 // should be const ...
974 Paragraph * startpar(selection.start.par());
975 Paragraph * endpar(selection.end.par());
976 pos_type const startpos(selection.start.pos());
977 pos_type const endpos(selection.end.pos());
979 if (startpar == endpar) {
980 return startpar->asString(buffer, startpos, endpos, label);
985 // First paragraph in selection
986 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
988 // The paragraphs in between (if any)
989 LyXCursor tmpcur(selection.start);
990 tmpcur.par(tmpcur.par()->next());
991 while (tmpcur.par() != endpar) {
992 result += tmpcur.par()->asString(buffer, 0,
993 tmpcur.par()->size(),
995 tmpcur.par(tmpcur.par()->next());
998 // Last paragraph in selection
999 result += endpar->asString(buffer, 0, endpos, label);
1005 void LyXText::clearSelection() const
1007 selection.set(false);
1008 selection.mark(false);
1009 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1010 // reset this in the bv_owner!
1011 if (bv_owner && bv_owner->text)
1012 bv_owner->text->xsel_cache.set(false);
1016 void LyXText::cursorHome(BufferView * bview) const
1018 setCursor(bview, cursor.par(), cursor.row()->pos());
1022 void LyXText::cursorEnd(BufferView * bview) const
1024 if (!cursor.row()->next()
1025 || cursor.row()->next()->par() != cursor.row()->par()) {
1026 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1028 if (!cursor.par()->empty() &&
1029 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1030 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1031 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1033 setCursor(bview,cursor.par(),
1034 rowLast(cursor.row()) + 1);
1040 void LyXText::cursorTop(BufferView * bview) const
1042 while (cursor.par()->previous())
1043 cursor.par(cursor.par()->previous());
1044 setCursor(bview, cursor.par(), 0);
1048 void LyXText::cursorBottom(BufferView * bview) const
1050 while (cursor.par()->next())
1051 cursor.par(cursor.par()->next());
1052 setCursor(bview, cursor.par(), cursor.par()->size());
1056 void LyXText::toggleFree(BufferView * bview,
1057 LyXFont const & font, bool toggleall)
1059 // If the mask is completely neutral, tell user
1060 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1061 // Could only happen with user style
1062 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1066 // Try implicit word selection
1067 // If there is a change in the language the implicit word selection
1069 LyXCursor resetCursor = cursor;
1070 bool implicitSelection = (font.language() == ignore_language
1071 && font.number() == LyXFont::IGNORE)
1072 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1075 setFont(bview, font, toggleall);
1077 // Implicit selections are cleared afterwards
1078 //and cursor is set to the original position.
1079 if (implicitSelection) {
1081 cursor = resetCursor;
1082 setCursor(bview, cursor.par(), cursor.pos());
1083 selection.cursor = cursor;
1086 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1090 string LyXText::getStringToIndex(BufferView * bview)
1094 // Try implicit word selection
1095 // If there is a change in the language the implicit word selection
1097 LyXCursor const reset_cursor = cursor;
1098 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1100 if (!selection.set()) {
1101 bview->owner()->message(_("Nothing to index!"));
1104 if (selection.start.par() != selection.end.par()) {
1105 bview->owner()->message(_("Cannot index more than one paragraph!"));
1109 idxstring = selectionAsString(bview->buffer(), false);
1111 // Implicit selections are cleared afterwards
1112 //and cursor is set to the original position.
1113 if (implicitSelection) {
1115 cursor = reset_cursor;
1116 setCursor(bview, cursor.par(), cursor.pos());
1117 selection.cursor = cursor;
1123 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1124 Paragraph const * par) const
1126 if (par->layout()->labeltype != LABEL_MANUAL)
1129 return par->beginningOfMainBody();
1133 // the DTP switches for paragraphs. LyX will store them in the first
1134 // physicla paragraph. When a paragraph is broken, the top settings rest,
1135 // the bottom settings are given to the new one. So I can make shure,
1136 // they do not duplicate themself and you cannnot make dirty things with
1139 void LyXText::setParagraph(BufferView * bview,
1140 bool line_top, bool line_bottom,
1141 bool pagebreak_top, bool pagebreak_bottom,
1142 VSpace const & space_top,
1143 VSpace const & space_bottom,
1144 Spacing const & spacing,
1146 string labelwidthstring,
1149 LyXCursor tmpcursor = cursor;
1150 if (!selection.set()) {
1151 selection.start = cursor;
1152 selection.end = cursor;
1155 // make sure that the depth behind the selection are restored, too
1156 Paragraph * endpar = selection.end.par()->next();
1157 Paragraph * undoendpar = endpar;
1159 if (endpar && endpar->getDepth()) {
1160 while (endpar && endpar->getDepth()) {
1161 endpar = endpar->next();
1162 undoendpar = endpar;
1166 // because of parindents etc.
1167 endpar = endpar->next();
1170 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1173 Paragraph * tmppar = selection.end.par();
1175 while (tmppar != selection.start.par()->previous()) {
1176 setCursor(bview, tmppar, 0);
1177 status(bview, LyXText::NEED_MORE_REFRESH);
1178 refresh_row = cursor.row();
1179 refresh_y = cursor.y() - cursor.row()->baseline();
1180 cursor.par()->params().lineTop(line_top);
1181 cursor.par()->params().lineBottom(line_bottom);
1182 cursor.par()->params().pagebreakTop(pagebreak_top);
1183 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1184 cursor.par()->params().spaceTop(space_top);
1185 cursor.par()->params().spaceBottom(space_bottom);
1186 cursor.par()->params().spacing(spacing);
1187 // does the layout allow the new alignment?
1188 LyXLayout_ptr const & layout = cursor.par()->layout();
1190 if (align == LYX_ALIGN_LAYOUT)
1191 align = layout->align;
1192 if (align & layout->alignpossible) {
1193 if (align == layout->align)
1194 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1196 cursor.par()->params().align(align);
1198 cursor.par()->setLabelWidthString(labelwidthstring);
1199 cursor.par()->params().noindent(noindent);
1200 tmppar = cursor.par()->previous();
1203 redoParagraphs(bview, selection.start, endpar);
1206 setCursor(bview, selection.start.par(), selection.start.pos());
1207 selection.cursor = cursor;
1208 setCursor(bview, selection.end.par(), selection.end.pos());
1209 setSelection(bview);
1210 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1212 bview->updateInset(inset_owner, true);
1216 // set the counter of a paragraph. This includes the labels
1217 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1219 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1220 LyXLayout_ptr const & layout = par->layout();
1222 if (par->previous()) {
1224 par->params().appendix(par->previous()->params().appendix());
1225 if (!par->params().appendix() && par->params().startOfAppendix()) {
1226 par->params().appendix(true);
1227 buf->counters().reset();
1229 par->enumdepth = par->previous()->enumdepth;
1230 par->itemdepth = par->previous()->itemdepth;
1232 par->params().appendix(par->params().startOfAppendix());
1237 /* Maybe we have to increment the enumeration depth.
1238 * BUT, enumeration in a footnote is considered in isolation from its
1239 * surrounding paragraph so don't increment if this is the
1240 * first line of the footnote
1241 * AND, bibliographies can't have their depth changed ie. they
1242 * are always of depth 0
1245 && par->previous()->getDepth() < par->getDepth()
1246 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1247 && par->enumdepth < 3
1248 && layout->labeltype != LABEL_BIBLIO) {
1252 // Maybe we have to decrement the enumeration depth, see note above
1254 && par->previous()->getDepth() > par->getDepth()
1255 && layout->labeltype != LABEL_BIBLIO) {
1256 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1259 if (!par->params().labelString().empty()) {
1260 par->params().labelString(string());
1263 if (layout->margintype == MARGIN_MANUAL) {
1264 if (par->params().labelWidthString().empty()) {
1265 par->setLabelWidthString(layout->labelstring());
1268 par->setLabelWidthString(string());
1271 // is it a layout that has an automatic label?
1272 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1274 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1275 string numbertype, langtype;
1278 if (i >= 0 && i<= buf->params.secnumdepth) {
1280 buf->counters().step(buf->counters().sects[i]);
1282 // Is there a label? Useful for Chapter layout
1283 if (!par->params().appendix()) {
1284 if (!layout->labelstring().empty())
1285 par->params().labelString(layout->labelstring());
1287 par->params().labelString(string());
1289 if (!layout->labelstring_appendix().empty())
1290 par->params().labelString(layout->labelstring_appendix());
1292 par->params().labelString(string());
1295 // Use if an integer is here less than elegant. For now.
1296 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1297 if (!par->params().appendix()) {
1298 numbertype = "sectioning";
1300 numbertype = "appendix";
1301 if (par->isRightToLeftPar(buf->params))
1302 langtype = "hebrew";
1307 s << buf->counters().numberLabel(buf->counters().sects[i],
1308 numbertype, langtype, head);
1310 par->params().labelString(par->params().labelString() + s.str().c_str());
1311 // We really want to remove the c_str as soon as
1314 // reset enum counters
1315 buf->counters().reset("enum");
1316 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1317 buf->counters().reset("enum");
1318 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1319 buf->counters().step(buf->counters().enums[par->enumdepth]);
1321 s << buf->counters().numberLabel(buf->counters().enums[par->enumdepth],
1322 "enumeration", langtype);
1323 par->params().labelString(s.str().c_str());
1326 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1327 buf->counters().step("bibitem");
1328 int number = buf->counters().value("bibitem");
1330 InsetCommandParams p("bibitem" );
1331 par->bibkey = new InsetBibKey(p);
1333 par->bibkey->setCounter(number);
1334 par->params().labelString(layout->labelstring());
1336 // In biblio should't be following counters but...
1338 string s = layout->labelstring();
1340 // the caption hack:
1341 if (layout->labeltype == LABEL_SENSITIVE) {
1342 Paragraph * tmppar = par;
1345 while (tmppar && tmppar->inInset()
1346 // the single '=' is intended below
1347 && (in = tmppar->inInset()->owner())) {
1348 if (in->lyxCode() == Inset::FLOAT_CODE) {
1352 tmppar = in->parOwner();
1358 = floatList.getType(static_cast<InsetFloat*>(in)->type());
1360 buf->counters().step(fl.name());
1362 // Doesn't work... yet.
1364 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1365 o << fl.name() << " #:";
1368 /* par->SetLayout(0);
1369 s = layout->labelstring; */
1370 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1371 ? " :úåòîùî øñç" : "Senseless: ";
1374 par->params().labelString(s);
1376 /* reset the enumeration counter. They are always resetted
1377 * when there is any other layout between */
1378 for (int i = par->enumdepth + 1; i < 4; i++) {
1379 buf->counters().set(buf->counters().enums[i], 0);
1385 // Updates all counters BEHIND the row. Changed paragraphs
1386 // with a dynamic left margin will be rebroken.
1387 void LyXText::updateCounters(BufferView * bview) const
1391 Row * row = firstrow;
1394 bview->buffer()->counters().reset();
1396 while (row->par() != par)
1399 setCounter(bview->buffer(), par);
1401 // now check for the headline layouts. remember that they
1402 // have a dynamic left margin
1403 LyXLayout_ptr const & layout = par->layout();
1405 if (layout->margintype == MARGIN_DYNAMIC
1406 || layout->labeltype == LABEL_SENSITIVE) {
1407 // Rebreak the paragraph
1408 removeParagraph(row);
1409 appendParagraph(bview, row);
1416 void LyXText::insertInset(BufferView * bview, Inset * inset)
1418 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1420 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1422 cursor.par()->insertInset(cursor.pos(), inset);
1423 // Just to rebreak and refresh correctly.
1424 // The character will not be inserted a second time
1425 insertChar(bview, Paragraph::META_INSET);
1426 // If we enter a highly editable inset the cursor should be to before
1427 // the inset. This couldn't happen before as Undo was not handled inside
1428 // inset now after the Undo LyX tries to call inset->Edit(...) again
1429 // and cannot do this as the cursor is behind the inset and GetInset
1430 // does not return the inset!
1431 if (isHighlyEditableInset(inset)) {
1432 cursorLeft(bview, true);
1438 void LyXText::copyEnvironmentType()
1440 copylayouttype = cursor.par()->layout()->name();
1444 void LyXText::pasteEnvironmentType(BufferView * bview)
1446 setLayout(bview, copylayouttype);
1450 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1452 // Stuff what we got on the clipboard. Even if there is no selection.
1454 // There is a problem with having the stuffing here in that the
1455 // larger the selection the slower LyX will get. This can be
1456 // solved by running the line below only when the selection has
1457 // finished. The solution used currently just works, to make it
1458 // faster we need to be more clever and probably also have more
1459 // calls to stuffClipboard. (Lgb)
1460 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1462 // This doesn't make sense, if there is no selection
1463 if (!selection.set())
1466 // OK, we have a selection. This is always between selection.start
1467 // and selection.end
1469 // make sure that the depth behind the selection are restored, too
1470 Paragraph * endpar = selection.end.par()->next();
1471 Paragraph * undoendpar = endpar;
1473 if (endpar && endpar->getDepth()) {
1474 while (endpar && endpar->getDepth()) {
1475 endpar = endpar->next();
1476 undoendpar = endpar;
1478 } else if (endpar) {
1479 endpar = endpar->next(); // because of parindents etc.
1482 setUndo(bview, Undo::DELETE,
1483 selection.start.par(), undoendpar);
1485 // there are two cases: cut only within one paragraph or
1486 // more than one paragraph
1487 if (selection.start.par() == selection.end.par()) {
1488 // only within one paragraph
1489 endpar = selection.end.par();
1490 int pos = selection.end.pos();
1491 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1492 selection.start.pos(), pos,
1493 bview->buffer()->params.textclass,
1495 selection.end.pos(pos);
1497 endpar = selection.end.par();
1498 int pos = selection.end.pos();
1499 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1500 selection.start.pos(), pos,
1501 bview->buffer()->params.textclass,
1504 selection.end.par(endpar);
1505 selection.end.pos(pos);
1506 cursor.pos(selection.end.pos());
1508 endpar = endpar->next();
1510 // sometimes necessary
1512 selection.start.par()->stripLeadingSpaces();
1514 redoParagraphs(bview, selection.start, endpar);
1516 // cutSelection can invalidate the cursor so we need to set
1518 cursor = selection.start;
1520 // need a valid cursor. (Lgb)
1523 setCursor(bview, cursor.par(), cursor.pos());
1524 selection.cursor = cursor;
1525 updateCounters(bview);
1529 void LyXText::copySelection(BufferView * bview)
1531 // stuff the selection onto the X clipboard, from an explicit copy request
1532 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1534 // this doesnt make sense, if there is no selection
1535 if (!selection.set())
1538 // ok we have a selection. This is always between selection.start
1539 // and sel_end cursor
1541 // copy behind a space if there is one
1542 while (selection.start.par()->size() > selection.start.pos()
1543 && selection.start.par()->isLineSeparator(selection.start.pos())
1544 && (selection.start.par() != selection.end.par()
1545 || selection.start.pos() < selection.end.pos()))
1546 selection.start.pos(selection.start.pos() + 1);
1548 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1549 selection.start.pos(), selection.end.pos(),
1550 bview->buffer()->params.textclass);
1554 void LyXText::pasteSelection(BufferView * bview)
1556 // this does not make sense, if there is nothing to paste
1557 if (!CutAndPaste::checkPastePossible(cursor.par()))
1560 setUndo(bview, Undo::INSERT,
1561 cursor.par(), cursor.par()->next());
1564 Paragraph * actpar = cursor.par();
1565 int pos = cursor.pos();
1567 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1568 bview->buffer()->params.textclass);
1570 redoParagraphs(bview, cursor, endpar);
1572 setCursor(bview, cursor.par(), cursor.pos());
1575 selection.cursor = cursor;
1576 setCursor(bview, actpar, pos);
1577 setSelection(bview);
1578 updateCounters(bview);
1582 // sets the selection over the number of characters of string, no check!!
1583 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1588 selection.cursor = cursor;
1589 for (string::size_type i = 0; i < str.length(); ++i)
1591 setSelection(bview);
1595 // simple replacing. The font of the first selected character is used
1596 void LyXText::replaceSelectionWithString(BufferView * bview,
1599 setCursorParUndo(bview);
1602 if (!selection.set()) { // create a dummy selection
1603 selection.end = cursor;
1604 selection.start = cursor;
1607 // Get font setting before we cut
1608 pos_type pos = selection.end.pos();
1609 LyXFont const font = selection.start.par()
1610 ->getFontSettings(bview->buffer()->params,
1611 selection.start.pos());
1613 // Insert the new string
1614 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1615 selection.end.par()->insertChar(pos, (*cit), font);
1619 // Cut the selection
1620 cutSelection(bview, true, false);
1626 // needed to insert the selection
1627 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1629 Paragraph * par = cursor.par();
1630 pos_type pos = cursor.pos();
1631 Paragraph * endpar = cursor.par()->next();
1633 setCursorParUndo(bview);
1635 // only to be sure, should not be neccessary
1638 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1640 redoParagraphs(bview, cursor, endpar);
1641 setCursor(bview, cursor.par(), cursor.pos());
1642 selection.cursor = cursor;
1643 setCursor(bview, par, pos);
1644 setSelection(bview);
1648 // turns double-CR to single CR, others where converted into one
1649 // blank. Then InsertStringAsLines is called
1650 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1652 string linestr(str);
1653 bool newline_inserted = false;
1654 for (string::size_type i = 0; i < linestr.length(); ++i) {
1655 if (linestr[i] == '\n') {
1656 if (newline_inserted) {
1657 // we know that \r will be ignored by
1658 // InsertStringA. Of course, it is a dirty
1659 // trick, but it works...
1660 linestr[i - 1] = '\r';
1664 newline_inserted = true;
1666 } else if (IsPrintable(linestr[i])) {
1667 newline_inserted = false;
1670 insertStringAsLines(bview, linestr);
1674 bool LyXText::gotoNextInset(BufferView * bview,
1675 vector<Inset::Code> const & codes,
1676 string const & contents) const
1678 LyXCursor res = cursor;
1681 if (res.pos() < res.par()->size() - 1) {
1682 res.pos(res.pos() + 1);
1684 res.par(res.par()->next());
1688 } while (res.par() &&
1689 !(res.par()->isInset(res.pos())
1690 && (inset = res.par()->getInset(res.pos())) != 0
1691 && find(codes.begin(), codes.end(), inset->lyxCode())
1693 && (contents.empty() ||
1694 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1698 setCursor(bview, res.par(), res.pos(), false);
1705 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1708 LyXCursor tmpcursor;
1712 Row * row = getRow(par, pos, y);
1714 // is there a break one row above
1715 if (row->previous() && row->previous()->par() == row->par()) {
1716 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1717 if (z >= row->pos()) {
1718 // set the dimensions of the row above
1719 y -= row->previous()->height();
1721 refresh_row = row->previous();
1722 status(bview, LyXText::NEED_MORE_REFRESH);
1724 breakAgain(bview, row->previous());
1726 // set the cursor again. Otherwise
1727 // dangling pointers are possible
1728 setCursor(bview, cursor.par(), cursor.pos(),
1729 false, cursor.boundary());
1730 selection.cursor = cursor;
1735 int const tmpheight = row->height();
1736 pos_type const tmplast = rowLast(row);
1740 breakAgain(bview, row);
1741 if (row->height() == tmpheight && rowLast(row) == tmplast)
1742 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1744 status(bview, LyXText::NEED_MORE_REFRESH);
1746 // check the special right address boxes
1747 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1754 redoDrawingOfParagraph(bview, tmpcursor);
1757 // set the cursor again. Otherwise dangling pointers are possible
1758 // also set the selection
1760 if (selection.set()) {
1762 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1763 false, selection.cursor.boundary());
1764 selection.cursor = cursor;
1765 setCursorIntern(bview, selection.start.par(),
1766 selection.start.pos(),
1767 false, selection.start.boundary());
1768 selection.start = cursor;
1769 setCursorIntern(bview, selection.end.par(),
1770 selection.end.pos(),
1771 false, selection.end.boundary());
1772 selection.end = cursor;
1773 setCursorIntern(bview, last_sel_cursor.par(),
1774 last_sel_cursor.pos(),
1775 false, last_sel_cursor.boundary());
1776 last_sel_cursor = cursor;
1779 setCursorIntern(bview, cursor.par(), cursor.pos(),
1780 false, cursor.boundary());
1784 // returns false if inset wasn't found
1785 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1787 // first check the current paragraph
1788 int pos = cursor.par()->getPositionOfInset(inset);
1790 checkParagraph(bview, cursor.par(), pos);
1794 // check every paragraph
1796 Paragraph * par = ownerParagraph();
1798 pos = par->getPositionOfInset(inset);
1800 checkParagraph(bview, par, pos);
1810 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1812 bool setfont, bool boundary) const
1814 LyXCursor old_cursor = cursor;
1815 setCursorIntern(bview, par, pos, setfont, boundary);
1816 return deleteEmptyParagraphMechanism(bview, old_cursor);
1820 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1821 pos_type pos, bool boundary) const
1828 cur.boundary(boundary);
1830 // get the cursor y position in text
1832 Row * row = getRow(par, pos, y);
1833 Row * old_row = row;
1835 // if we are before the first char of this row and are still in the
1836 // same paragraph and there is a previous row then put the cursor on
1837 // the end of the previous row
1838 cur.iy(y + row->baseline());
1840 if (row->previous() && pos &&
1841 row->previous()->par() == row->par() &&
1842 par->getChar(pos) == Paragraph::META_INSET &&
1843 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1845 row = row->previous();
1850 // y is now the beginning of the cursor row
1851 y += row->baseline();
1852 // y is now the cursor baseline
1855 pos_type last = rowLastPrintable(old_row);
1857 if (pos > last + 1) {
1858 // This shouldn't happen.
1861 } else if (pos < row->pos()) {
1866 // now get the cursors x position
1867 float x = getCursorX(bview, row, pos, last, boundary);
1870 if (old_row != row) {
1871 x = getCursorX(bview, old_row, pos, last, boundary);
1878 float LyXText::getCursorX(BufferView * bview, Row * row,
1879 pos_type pos, pos_type last, bool boundary) const
1881 pos_type cursor_vpos = 0;
1883 float fill_separator;
1885 float fill_label_hfill;
1886 // This call HAS to be here because of the BidiTables!!!
1887 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1890 if (last < row->pos())
1891 cursor_vpos = row->pos();
1892 else if (pos > last && !boundary)
1893 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1894 ? row->pos() : last + 1;
1895 else if (pos > row->pos() &&
1896 (pos > last || boundary))
1897 /// Place cursor after char at (logical) position pos - 1
1898 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1899 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1901 /// Place cursor before char at (logical) position pos
1902 cursor_vpos = (bidi_level(pos) % 2 == 0)
1903 ? log2vis(pos) : log2vis(pos) + 1;
1905 pos_type main_body =
1906 beginningOfMainBody(bview->buffer(), row->par());
1907 if ((main_body > 0) &&
1908 ((main_body-1 > last) ||
1909 !row->par()->isLineSeparator(main_body-1)))
1912 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1913 pos_type pos = vis2log(vpos);
1914 if (main_body > 0 && pos == main_body - 1) {
1915 x += fill_label_hfill +
1916 font_metrics::width(
1917 row->par()->layout()->labelsep,
1918 getLabelFont(bview->buffer(),
1920 if (row->par()->isLineSeparator(main_body - 1))
1921 x -= singleWidth(bview,
1922 row->par(), main_body - 1);
1924 if (hfillExpansion(bview->buffer(), row, pos)) {
1925 x += singleWidth(bview, row->par(), pos);
1926 if (pos >= main_body)
1929 x += fill_label_hfill;
1930 } else if (row->par()->isSeparator(pos)) {
1931 x += singleWidth(bview, row->par(), pos);
1932 if (pos >= main_body)
1933 x += fill_separator;
1935 x += singleWidth(bview, row->par(), pos);
1941 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1942 pos_type pos, bool setfont, bool boundary) const
1944 InsetText * it = static_cast<InsetText *>(par->inInset());
1946 if (it != inset_owner) {
1947 lyxerr[Debug::INSETS] << "InsetText is " << it
1949 << "inset_owner is "
1950 << inset_owner << endl;
1951 #ifdef WITH_WARNINGS
1952 #warning I believe this code is wrong. (Lgb)
1953 #warning Jürgen, have a look at this. (Lgb)
1954 #warning Hmmm, I guess you are right but we
1955 #warning should verify when this is needed
1957 // Jürgen, would you like to have a look?
1958 // I guess we need to move the outer cursor
1959 // and open and lock the inset (bla bla bla)
1960 // stuff I don't know... so can you have a look?
1962 // I moved the lyxerr stuff in here so we can see if
1963 // this is actually really needed and where!
1965 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1970 setCursor(bview, cursor, par, pos, boundary);
1972 setCurrentFont(bview);
1976 void LyXText::setCurrentFont(BufferView * bview) const
1978 pos_type pos = cursor.pos();
1979 if (cursor.boundary() && pos > 0)
1983 if (pos == cursor.par()->size())
1985 else // potentional bug... BUG (Lgb)
1986 if (cursor.par()->isSeparator(pos)) {
1987 if (pos > cursor.row()->pos() &&
1988 bidi_level(pos) % 2 ==
1989 bidi_level(pos - 1) % 2)
1991 else if (pos + 1 < cursor.par()->size())
1997 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1998 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2000 if (cursor.pos() == cursor.par()->size() &&
2001 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2002 !cursor.boundary()) {
2003 Language const * lang =
2004 cursor.par()->getParLanguage(bview->buffer()->params);
2005 current_font.setLanguage(lang);
2006 current_font.setNumber(LyXFont::OFF);
2007 real_current_font.setLanguage(lang);
2008 real_current_font.setNumber(LyXFont::OFF);
2013 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2015 LyXCursor old_cursor = cursor;
2017 setCursorFromCoordinates(bview, cursor, x, y);
2018 setCurrentFont(bview);
2019 deleteEmptyParagraphMechanism(bview, old_cursor);
2026 * return true if the cursor given is at the end of a row,
2027 * and the next row is filled by an inset that spans an entire
2030 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2033 Row const & next = *row.next();
2035 if (next.pos() != cur.pos() || next.par() != cur.par())
2037 if (!cur.par()->isInset(cur.pos()))
2039 Inset const * inset = cur.par()->getInset(cur.pos());
2040 if (inset->needFullRow() || inset->display())
2047 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2050 // Get the row first.
2052 Row * row = getRowNearY(y);
2054 pos_type const column = getColumnNearX(bview, row, x, bound);
2055 cur.par(row->par());
2056 cur.pos(row->pos() + column);
2058 cur.y(y + row->baseline());
2061 if (beforeFullRowInset(*row, cur)) {
2062 pos_type last = rowLastPrintable(row);
2063 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2065 cur.iy(y + row->height() + row->next()->baseline());
2066 cur.irow(row->next());
2072 cur.boundary(bound);
2076 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2078 if (cursor.pos() > 0) {
2079 bool boundary = cursor.boundary();
2080 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2081 if (!internal && !boundary &&
2082 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2083 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2084 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2085 Paragraph * par = cursor.par()->previous();
2086 setCursor(bview, par, par->size());
2091 void LyXText::cursorRight(BufferView * bview, bool internal) const
2093 if (!internal && cursor.boundary() &&
2094 !cursor.par()->isNewline(cursor.pos()))
2095 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2096 else if (cursor.pos() < cursor.par()->size()) {
2097 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2099 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2100 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2101 } else if (cursor.par()->next())
2102 setCursor(bview, cursor.par()->next(), 0);
2106 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2109 int x = cursor.x_fix();
2110 int y = cursor.y() - cursor.row()->baseline() - 1;
2111 setCursorFromCoordinates(bview, x, y);
2113 int y1 = cursor.iy() - first_y;
2117 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2118 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2119 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2123 setCursorFromCoordinates(bview, cursor.x_fix(),
2124 cursor.y() - cursor.row()->baseline() - 1);
2129 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2132 int x = cursor.x_fix();
2133 int y = cursor.y() - cursor.row()->baseline() +
2134 cursor.row()->height() + 1;
2135 setCursorFromCoordinates(bview, x, y);
2136 if (!selecting && cursor.row() == cursor.irow()) {
2137 int y1 = cursor.iy() - first_y;
2141 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2142 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2143 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2147 setCursorFromCoordinates(bview, cursor.x_fix(),
2148 cursor.y() - cursor.row()->baseline()
2149 + cursor.row()->height() + 1);
2154 void LyXText::cursorUpParagraph(BufferView * bview) const
2156 if (cursor.pos() > 0) {
2157 setCursor(bview, cursor.par(), 0);
2159 else if (cursor.par()->previous()) {
2160 setCursor(bview, cursor.par()->previous(), 0);
2165 void LyXText::cursorDownParagraph(BufferView * bview) const
2167 if (cursor.par()->next()) {
2168 setCursor(bview, cursor.par()->next(), 0);
2170 setCursor(bview, cursor.par(), cursor.par()->size());
2174 // fix the cursor `cur' after a characters has been deleted at `where'
2175 // position. Called by deleteEmptyParagraphMechanism
2176 void LyXText::fixCursorAfterDelete(BufferView * bview,
2178 LyXCursor const & where) const
2180 // if cursor is not in the paragraph where the delete occured,
2182 if (cur.par() != where.par())
2185 // if cursor position is after the place where the delete occured,
2187 if (cur.pos() > where.pos())
2188 cur.pos(cur.pos()-1);
2190 // check also if we don't want to set the cursor on a spot behind the
2191 // pagragraph because we erased the last character.
2192 if (cur.pos() > cur.par()->size())
2193 cur.pos(cur.par()->size());
2195 // recompute row et al. for this cursor
2196 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2200 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2201 LyXCursor const & old_cursor) const
2203 // Would be wrong to delete anything if we have a selection.
2204 if (selection.set())
2207 // We allow all kinds of "mumbo-jumbo" when freespacing.
2208 if (old_cursor.par()->layout()->free_spacing
2209 || old_cursor.par()->isFreeSpacing()) {
2213 /* Ok I'll put some comments here about what is missing.
2214 I have fixed BackSpace (and thus Delete) to not delete
2215 double-spaces automagically. I have also changed Cut,
2216 Copy and Paste to hopefully do some sensible things.
2217 There are still some small problems that can lead to
2218 double spaces stored in the document file or space at
2219 the beginning of paragraphs. This happens if you have
2220 the cursor betwenn to spaces and then save. Or if you
2221 cut and paste and the selection have a space at the
2222 beginning and then save right after the paste. I am
2223 sure none of these are very hard to fix, but I will
2224 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2225 that I can get some feedback. (Lgb)
2228 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2229 // delete the LineSeparator.
2232 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2233 // delete the LineSeparator.
2236 // If the pos around the old_cursor were spaces, delete one of them.
2237 if (old_cursor.par() != cursor.par()
2238 || old_cursor.pos() != cursor.pos()) {
2239 // Only if the cursor has really moved
2241 if (old_cursor.pos() > 0
2242 && old_cursor.pos() < old_cursor.par()->size()
2243 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2244 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2245 old_cursor.par()->erase(old_cursor.pos() - 1);
2246 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2248 #ifdef WITH_WARNINGS
2249 #warning This will not work anymore when we have multiple views of the same buffer
2250 // In this case, we will have to correct also the cursors held by
2251 // other bufferviews. It will probably be easier to do that in a more
2252 // automated way in LyXCursor code. (JMarc 26/09/2001)
2254 // correct all cursors held by the LyXText
2255 fixCursorAfterDelete(bview, cursor, old_cursor);
2256 fixCursorAfterDelete(bview, selection.cursor,
2258 fixCursorAfterDelete(bview, selection.start,
2260 fixCursorAfterDelete(bview, selection.end, old_cursor);
2261 fixCursorAfterDelete(bview, last_sel_cursor,
2263 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2264 fixCursorAfterDelete(bview, toggle_end_cursor,
2270 // don't delete anything if this is the ONLY paragraph!
2271 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2274 // Do not delete empty paragraphs with keepempty set.
2275 if (old_cursor.par()->layout()->keepempty)
2278 // only do our magic if we changed paragraph
2279 if (old_cursor.par() == cursor.par())
2282 // record if we have deleted a paragraph
2283 // we can't possibly have deleted a paragraph before this point
2284 bool deleted = false;
2286 if ((old_cursor.par()->empty()
2287 || (old_cursor.par()->size() == 1
2288 && old_cursor.par()->isLineSeparator(0)))) {
2289 // ok, we will delete anything
2290 LyXCursor tmpcursor;
2292 // make sure that you do not delete any environments
2293 status(bview, LyXText::NEED_MORE_REFRESH);
2296 if (old_cursor.row()->previous()) {
2297 refresh_row = old_cursor.row()->previous();
2298 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2300 cursor = old_cursor; // that undo can restore the right cursor position
2301 Paragraph * endpar = old_cursor.par()->next();
2302 if (endpar && endpar->getDepth()) {
2303 while (endpar && endpar->getDepth()) {
2304 endpar = endpar->next();
2307 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2311 removeRow(old_cursor.row());
2312 if (ownerParagraph() == old_cursor.par()) {
2313 ownerParagraph(ownerParagraph()->next());
2316 delete old_cursor.par();
2318 /* Breakagain the next par. Needed because of
2319 * the parindent that can occur or dissappear.
2320 * The next row can change its height, if
2321 * there is another layout before */
2322 if (refresh_row->next()) {
2323 breakAgain(bview, refresh_row->next());
2324 updateCounters(bview);
2326 setHeightOfRow(bview, refresh_row);
2328 refresh_row = old_cursor.row()->next();
2329 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2332 cursor = old_cursor; // that undo can restore the right cursor position
2333 Paragraph * endpar = old_cursor.par()->next();
2334 if (endpar && endpar->getDepth()) {
2335 while (endpar && endpar->getDepth()) {
2336 endpar = endpar->next();
2339 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2343 removeRow(old_cursor.row());
2345 if (ownerParagraph() == old_cursor.par()) {
2346 ownerParagraph(ownerParagraph()->next());
2349 delete old_cursor.par();
2351 /* Breakagain the next par. Needed because of
2352 the parindent that can occur or dissappear.
2353 The next row can change its height, if
2354 there is another layout before */
2356 breakAgain(bview, refresh_row);
2357 updateCounters(bview);
2362 setCursorIntern(bview, cursor.par(), cursor.pos());
2364 if (selection.cursor.par() == old_cursor.par()
2365 && selection.cursor.pos() == old_cursor.pos()) {
2366 // correct selection
2367 selection.cursor = cursor;
2371 if (old_cursor.par()->stripLeadingSpaces()) {
2372 redoParagraphs(bview, old_cursor,
2373 old_cursor.par()->next());
2375 setCursorIntern(bview, cursor.par(), cursor.pos());
2376 selection.cursor = cursor;
2383 Paragraph * LyXText::ownerParagraph() const
2386 return inset_owner->paragraph();
2388 return &*(bv_owner->buffer()->paragraphs.begin());
2392 void LyXText::ownerParagraph(Paragraph * p) const
2395 inset_owner->paragraph(p);
2397 bv_owner->buffer()->paragraphs.set(p);
2402 void LyXText::ownerParagraph(int id, Paragraph * p) const
2404 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2405 if (op && op->inInset()) {
2406 static_cast<InsetText *>(op->inInset())->paragraph(p);
2413 LyXText::text_status LyXText::status() const
2419 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2421 LyXText * t = bview->text;
2423 // We should only go up with refreshing code so this means that if
2424 // we have a MORE refresh we should never set it to LITTLE if we still
2425 // didn't handle it (and then it will be UNCHANGED. Now as long as
2426 // we stay inside one LyXText this may work but we need to tell the
2427 // outermost LyXText that it should REALLY draw us if there is some
2428 // change in a Inset::LyXText. So you see that when we are inside a
2429 // inset's LyXText we give the LITTLE to the outermost LyXText to
2430 // tell'em that it should redraw the actual row (where the inset
2431 // resides! Capito?!
2433 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2435 if (inset_owner && st != UNCHANGED) {
2436 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2437 if (!t->refresh_row) {
2438 t->refresh_row = t->cursor.row();
2439 t->refresh_y = t->cursor.y() -
2440 t->cursor.row()->baseline();