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, cursor.row());
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, cursor.row());
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, cursor.row());
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());
889 void LyXText::fullRebreak(BufferView * bview)
895 if (need_break_row) {
896 breakAgain(bview, need_break_row);
903 // important for the screen
906 // the cursor set functions have a special mechanism. When they
907 // realize, that you left an empty paragraph, they will delete it.
908 // They also delete the corresponding row
910 // need the selection cursor:
911 void LyXText::setSelection(BufferView * bview)
913 bool const lsel = selection.set();
915 if (!selection.set()) {
916 last_sel_cursor = selection.cursor;
917 selection.start = selection.cursor;
918 selection.end = selection.cursor;
923 // first the toggling area
924 if (cursor.y() < last_sel_cursor.y()
925 || (cursor.y() == last_sel_cursor.y()
926 && cursor.x() < last_sel_cursor.x())) {
927 toggle_end_cursor = last_sel_cursor;
928 toggle_cursor = cursor;
930 toggle_end_cursor = cursor;
931 toggle_cursor = last_sel_cursor;
934 last_sel_cursor = cursor;
936 // and now the whole selection
938 if (selection.cursor.par() == cursor.par())
939 if (selection.cursor.pos() < cursor.pos()) {
940 selection.end = cursor;
941 selection.start = selection.cursor;
943 selection.end = selection.cursor;
944 selection.start = cursor;
946 else if (selection.cursor.y() < cursor.y() ||
947 (selection.cursor.y() == cursor.y()
948 && selection.cursor.x() < cursor.x())) {
949 selection.end = cursor;
950 selection.start = selection.cursor;
953 selection.end = selection.cursor;
954 selection.start = cursor;
957 // a selection with no contents is not a selection
958 if (selection.start.par() == selection.end.par() &&
959 selection.start.pos() == selection.end.pos())
960 selection.set(false);
962 if (inset_owner && (selection.set() || lsel))
963 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
967 string const LyXText::selectionAsString(Buffer const * buffer,
970 if (!selection.set()) return string();
972 // should be const ...
973 Paragraph * startpar(selection.start.par());
974 Paragraph * endpar(selection.end.par());
975 pos_type const startpos(selection.start.pos());
976 pos_type const endpos(selection.end.pos());
978 if (startpar == endpar) {
979 return startpar->asString(buffer, startpos, endpos, label);
984 // First paragraph in selection
985 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
987 // The paragraphs in between (if any)
988 LyXCursor tmpcur(selection.start);
989 tmpcur.par(tmpcur.par()->next());
990 while (tmpcur.par() != endpar) {
991 result += tmpcur.par()->asString(buffer, 0,
992 tmpcur.par()->size(),
994 tmpcur.par(tmpcur.par()->next());
997 // Last paragraph in selection
998 result += endpar->asString(buffer, 0, endpos, label);
1004 void LyXText::clearSelection() const
1006 selection.set(false);
1007 selection.mark(false);
1008 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1009 // reset this in the bv_owner!
1010 if (bv_owner && bv_owner->text)
1011 bv_owner->text->xsel_cache.set(false);
1015 void LyXText::cursorHome(BufferView * bview) const
1017 setCursor(bview, cursor.par(), cursor.row()->pos());
1021 void LyXText::cursorEnd(BufferView * bview) const
1023 if (!cursor.row()->next()
1024 || cursor.row()->next()->par() != cursor.row()->par()) {
1025 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1027 if (cursor.par()->size() &&
1028 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1029 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1030 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1032 setCursor(bview,cursor.par(),
1033 rowLast(cursor.row()) + 1);
1039 void LyXText::cursorTop(BufferView * bview) const
1041 while (cursor.par()->previous())
1042 cursor.par(cursor.par()->previous());
1043 setCursor(bview, cursor.par(), 0);
1047 void LyXText::cursorBottom(BufferView * bview) const
1049 while (cursor.par()->next())
1050 cursor.par(cursor.par()->next());
1051 setCursor(bview, cursor.par(), cursor.par()->size());
1055 void LyXText::toggleFree(BufferView * bview,
1056 LyXFont const & font, bool toggleall)
1058 // If the mask is completely neutral, tell user
1059 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1060 // Could only happen with user style
1061 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1065 // Try implicit word selection
1066 // If there is a change in the language the implicit word selection
1068 LyXCursor resetCursor = cursor;
1069 bool implicitSelection = (font.language() == ignore_language
1070 && font.number() == LyXFont::IGNORE)
1071 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1074 setFont(bview, font, toggleall);
1076 // Implicit selections are cleared afterwards
1077 //and cursor is set to the original position.
1078 if (implicitSelection) {
1080 cursor = resetCursor;
1081 setCursor(bview, cursor.par(), cursor.pos());
1082 selection.cursor = cursor;
1085 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1089 string LyXText::getStringToIndex(BufferView * bview)
1093 // Try implicit word selection
1094 // If there is a change in the language the implicit word selection
1096 LyXCursor const reset_cursor = cursor;
1097 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1099 if (!selection.set()) {
1100 bview->owner()->message(_("Nothing to index!"));
1103 if (selection.start.par() != selection.end.par()) {
1104 bview->owner()->message(_("Cannot index more than one paragraph!"));
1108 idxstring = selectionAsString(bview->buffer(), false);
1110 // Implicit selections are cleared afterwards
1111 //and cursor is set to the original position.
1112 if (implicitSelection) {
1114 cursor = reset_cursor;
1115 setCursor(bview, cursor.par(), cursor.pos());
1116 selection.cursor = cursor;
1122 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1123 Paragraph const * par) const
1125 if (par->layout()->labeltype != LABEL_MANUAL)
1128 return par->beginningOfMainBody();
1132 // the DTP switches for paragraphs. LyX will store them in the first
1133 // physicla paragraph. When a paragraph is broken, the top settings rest,
1134 // the bottom settings are given to the new one. So I can make shure,
1135 // they do not duplicate themself and you cannnot make dirty things with
1138 void LyXText::setParagraph(BufferView * bview,
1139 bool line_top, bool line_bottom,
1140 bool pagebreak_top, bool pagebreak_bottom,
1141 VSpace const & space_top,
1142 VSpace const & space_bottom,
1143 Spacing const & spacing,
1145 string labelwidthstring,
1148 LyXCursor tmpcursor = cursor;
1149 if (!selection.set()) {
1150 selection.start = cursor;
1151 selection.end = cursor;
1154 // make sure that the depth behind the selection are restored, too
1155 Paragraph * endpar = selection.end.par()->next();
1156 Paragraph * undoendpar = endpar;
1158 if (endpar && endpar->getDepth()) {
1159 while (endpar && endpar->getDepth()) {
1160 endpar = endpar->next();
1161 undoendpar = endpar;
1165 // because of parindents etc.
1166 endpar = endpar->next();
1169 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1172 Paragraph * tmppar = selection.end.par();
1174 while (tmppar != selection.start.par()->previous()) {
1175 setCursor(bview, tmppar, 0);
1176 status(bview, LyXText::NEED_MORE_REFRESH);
1177 refresh_row = cursor.row();
1178 refresh_y = cursor.y() - cursor.row()->baseline();
1179 cursor.par()->params().lineTop(line_top);
1180 cursor.par()->params().lineBottom(line_bottom);
1181 cursor.par()->params().pagebreakTop(pagebreak_top);
1182 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1183 cursor.par()->params().spaceTop(space_top);
1184 cursor.par()->params().spaceBottom(space_bottom);
1185 cursor.par()->params().spacing(spacing);
1186 // does the layout allow the new alignment?
1187 LyXLayout_ptr const & layout = cursor.par()->layout();
1189 if (align == LYX_ALIGN_LAYOUT)
1190 align = layout->align;
1191 if (align & layout->alignpossible) {
1192 if (align == layout->align)
1193 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1195 cursor.par()->params().align(align);
1197 cursor.par()->setLabelWidthString(labelwidthstring);
1198 cursor.par()->params().noindent(noindent);
1199 tmppar = cursor.par()->previous();
1202 redoParagraphs(bview, selection.start, endpar);
1205 setCursor(bview, selection.start.par(), selection.start.pos());
1206 selection.cursor = cursor;
1207 setCursor(bview, selection.end.par(), selection.end.pos());
1208 setSelection(bview);
1209 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1211 bview->updateInset(inset_owner, true);
1215 // set the counter of a paragraph. This includes the labels
1216 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1218 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1219 LyXLayout_ptr const & layout = par->layout();
1221 // copy the prev-counters to this one,
1222 // unless this is the first paragraph
1223 if (par->previous()) {
1225 par->counters().copy(par->previous()->counters(), par->counters(), "");
1227 par->params().appendix(par->previous()->params().appendix());
1228 if (!par->params().appendix() && par->params().startOfAppendix()) {
1229 par->params().appendix(true);
1230 par->counters().reset("");
1232 par->enumdepth = par->previous()->enumdepth;
1233 par->itemdepth = par->previous()->itemdepth;
1235 par->counters().reset("");
1236 par->params().appendix(par->params().startOfAppendix());
1241 /* Maybe we have to increment the enumeration depth.
1242 * BUT, enumeration in a footnote is considered in isolation from its
1243 * surrounding paragraph so don't increment if this is the
1244 * first line of the footnote
1245 * AND, bibliographies can't have their depth changed ie. they
1246 * are always of depth 0
1249 && par->previous()->getDepth() < par->getDepth()
1250 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1251 && par->enumdepth < 3
1252 && layout->labeltype != LABEL_BIBLIO) {
1256 // Maybe we have to decrement the enumeration depth, see note above
1258 && par->previous()->getDepth() > par->getDepth()
1259 && layout->labeltype != LABEL_BIBLIO) {
1260 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1261 par->counters().set(par->counters().enums[par->enumdepth],
1262 par->depthHook(par->getDepth())->counters().value(par->counters().enums[par->enumdepth]));
1265 if (!par->params().labelString().empty()) {
1266 par->params().labelString(string());
1269 if (layout->margintype == MARGIN_MANUAL) {
1270 if (par->params().labelWidthString().empty()) {
1271 par->setLabelWidthString(layout->labelstring());
1274 par->setLabelWidthString(string());
1277 // is it a layout that has an automatic label?
1278 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1280 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1281 string numbertype, langtype;
1284 if (i >= 0 && i<= buf->params.secnumdepth) {
1286 par->counters().step(par->counters().sects[i]);
1288 // Is there a label? Useful for Chapter layout
1289 if (!par->params().appendix()) {
1290 if (!layout->labelstring().empty())
1291 par->params().labelString(layout->labelstring());
1293 par->params().labelString(string());
1295 if (!layout->labelstring_appendix().empty())
1296 par->params().labelString(layout->labelstring_appendix());
1298 par->params().labelString(string());
1301 // Use if an integer is here less than elegant. For now.
1302 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1303 if (!par->params().appendix()) {
1304 numbertype = "sectioning";
1306 numbertype = "appendix";
1307 if (par->isRightToLeftPar(buf->params))
1308 langtype = "hebrew";
1313 s << par->counters().numberLabel(par->counters().sects[i],
1314 numbertype, langtype, head);
1316 par->params().labelString(par->params().labelString() +s.str().c_str());
1317 // We really want to remove the c_str as soon as
1320 // reset enum counters
1321 par->counters().reset("enum");
1322 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1323 par->counters().reset("enum");
1324 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1325 par->counters().step(par->counters().enums[par->enumdepth]);
1327 s << par->counters().numberLabel(par->counters().enums[par->enumdepth],
1328 "enumeration", langtype);
1329 par->params().labelString(s.str().c_str());
1332 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1333 par->counters().step("bibitem");
1334 int number = par->counters().value("bibitem");
1336 InsetCommandParams p("bibitem" );
1337 par->bibkey = new InsetBibKey(p);
1339 par->bibkey->setCounter(number);
1340 par->params().labelString(layout->labelstring());
1342 // In biblio should't be following counters but...
1344 string s = layout->labelstring();
1346 // the caption hack:
1347 if (layout->labeltype == LABEL_SENSITIVE) {
1348 bool isOK (par->inInset() && par->inInset()->owner() &&
1349 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1352 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1354 = floatList.getType(tmp->type());
1356 // Why doesn't it work? -- MV
1357 par->counters().step(fl.name());
1358 // We should get the correct number here too.
1360 o << fl.name() << " " << par->counters().value(fl.name()) << ":";
1363 /* par->SetLayout(0);
1364 s = layout->labelstring; */
1365 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1366 ? " :úåòîùî øñç" : "Senseless: ";
1369 par->params().labelString(s);
1371 /* reset the enumeration counter. They are always resetted
1372 * when there is any other layout between */
1373 for (int i = par->enumdepth + 1; i < 4; i++) {
1374 par->counters().set(par->counters().enums[i], 0);
1380 // Updates all counters BEHIND the row. Changed paragraphs
1381 // with a dynamic left margin will be rebroken.
1382 void LyXText::updateCounters(BufferView * bview, Row * row) const
1390 par = row->par()->next();
1394 while (row->par() != par)
1397 setCounter(bview->buffer(), par);
1399 // now check for the headline layouts. remember that they
1400 // have a dynamic left margin
1401 LyXLayout_ptr const & layout = par->layout();
1403 if (layout->margintype == MARGIN_DYNAMIC
1404 || layout->labeltype == LABEL_SENSITIVE) {
1405 // Rebreak the paragraph
1406 removeParagraph(row);
1407 appendParagraph(bview, row);
1414 void LyXText::insertInset(BufferView * bview, Inset * inset)
1416 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1418 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1420 cursor.par()->insertInset(cursor.pos(), inset);
1421 // Just to rebreak and refresh correctly.
1422 // The character will not be inserted a second time
1423 insertChar(bview, Paragraph::META_INSET);
1424 // If we enter a highly editable inset the cursor should be to before
1425 // the inset. This couldn't happen before as Undo was not handled inside
1426 // inset now after the Undo LyX tries to call inset->Edit(...) again
1427 // and cannot do this as the cursor is behind the inset and GetInset
1428 // does not return the inset!
1429 if (isHighlyEditableInset(inset)) {
1430 cursorLeft(bview, true);
1436 void LyXText::copyEnvironmentType()
1438 copylayouttype = cursor.par()->layout()->name();
1442 void LyXText::pasteEnvironmentType(BufferView * bview)
1444 setLayout(bview, copylayouttype);
1448 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1450 // Stuff what we got on the clipboard. Even if there is no selection.
1452 // There is a problem with having the stuffing here in that the
1453 // larger the selection the slower LyX will get. This can be
1454 // solved by running the line below only when the selection has
1455 // finished. The solution used currently just works, to make it
1456 // faster we need to be more clever and probably also have more
1457 // calls to stuffClipboard. (Lgb)
1458 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1460 // This doesn't make sense, if there is no selection
1461 if (!selection.set())
1464 // OK, we have a selection. This is always between selection.start
1465 // and selection.end
1467 // make sure that the depth behind the selection are restored, too
1468 Paragraph * endpar = selection.end.par()->next();
1469 Paragraph * undoendpar = endpar;
1471 if (endpar && endpar->getDepth()) {
1472 while (endpar && endpar->getDepth()) {
1473 endpar = endpar->next();
1474 undoendpar = endpar;
1476 } else if (endpar) {
1477 endpar = endpar->next(); // because of parindents etc.
1480 setUndo(bview, Undo::DELETE,
1481 selection.start.par(), undoendpar);
1483 // there are two cases: cut only within one paragraph or
1484 // more than one paragraph
1485 if (selection.start.par() == selection.end.par()) {
1486 // only within one paragraph
1487 endpar = selection.end.par();
1488 int pos = selection.end.pos();
1489 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1490 selection.start.pos(), pos,
1491 bview->buffer()->params.textclass,
1493 selection.end.pos(pos);
1495 endpar = selection.end.par();
1496 int pos = selection.end.pos();
1497 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1498 selection.start.pos(), pos,
1499 bview->buffer()->params.textclass,
1502 selection.end.par(endpar);
1503 selection.end.pos(pos);
1504 cursor.pos(selection.end.pos());
1506 endpar = endpar->next();
1508 // sometimes necessary
1510 selection.start.par()->stripLeadingSpaces();
1512 redoParagraphs(bview, selection.start, endpar);
1514 // cutSelection can invalidate the cursor so we need to set
1516 cursor = selection.start;
1518 // need a valid cursor. (Lgb)
1521 setCursor(bview, cursor.par(), cursor.pos());
1522 selection.cursor = cursor;
1523 updateCounters(bview, cursor.row());
1527 void LyXText::copySelection(BufferView * bview)
1529 // stuff the selection onto the X clipboard, from an explicit copy request
1530 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1532 // this doesnt make sense, if there is no selection
1533 if (!selection.set())
1536 // ok we have a selection. This is always between selection.start
1537 // and sel_end cursor
1539 // copy behind a space if there is one
1540 while (selection.start.par()->size() > selection.start.pos()
1541 && selection.start.par()->isLineSeparator(selection.start.pos())
1542 && (selection.start.par() != selection.end.par()
1543 || selection.start.pos() < selection.end.pos()))
1544 selection.start.pos(selection.start.pos() + 1);
1546 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1547 selection.start.pos(), selection.end.pos(),
1548 bview->buffer()->params.textclass);
1552 void LyXText::pasteSelection(BufferView * bview)
1554 // this does not make sense, if there is nothing to paste
1555 if (!CutAndPaste::checkPastePossible(cursor.par()))
1558 setUndo(bview, Undo::INSERT,
1559 cursor.par(), cursor.par()->next());
1562 Paragraph * actpar = cursor.par();
1563 int pos = cursor.pos();
1565 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1566 bview->buffer()->params.textclass);
1568 redoParagraphs(bview, cursor, endpar);
1570 setCursor(bview, cursor.par(), cursor.pos());
1573 selection.cursor = cursor;
1574 setCursor(bview, actpar, pos);
1575 setSelection(bview);
1576 updateCounters(bview, cursor.row());
1580 // sets the selection over the number of characters of string, no check!!
1581 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1586 selection.cursor = cursor;
1587 for (string::size_type i = 0; i < str.length(); ++i)
1589 setSelection(bview);
1593 // simple replacing. The font of the first selected character is used
1594 void LyXText::replaceSelectionWithString(BufferView * bview,
1597 setCursorParUndo(bview);
1600 if (!selection.set()) { // create a dummy selection
1601 selection.end = cursor;
1602 selection.start = cursor;
1605 // Get font setting before we cut
1606 pos_type pos = selection.end.pos();
1607 LyXFont const font = selection.start.par()
1608 ->getFontSettings(bview->buffer()->params,
1609 selection.start.pos());
1611 // Insert the new string
1612 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1613 selection.end.par()->insertChar(pos, (*cit), font);
1617 // Cut the selection
1618 cutSelection(bview, true, false);
1624 // needed to insert the selection
1625 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1627 Paragraph * par = cursor.par();
1628 pos_type pos = cursor.pos();
1629 Paragraph * endpar = cursor.par()->next();
1631 setCursorParUndo(bview);
1633 // only to be sure, should not be neccessary
1636 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1638 redoParagraphs(bview, cursor, endpar);
1639 setCursor(bview, cursor.par(), cursor.pos());
1640 selection.cursor = cursor;
1641 setCursor(bview, par, pos);
1642 setSelection(bview);
1646 // turns double-CR to single CR, others where converted into one
1647 // blank. Then InsertStringAsLines is called
1648 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1650 string linestr(str);
1651 bool newline_inserted = false;
1652 for (string::size_type i = 0; i < linestr.length(); ++i) {
1653 if (linestr[i] == '\n') {
1654 if (newline_inserted) {
1655 // we know that \r will be ignored by
1656 // InsertStringA. Of course, it is a dirty
1657 // trick, but it works...
1658 linestr[i - 1] = '\r';
1662 newline_inserted = true;
1664 } else if (IsPrintable(linestr[i])) {
1665 newline_inserted = false;
1668 insertStringAsLines(bview, linestr);
1672 bool LyXText::gotoNextInset(BufferView * bview,
1673 vector<Inset::Code> const & codes,
1674 string const & contents) const
1676 LyXCursor res = cursor;
1679 if (res.pos() < res.par()->size() - 1) {
1680 res.pos(res.pos() + 1);
1682 res.par(res.par()->next());
1686 } while (res.par() &&
1687 !(res.par()->isInset(res.pos())
1688 && (inset = res.par()->getInset(res.pos())) != 0
1689 && find(codes.begin(), codes.end(), inset->lyxCode())
1691 && (contents.empty() ||
1692 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1696 setCursor(bview, res.par(), res.pos(), false);
1703 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1706 LyXCursor tmpcursor;
1710 Row * row = getRow(par, pos, y);
1712 // is there a break one row above
1713 if (row->previous() && row->previous()->par() == row->par()) {
1714 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1715 if (z >= row->pos()) {
1716 // set the dimensions of the row above
1717 y -= row->previous()->height();
1719 refresh_row = row->previous();
1720 status(bview, LyXText::NEED_MORE_REFRESH);
1722 breakAgain(bview, row->previous());
1724 // set the cursor again. Otherwise
1725 // dangling pointers are possible
1726 setCursor(bview, cursor.par(), cursor.pos(),
1727 false, cursor.boundary());
1728 selection.cursor = cursor;
1733 int const tmpheight = row->height();
1734 pos_type const tmplast = rowLast(row);
1738 breakAgain(bview, row);
1739 if (row->height() == tmpheight && rowLast(row) == tmplast)
1740 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1742 status(bview, LyXText::NEED_MORE_REFRESH);
1744 // check the special right address boxes
1745 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1752 redoDrawingOfParagraph(bview, tmpcursor);
1755 // set the cursor again. Otherwise dangling pointers are possible
1756 // also set the selection
1758 if (selection.set()) {
1760 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1761 false, selection.cursor.boundary());
1762 selection.cursor = cursor;
1763 setCursorIntern(bview, selection.start.par(),
1764 selection.start.pos(),
1765 false, selection.start.boundary());
1766 selection.start = cursor;
1767 setCursorIntern(bview, selection.end.par(),
1768 selection.end.pos(),
1769 false, selection.end.boundary());
1770 selection.end = cursor;
1771 setCursorIntern(bview, last_sel_cursor.par(),
1772 last_sel_cursor.pos(),
1773 false, last_sel_cursor.boundary());
1774 last_sel_cursor = cursor;
1777 setCursorIntern(bview, cursor.par(), cursor.pos(),
1778 false, cursor.boundary());
1782 // returns false if inset wasn't found
1783 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1785 // first check the current paragraph
1786 int pos = cursor.par()->getPositionOfInset(inset);
1788 checkParagraph(bview, cursor.par(), pos);
1792 // check every paragraph
1794 Paragraph * par = ownerParagraph();
1796 pos = par->getPositionOfInset(inset);
1798 checkParagraph(bview, par, pos);
1808 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1810 bool setfont, bool boundary) const
1812 LyXCursor old_cursor = cursor;
1813 setCursorIntern(bview, par, pos, setfont, boundary);
1814 return deleteEmptyParagraphMechanism(bview, old_cursor);
1818 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1819 pos_type pos, bool boundary) const
1826 cur.boundary(boundary);
1828 // get the cursor y position in text
1830 Row * row = getRow(par, pos, y);
1831 Row * old_row = row;
1833 // if we are before the first char of this row and are still in the
1834 // same paragraph and there is a previous row then put the cursor on
1835 // the end of the previous row
1836 cur.iy(y + row->baseline());
1838 if (row->previous() && pos &&
1839 row->previous()->par() == row->par() &&
1840 par->getChar(pos) == Paragraph::META_INSET &&
1841 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1843 row = row->previous();
1848 // y is now the beginning of the cursor row
1849 y += row->baseline();
1850 // y is now the cursor baseline
1853 pos_type last = rowLastPrintable(old_row);
1855 if (pos > last + 1) {
1856 // This shouldn't happen.
1859 } else if (pos < row->pos()) {
1864 // now get the cursors x position
1865 float x = getCursorX(bview, row, pos, last, boundary);
1868 if (old_row != row) {
1869 x = getCursorX(bview, old_row, pos, last, boundary);
1876 float LyXText::getCursorX(BufferView * bview, Row * row,
1877 pos_type pos, pos_type last, bool boundary) const
1879 pos_type cursor_vpos = 0;
1881 float fill_separator;
1883 float fill_label_hfill;
1884 // This call HAS to be here because of the BidiTables!!!
1885 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1888 if (last < row->pos())
1889 cursor_vpos = row->pos();
1890 else if (pos > last && !boundary)
1891 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1892 ? row->pos() : last + 1;
1893 else if (pos > row->pos() &&
1894 (pos > last || boundary))
1895 /// Place cursor after char at (logical) position pos - 1
1896 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1897 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1899 /// Place cursor before char at (logical) position pos
1900 cursor_vpos = (bidi_level(pos) % 2 == 0)
1901 ? log2vis(pos) : log2vis(pos) + 1;
1903 pos_type main_body =
1904 beginningOfMainBody(bview->buffer(), row->par());
1905 if ((main_body > 0) &&
1906 ((main_body-1 > last) ||
1907 !row->par()->isLineSeparator(main_body-1)))
1910 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1911 pos_type pos = vis2log(vpos);
1912 if (main_body > 0 && pos == main_body - 1) {
1913 x += fill_label_hfill +
1914 font_metrics::width(
1915 row->par()->layout()->labelsep,
1916 getLabelFont(bview->buffer(),
1918 if (row->par()->isLineSeparator(main_body - 1))
1919 x -= singleWidth(bview,
1920 row->par(), main_body - 1);
1922 if (hfillExpansion(bview->buffer(), row, pos)) {
1923 x += singleWidth(bview, row->par(), pos);
1924 if (pos >= main_body)
1927 x += fill_label_hfill;
1928 } else if (row->par()->isSeparator(pos)) {
1929 x += singleWidth(bview, row->par(), pos);
1930 if (pos >= main_body)
1931 x += fill_separator;
1933 x += singleWidth(bview, row->par(), pos);
1939 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1940 pos_type pos, bool setfont, bool boundary) const
1942 InsetText * it = static_cast<InsetText *>(par->inInset());
1944 if (it != inset_owner) {
1945 lyxerr[Debug::INSETS] << "InsetText is " << it
1947 << "inset_owner is "
1948 << inset_owner << endl;
1949 #ifdef WITH_WARNINGS
1950 #warning I believe this code is wrong. (Lgb)
1951 #warning Jürgen, have a look at this. (Lgb)
1952 #warning Hmmm, I guess you are right but we
1953 #warning should verify when this is needed
1955 // Jürgen, would you like to have a look?
1956 // I guess we need to move the outer cursor
1957 // and open and lock the inset (bla bla bla)
1958 // stuff I don't know... so can you have a look?
1960 // I moved the lyxerr stuff in here so we can see if
1961 // this is actually really needed and where!
1963 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1968 setCursor(bview, cursor, par, pos, boundary);
1970 setCurrentFont(bview);
1974 void LyXText::setCurrentFont(BufferView * bview) const
1976 pos_type pos = cursor.pos();
1977 if (cursor.boundary() && pos > 0)
1981 if (pos == cursor.par()->size())
1983 else // potentional bug... BUG (Lgb)
1984 if (cursor.par()->isSeparator(pos)) {
1985 if (pos > cursor.row()->pos() &&
1986 bidi_level(pos) % 2 ==
1987 bidi_level(pos - 1) % 2)
1989 else if (pos + 1 < cursor.par()->size())
1995 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1996 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1998 if (cursor.pos() == cursor.par()->size() &&
1999 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2000 !cursor.boundary()) {
2001 Language const * lang =
2002 cursor.par()->getParLanguage(bview->buffer()->params);
2003 current_font.setLanguage(lang);
2004 current_font.setNumber(LyXFont::OFF);
2005 real_current_font.setLanguage(lang);
2006 real_current_font.setNumber(LyXFont::OFF);
2011 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2013 LyXCursor old_cursor = cursor;
2015 setCursorFromCoordinates(bview, cursor, x, y);
2016 setCurrentFont(bview);
2017 deleteEmptyParagraphMechanism(bview, old_cursor);
2024 * return true if the cursor given is at the end of a row,
2025 * and the next row is filled by an inset that spans an entire
2028 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2031 Row const & next = *row.next();
2033 if (next.pos() != cur.pos() || next.par() != cur.par())
2035 if (!cur.par()->isInset(cur.pos()))
2037 Inset const * inset = cur.par()->getInset(cur.pos());
2038 if (inset->needFullRow() || inset->display())
2045 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2048 // Get the row first.
2050 Row * row = getRowNearY(y);
2052 pos_type const column = getColumnNearX(bview, row, x, bound);
2053 cur.par(row->par());
2054 cur.pos(row->pos() + column);
2056 cur.y(y + row->baseline());
2059 if (beforeFullRowInset(*row, cur)) {
2060 pos_type last = rowLastPrintable(row);
2061 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2063 cur.iy(y + row->height() + row->next()->baseline());
2064 cur.irow(row->next());
2070 cur.boundary(bound);
2074 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2076 if (cursor.pos() > 0) {
2077 bool boundary = cursor.boundary();
2078 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2079 if (!internal && !boundary &&
2080 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2081 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2082 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2083 Paragraph * par = cursor.par()->previous();
2084 setCursor(bview, par, par->size());
2089 void LyXText::cursorRight(BufferView * bview, bool internal) const
2091 if (!internal && cursor.boundary() &&
2092 !cursor.par()->isNewline(cursor.pos()))
2093 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2094 else if (cursor.pos() < cursor.par()->size()) {
2095 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2097 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2098 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2099 } else if (cursor.par()->next())
2100 setCursor(bview, cursor.par()->next(), 0);
2104 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2107 int x = cursor.x_fix();
2108 int y = cursor.y() - cursor.row()->baseline() - 1;
2109 setCursorFromCoordinates(bview, x, y);
2111 int y1 = cursor.iy() - first_y;
2115 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2116 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2117 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2121 setCursorFromCoordinates(bview, cursor.x_fix(),
2122 cursor.y() - cursor.row()->baseline() - 1);
2127 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2130 int x = cursor.x_fix();
2131 int y = cursor.y() - cursor.row()->baseline() +
2132 cursor.row()->height() + 1;
2133 setCursorFromCoordinates(bview, x, y);
2134 if (!selecting && cursor.row() == cursor.irow()) {
2135 int y1 = cursor.iy() - first_y;
2139 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2140 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2141 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2145 setCursorFromCoordinates(bview, cursor.x_fix(),
2146 cursor.y() - cursor.row()->baseline()
2147 + cursor.row()->height() + 1);
2152 void LyXText::cursorUpParagraph(BufferView * bview) const
2154 if (cursor.pos() > 0) {
2155 setCursor(bview, cursor.par(), 0);
2157 else if (cursor.par()->previous()) {
2158 setCursor(bview, cursor.par()->previous(), 0);
2163 void LyXText::cursorDownParagraph(BufferView * bview) const
2165 if (cursor.par()->next()) {
2166 setCursor(bview, cursor.par()->next(), 0);
2168 setCursor(bview, cursor.par(), cursor.par()->size());
2172 // fix the cursor `cur' after a characters has been deleted at `where'
2173 // position. Called by deleteEmptyParagraphMechanism
2174 void LyXText::fixCursorAfterDelete(BufferView * bview,
2176 LyXCursor const & where) const
2178 // if cursor is not in the paragraph where the delete occured,
2180 if (cur.par() != where.par())
2183 // if cursor position is after the place where the delete occured,
2185 if (cur.pos() > where.pos())
2186 cur.pos(cur.pos()-1);
2188 // check also if we don't want to set the cursor on a spot behind the
2189 // pagragraph because we erased the last character.
2190 if (cur.pos() > cur.par()->size())
2191 cur.pos(cur.par()->size());
2193 // recompute row et al. for this cursor
2194 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2198 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2199 LyXCursor const & old_cursor) const
2201 // Would be wrong to delete anything if we have a selection.
2202 if (selection.set())
2205 // We allow all kinds of "mumbo-jumbo" when freespacing.
2206 if (old_cursor.par()->layout()->free_spacing
2207 || old_cursor.par()->isFreeSpacing()) {
2211 /* Ok I'll put some comments here about what is missing.
2212 I have fixed BackSpace (and thus Delete) to not delete
2213 double-spaces automagically. I have also changed Cut,
2214 Copy and Paste to hopefully do some sensible things.
2215 There are still some small problems that can lead to
2216 double spaces stored in the document file or space at
2217 the beginning of paragraphs. This happens if you have
2218 the cursor betwenn to spaces and then save. Or if you
2219 cut and paste and the selection have a space at the
2220 beginning and then save right after the paste. I am
2221 sure none of these are very hard to fix, but I will
2222 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2223 that I can get some feedback. (Lgb)
2226 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2227 // delete the LineSeparator.
2230 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2231 // delete the LineSeparator.
2234 // If the pos around the old_cursor were spaces, delete one of them.
2235 if (old_cursor.par() != cursor.par()
2236 || old_cursor.pos() != cursor.pos()) {
2237 // Only if the cursor has really moved
2239 if (old_cursor.pos() > 0
2240 && old_cursor.pos() < old_cursor.par()->size()
2241 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2242 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2243 old_cursor.par()->erase(old_cursor.pos() - 1);
2244 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2246 #ifdef WITH_WARNINGS
2247 #warning This will not work anymore when we have multiple views of the same buffer
2248 // In this case, we will have to correct also the cursors held by
2249 // other bufferviews. It will probably be easier to do that in a more
2250 // automated way in LyXCursor code. (JMarc 26/09/2001)
2252 // correct all cursors held by the LyXText
2253 fixCursorAfterDelete(bview, cursor, old_cursor);
2254 fixCursorAfterDelete(bview, selection.cursor,
2256 fixCursorAfterDelete(bview, selection.start,
2258 fixCursorAfterDelete(bview, selection.end, old_cursor);
2259 fixCursorAfterDelete(bview, last_sel_cursor,
2261 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2262 fixCursorAfterDelete(bview, toggle_end_cursor,
2268 // don't delete anything if this is the ONLY paragraph!
2269 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2272 // Do not delete empty paragraphs with keepempty set.
2273 if (old_cursor.par()->layout()->keepempty)
2276 // only do our magic if we changed paragraph
2277 if (old_cursor.par() == cursor.par())
2280 // record if we have deleted a paragraph
2281 // we can't possibly have deleted a paragraph before this point
2282 bool deleted = false;
2284 if ((old_cursor.par()->size() == 0
2285 || (old_cursor.par()->size() == 1
2286 && old_cursor.par()->isLineSeparator(0)))) {
2287 // ok, we will delete anything
2288 LyXCursor tmpcursor;
2290 // make sure that you do not delete any environments
2291 status(bview, LyXText::NEED_MORE_REFRESH);
2294 if (old_cursor.row()->previous()) {
2295 refresh_row = old_cursor.row()->previous();
2296 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2298 cursor = old_cursor; // that undo can restore the right cursor position
2299 Paragraph * endpar = old_cursor.par()->next();
2300 if (endpar && endpar->getDepth()) {
2301 while (endpar && endpar->getDepth()) {
2302 endpar = endpar->next();
2305 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2309 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 */
2320 if (refresh_row->next()) {
2321 breakAgain(bview, refresh_row->next());
2322 updateCounters(bview, refresh_row);
2324 setHeightOfRow(bview, refresh_row);
2326 refresh_row = old_cursor.row()->next();
2327 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2330 cursor = old_cursor; // that undo can restore the right cursor position
2331 Paragraph * endpar = old_cursor.par()->next();
2332 if (endpar && endpar->getDepth()) {
2333 while (endpar && endpar->getDepth()) {
2334 endpar = endpar->next();
2337 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2341 removeRow(old_cursor.row());
2343 if (ownerParagraph() == old_cursor.par()) {
2344 ownerParagraph(ownerParagraph()->next());
2347 delete old_cursor.par();
2349 /* Breakagain the next par. Needed because of
2350 the parindent that can occur or dissappear.
2351 The next row can change its height, if
2352 there is another layout before */
2354 breakAgain(bview, refresh_row);
2355 updateCounters(bview, refresh_row->previous());
2360 setCursorIntern(bview, cursor.par(), cursor.pos());
2362 if (selection.cursor.par() == old_cursor.par()
2363 && selection.cursor.pos() == old_cursor.pos()) {
2364 // correct selection
2365 selection.cursor = cursor;
2369 if (old_cursor.par()->stripLeadingSpaces()) {
2370 redoParagraphs(bview, old_cursor,
2371 old_cursor.par()->next());
2373 setCursorIntern(bview, cursor.par(), cursor.pos());
2374 selection.cursor = cursor;
2381 void LyXText::toggleAppendix(BufferView * bview)
2383 Paragraph * par = cursor.par();
2384 bool start = !par->params().startOfAppendix();
2386 // ensure that we have only one start_of_appendix in this document
2387 Paragraph * tmp = ownerParagraph();
2388 for (; tmp; tmp = tmp->next()) {
2389 tmp->params().startOfAppendix(false);
2392 par->params().startOfAppendix(start);
2394 // we can set the refreshing parameters now
2395 status(bview, LyXText::NEED_MORE_REFRESH);
2397 refresh_row = 0; // not needed for full update
2398 updateCounters(bview, 0);
2399 setCursor(bview, cursor.par(), cursor.pos());
2403 Paragraph * LyXText::ownerParagraph() const
2406 return inset_owner->paragraph();
2408 return bv_owner->buffer()->paragraph;
2412 void LyXText::ownerParagraph(Paragraph * p) const
2415 inset_owner->paragraph(p);
2417 bv_owner->buffer()->paragraph = p;
2422 void LyXText::ownerParagraph(int id, Paragraph * p) const
2424 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2425 if (op && op->inInset()) {
2426 static_cast<InsetText *>(op->inInset())->paragraph(p);
2433 LyXText::text_status LyXText::status() const
2439 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2441 LyXText * t = bview->text;
2443 // We should only go up with refreshing code so this means that if
2444 // we have a MORE refresh we should never set it to LITTLE if we still
2445 // didn't handle it (and then it will be UNCHANGED. Now as long as
2446 // we stay inside one LyXText this may work but we need to tell the
2447 // outermost LyXText that it should REALLY draw us if there is some
2448 // change in a Inset::LyXText. So you see that when we are inside a
2449 // inset's LyXText we give the LITTLE to the outermost LyXText to
2450 // tell'em that it should redraw the actual row (where the inset
2451 // resides! Capito?!
2453 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2455 if (inset_owner && st != UNCHANGED) {
2456 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2457 if (!t->refresh_row) {
2458 t->refresh_row = t->cursor.row();
2459 t->refresh_y = t->cursor.y() -
2460 t->cursor.row()->baseline();