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 : 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 : 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 first_y = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
96 insertParagraph(bview, par, lastrow);
99 setCursorIntern(bview, firstrow->par(), 0);
100 selection.cursor = cursor;
106 // Delete all rows, this does not touch the paragraphs!
107 Row * tmprow = firstrow;
109 tmprow = firstrow->next();
118 LyXFont const realizeFont(LyXFont const & font,
122 LyXTextClass const & tclass = buf->params.getLyXTextClass();
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(tclass[par->layout()]->font,
134 buf->params.language);
136 par_depth = par->getDepth();
140 #ifndef INHERIT_LANGUAGE
141 tmpfont.realize(tclass.defaultfont());
143 tmpfont.realize(tclass.defaultfont(), buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout_ptr const & layout = par->layout();
166 // We specialize the 95% common case:
167 if (!par->getDepth()) {
168 if (layout->labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params, pos);
173 par->inInset()->getDrawFont(f);
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout->reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
182 par->inInset()->getDrawFont(f);
183 #ifndef INHERIT_LANGUAGE
184 return f.realize(layout->resfont);
186 return f.realize(layout.resfont, buf->params.language);
191 // The uncommon case need not be optimized as much
195 if (pos < beginningOfMainBody(buf, par)) {
197 layoutfont = layout->labelfont;
200 layoutfont = layout->font;
203 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
204 #ifndef INHERIT_LANGUAGE
205 tmpfont.realize(layoutfont);
207 tmpfont.realize(layoutfont, buf->params.language);
210 par->inInset()->getDrawFont(tmpfont);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout_ptr const & layout = par->layout();
220 if (!par->getDepth()) {
221 return layout->resfont;
224 return realizeFont(layout->font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout_ptr const & layout = par->layout();
232 if (!par->getDepth()) {
233 return layout->reslabelfont;
236 return realizeFont(layout->labelfont, buf, par);
240 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
241 pos_type pos, LyXFont const & fnt,
244 Buffer const * buf = bv->buffer();
245 LyXFont font = getFont(buf, par, pos);
246 font.update(fnt, buf->params.language, toggleall);
247 // Let the insets convert their font
248 if (par->isInset(pos)) {
249 Inset * inset = par->getInset(pos);
250 if (isEditableInset(inset)) {
251 UpdatableInset * uinset =
252 static_cast<UpdatableInset *>(inset);
253 uinset->setFont(bv, fnt, toggleall, true);
257 // Plug thru to version below:
258 setCharFont(buf, par, pos, font);
262 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
263 pos_type pos, LyXFont const & fnt)
267 LyXTextClass const & tclass = buf->params.getLyXTextClass();
268 LyXLayout_ptr const & layout = par->layout();
270 // Get concrete layout font to reduce against
273 if (pos < beginningOfMainBody(buf, par))
274 layoutfont = layout->labelfont;
276 layoutfont = layout->font;
278 // Realize against environment font information
279 if (par->getDepth()) {
280 Paragraph * tp = par;
281 while (!layoutfont.resolved() && tp && tp->getDepth()) {
282 tp = tp->outerHook();
284 #ifndef INHERIT_LANGUAGE
285 layoutfont.realize(tp->layout()->font);
287 layoutfont.realize(tclass[tp->layout()].font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(tclass.defaultfont());
296 layoutfont.realize(tclass.defaultfont(), buf->params.language);
299 // Now, reduce font against full layout font
300 font.reduce(layoutfont);
302 par->setFont(pos, font);
306 // inserts a new row behind the specified row, increments
307 // the touched counters
308 void LyXText::insertRow(Row * row, Paragraph * par,
311 Row * tmprow = new Row;
314 tmprow->next(firstrow);
317 tmprow->previous(row);
318 tmprow->next(row->next());
323 tmprow->next()->previous(tmprow);
325 if (tmprow->previous())
326 tmprow->previous()->next(tmprow);
337 // removes the row and reset the touched counters
338 void LyXText::removeRow(Row * row) const
340 Row * row_prev = row->previous();
342 row->next()->previous(row_prev);
344 firstrow = row->next();
345 // lyx::Assert(firstrow);
347 row_prev->next(row->next());
349 if (row == lastrow) {
350 lyx::Assert(!row->next());
353 if (refresh_row == row) {
354 refresh_row = row_prev ? row_prev : row->next();
355 // what about refresh_y, refresh_height
358 height -= row->height(); // the text becomes smaller
364 // remove all following rows of the paragraph of the specified row.
365 void LyXText::removeParagraph(Row * row) const
367 Paragraph * tmppar = row->par();
371 while (row && row->par() == tmppar) {
372 tmprow = row->next();
379 // insert the specified paragraph behind the specified row
380 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
383 // insert a new row, starting at position 0
384 insertRow(row, par, 0);
387 setCounter(bview->buffer(), par);
389 // and now append the whole paragraph behind the new row
392 appendParagraph(bview, firstrow);
394 row->next()->height(0);
395 appendParagraph(bview, row->next());
400 Inset * LyXText::getInset() const
403 if (cursor.pos() == 0 && cursor.par()->bibkey) {
404 inset = cursor.par()->bibkey;
405 } else if (cursor.pos() < cursor.par()->size()
406 && cursor.par()->isInset(cursor.pos())) {
407 inset = cursor.par()->getInset(cursor.pos());
413 void LyXText::toggleInset(BufferView * bview)
415 Inset * inset = getInset();
416 // is there an editable inset at cursor position?
417 if (!isEditableInset(inset)) {
418 // No, try to see if we are inside a collapsable inset
419 if (inset_owner && inset_owner->owner()
420 && inset_owner->owner()->isOpen()) {
421 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
422 inset_owner->owner()->close(bview);
423 bview->getLyXText()->cursorRight(bview);
427 //bview->owner()->message(inset->editMessage());
429 // do we want to keep this?? (JMarc)
430 if (!isHighlyEditableInset(inset))
431 setCursorParUndo(bview);
433 if (inset->isOpen()) {
439 inset->open(bview, !inset->isOpen());
444 /* used in setlayout */
445 // Asger is not sure we want to do this...
446 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
449 LyXLayout_ptr const & layout = par->layout();
452 for (pos_type pos = 0; pos < par->size(); ++pos) {
453 if (pos < beginningOfMainBody(buf, par))
454 layoutfont = layout->labelfont;
456 layoutfont = layout->font;
458 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
459 tmpfont.reduce(layoutfont);
460 par->setFont(pos, tmpfont);
465 Paragraph * LyXText::setLayout(BufferView * bview,
466 LyXCursor & cur, LyXCursor & sstart_cur,
467 LyXCursor & send_cur,
468 string const & layout)
470 Paragraph * endpar = send_cur.par()->next();
471 Paragraph * undoendpar = endpar;
473 if (endpar && endpar->getDepth()) {
474 while (endpar && endpar->getDepth()) {
475 endpar = endpar->next();
479 endpar = endpar->next(); // because of parindents etc.
482 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
484 // ok we have a selection. This is always between sstart_cur
485 // and sel_end cursor
487 Paragraph * par = sstart_cur.par();
488 Paragraph * epar = send_cur.par()->next();
490 LyXLayout_ptr const & lyxlayout =
491 bview->buffer()->params.getLyXTextClass()[layout];
494 par->applyLayout(lyxlayout);
495 makeFontEntriesLayoutSpecific(bview->buffer(), par);
496 Paragraph * fppar = par;
497 fppar->params().spaceTop(lyxlayout->fill_top ?
498 VSpace(VSpace::VFILL)
499 : VSpace(VSpace::NONE));
500 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
501 VSpace(VSpace::VFILL)
502 : VSpace(VSpace::NONE));
503 if (lyxlayout->margintype == MARGIN_MANUAL)
504 par->setLabelWidthString(lyxlayout->labelstring());
505 if (lyxlayout->labeltype != LABEL_BIBLIO
507 delete fppar->bibkey;
512 } while (par != epar);
518 // set layout over selection and make a total rebreak of those paragraphs
519 void LyXText::setLayout(BufferView * bview, string const & layout)
521 LyXCursor tmpcursor = cursor; /* store the current cursor */
523 // if there is no selection just set the layout
524 // of the current paragraph */
525 if (!selection.set()) {
526 selection.start = cursor; // dummy selection
527 selection.end = cursor;
529 Paragraph * endpar = setLayout(bview, cursor, selection.start,
530 selection.end, layout);
531 redoParagraphs(bview, selection.start, endpar);
533 // we have to reset the selection, because the
534 // geometry could have changed
535 setCursor(bview, selection.start.par(),
536 selection.start.pos(), false);
537 selection.cursor = cursor;
538 setCursor(bview, selection.end.par(), selection.end.pos(), false);
539 updateCounters(bview);
542 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
546 // increment depth over selection and
547 // make a total rebreak of those paragraphs
548 void LyXText::incDepth(BufferView * bview)
550 // If there is no selection, just use the current paragraph
551 if (!selection.set()) {
552 selection.start = cursor; // dummy selection
553 selection.end = cursor;
556 // We end at the next paragraph with depth 0
557 Paragraph * endpar = selection.end.par()->next();
559 Paragraph * undoendpar = endpar;
561 if (endpar && endpar->getDepth()) {
562 while (endpar && endpar->getDepth()) {
563 endpar = endpar->next();
567 endpar = endpar->next(); // because of parindents etc.
570 setUndo(bview, Undo::EDIT,
571 selection.start.par(), undoendpar);
573 LyXCursor tmpcursor = cursor; // store the current cursor
575 // ok we have a selection. This is always between sel_start_cursor
576 // and sel_end cursor
577 cursor = selection.start;
579 bool anything_changed = false;
582 // NOTE: you can't change the depth of a bibliography entry
583 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
584 Paragraph * prev = cursor.par()->previous();
587 if (cursor.par()->getDepth()
588 < prev->getMaxDepthAfter()) {
589 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
590 anything_changed = true;
594 if (cursor.par() == selection.end.par())
596 cursor.par(cursor.par()->next());
599 // if nothing changed set all depth to 0
600 if (!anything_changed) {
601 cursor = selection.start;
602 while (cursor.par() != selection.end.par()) {
603 cursor.par()->params().depth(0);
604 cursor.par(cursor.par()->next());
606 cursor.par()->params().depth(0);
609 redoParagraphs(bview, selection.start, endpar);
611 // we have to reset the selection, because the
612 // geometry could have changed
613 setCursor(bview, selection.start.par(), selection.start.pos());
614 selection.cursor = cursor;
615 setCursor(bview, selection.end.par(), selection.end.pos());
616 updateCounters(bview);
619 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
623 // decrement depth over selection and
624 // make a total rebreak of those paragraphs
625 void LyXText::decDepth(BufferView * bview)
627 // if there is no selection just set the layout
628 // of the current paragraph
629 if (!selection.set()) {
630 selection.start = cursor; // dummy selection
631 selection.end = cursor;
633 Paragraph * endpar = selection.end.par()->next();
634 Paragraph * undoendpar = endpar;
636 if (endpar && endpar->getDepth()) {
637 while (endpar && endpar->getDepth()) {
638 endpar = endpar->next();
642 endpar = endpar->next(); // because of parindents etc.
645 setUndo(bview, Undo::EDIT,
646 selection.start.par(), undoendpar);
648 LyXCursor tmpcursor = cursor; // store the current cursor
650 // ok we have a selection. This is always between sel_start_cursor
651 // and sel_end cursor
652 cursor = selection.start;
655 if (cursor.par()->params().depth()) {
656 cursor.par()->params()
657 .depth(cursor.par()->params().depth() - 1);
659 if (cursor.par() == selection.end.par()) {
662 cursor.par(cursor.par()->next());
665 redoParagraphs(bview, selection.start, endpar);
667 // we have to reset the selection, because the
668 // geometry could have changed
669 setCursor(bview, selection.start.par(),
670 selection.start.pos());
671 selection.cursor = cursor;
672 setCursor(bview, selection.end.par(), selection.end.pos());
673 updateCounters(bview);
676 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
680 // set font over selection and make a total rebreak of those paragraphs
681 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
683 // if there is no selection just set the current_font
684 if (!selection.set()) {
685 // Determine basis font
687 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
689 layoutfont = getLabelFont(bview->buffer(),
692 layoutfont = getLayoutFont(bview->buffer(),
695 // Update current font
696 real_current_font.update(font,
697 bview->buffer()->params.language,
700 // Reduce to implicit settings
701 current_font = real_current_font;
702 current_font.reduce(layoutfont);
703 // And resolve it completely
704 #ifndef INHERIT_LANGUAGE
705 real_current_font.realize(layoutfont);
707 real_current_font.realize(layoutfont,
708 bview->buffer()->params.language);
713 LyXCursor tmpcursor = cursor; // store the current cursor
715 // ok we have a selection. This is always between sel_start_cursor
716 // and sel_end cursor
718 setUndo(bview, Undo::EDIT,
719 selection.start.par(), selection.end.par()->next());
721 cursor = selection.start;
722 while (cursor.par() != selection.end.par() ||
723 cursor.pos() < selection.end.pos())
725 if (cursor.pos() < cursor.par()->size()) {
726 // an open footnote should behave like a closed one
727 setCharFont(bview, cursor.par(), cursor.pos(),
729 cursor.pos(cursor.pos() + 1);
732 cursor.par(cursor.par()->next());
737 redoParagraphs(bview, selection.start, selection.end.par()->next());
739 // we have to reset the selection, because the
740 // geometry could have changed, but we keep
741 // it for user convenience
742 setCursor(bview, selection.start.par(), selection.start.pos());
743 selection.cursor = cursor;
744 setCursor(bview, selection.end.par(), selection.end.pos());
746 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
747 tmpcursor.boundary());
751 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
753 Row * tmprow = cur.row();
754 int y = cur.y() - tmprow->baseline();
756 setHeightOfRow(bview, tmprow);
758 while (tmprow->previous()
759 && tmprow->previous()->par() == tmprow->par()) {
760 tmprow = tmprow->previous();
761 y -= tmprow->height();
762 setHeightOfRow(bview, tmprow);
765 // we can set the refreshing parameters now
766 status(bview, LyXText::NEED_MORE_REFRESH);
768 refresh_row = tmprow;
769 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
773 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
775 Row * tmprow = cur.row();
777 int y = cur.y() - tmprow->baseline();
778 setHeightOfRow(bview, tmprow);
780 while (tmprow->previous()
781 && tmprow->previous()->par() == tmprow->par()) {
782 tmprow = tmprow->previous();
783 y -= tmprow->height();
786 // we can set the refreshing parameters now
787 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
789 refresh_row = tmprow;
791 status(bview, LyXText::NEED_MORE_REFRESH);
792 setCursor(bview, cur.par(), cur.pos());
796 // deletes and inserts again all paragaphs between the cursor
797 // and the specified par
798 // This function is needed after SetLayout and SetFont etc.
799 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
800 Paragraph const * endpar) const
803 Paragraph * tmppar = 0;
804 Paragraph * first_phys_par = 0;
806 Row * tmprow = cur.row();
808 int y = cur.y() - tmprow->baseline();
810 if (!tmprow->previous()) {
811 // a trick/hack for UNDO
812 // This is needed because in an UNDO/REDO we could have changed
813 // the ownerParagrah() so the paragraph inside the row is NOT
814 // my really first par anymore. Got it Lars ;) (Jug 20011206)
815 first_phys_par = ownerParagraph();
817 first_phys_par = tmprow->par();
818 while (tmprow->previous()
819 && tmprow->previous()->par() == first_phys_par)
821 tmprow = tmprow->previous();
822 y -= tmprow->height();
826 // we can set the refreshing parameters now
827 status(bview, LyXText::NEED_MORE_REFRESH);
829 refresh_row = tmprow->previous(); /* the real refresh row will
830 be deleted, so I store
834 tmppar = tmprow->next()->par();
837 while (tmprow->next() && tmppar != endpar) {
838 removeRow(tmprow->next());
839 if (tmprow->next()) {
840 tmppar = tmprow->next()->par();
846 // remove the first one
847 tmprow2 = tmprow; /* this is because tmprow->previous()
849 tmprow = tmprow->previous();
852 tmppar = first_phys_par;
856 insertParagraph(bview, tmppar, tmprow);
860 while (tmprow->next()
861 && tmprow->next()->par() == tmppar) {
862 tmprow = tmprow->next();
864 tmppar = tmppar->next();
866 } while (tmppar && tmppar != endpar);
868 // this is because of layout changes
870 refresh_y -= refresh_row->height();
871 setHeightOfRow(bview, refresh_row);
873 refresh_row = firstrow;
875 setHeightOfRow(bview, refresh_row);
878 if (tmprow && tmprow->next())
879 setHeightOfRow(bview, tmprow->next());
880 updateCounters(bview);
884 void LyXText::fullRebreak(BufferView * bview)
890 if (need_break_row) {
891 breakAgain(bview, need_break_row);
898 // important for the screen
901 // the cursor set functions have a special mechanism. When they
902 // realize, that you left an empty paragraph, they will delete it.
903 // They also delete the corresponding row
905 // need the selection cursor:
906 void LyXText::setSelection(BufferView * bview)
908 bool const lsel = selection.set();
910 if (!selection.set()) {
911 last_sel_cursor = selection.cursor;
912 selection.start = selection.cursor;
913 selection.end = selection.cursor;
918 // first the toggling area
919 if (cursor.y() < last_sel_cursor.y()
920 || (cursor.y() == last_sel_cursor.y()
921 && cursor.x() < last_sel_cursor.x())) {
922 toggle_end_cursor = last_sel_cursor;
923 toggle_cursor = cursor;
925 toggle_end_cursor = cursor;
926 toggle_cursor = last_sel_cursor;
929 last_sel_cursor = cursor;
931 // and now the whole selection
933 if (selection.cursor.par() == cursor.par())
934 if (selection.cursor.pos() < cursor.pos()) {
935 selection.end = cursor;
936 selection.start = selection.cursor;
938 selection.end = selection.cursor;
939 selection.start = cursor;
941 else if (selection.cursor.y() < cursor.y() ||
942 (selection.cursor.y() == cursor.y()
943 && selection.cursor.x() < cursor.x())) {
944 selection.end = cursor;
945 selection.start = selection.cursor;
948 selection.end = selection.cursor;
949 selection.start = cursor;
952 // a selection with no contents is not a selection
953 if (selection.start.par() == selection.end.par() &&
954 selection.start.pos() == selection.end.pos())
955 selection.set(false);
957 if (inset_owner && (selection.set() || lsel))
958 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
962 string const LyXText::selectionAsString(Buffer const * buffer,
965 if (!selection.set()) return string();
967 // should be const ...
968 Paragraph * startpar(selection.start.par());
969 Paragraph * endpar(selection.end.par());
970 pos_type const startpos(selection.start.pos());
971 pos_type const endpos(selection.end.pos());
973 if (startpar == endpar) {
974 return startpar->asString(buffer, startpos, endpos, label);
979 // First paragraph in selection
980 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
982 // The paragraphs in between (if any)
983 LyXCursor tmpcur(selection.start);
984 tmpcur.par(tmpcur.par()->next());
985 while (tmpcur.par() != endpar) {
986 result += tmpcur.par()->asString(buffer, 0,
987 tmpcur.par()->size(),
989 tmpcur.par(tmpcur.par()->next());
992 // Last paragraph in selection
993 result += endpar->asString(buffer, 0, endpos, label);
999 void LyXText::clearSelection() const
1001 selection.set(false);
1002 selection.mark(false);
1003 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1004 // reset this in the bv_owner!
1005 if (bv_owner && bv_owner->text)
1006 bv_owner->text->xsel_cache.set(false);
1010 void LyXText::cursorHome(BufferView * bview) const
1012 setCursor(bview, cursor.par(), cursor.row()->pos());
1016 void LyXText::cursorEnd(BufferView * bview) const
1018 if (!cursor.row()->next()
1019 || cursor.row()->next()->par() != cursor.row()->par()) {
1020 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1022 if (!cursor.par()->empty() &&
1023 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1024 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1025 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1027 setCursor(bview,cursor.par(),
1028 rowLast(cursor.row()) + 1);
1034 void LyXText::cursorTop(BufferView * bview) const
1036 while (cursor.par()->previous())
1037 cursor.par(cursor.par()->previous());
1038 setCursor(bview, cursor.par(), 0);
1042 void LyXText::cursorBottom(BufferView * bview) const
1044 while (cursor.par()->next())
1045 cursor.par(cursor.par()->next());
1046 setCursor(bview, cursor.par(), cursor.par()->size());
1050 void LyXText::toggleFree(BufferView * bview,
1051 LyXFont const & font, bool toggleall)
1053 // If the mask is completely neutral, tell user
1054 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1055 // Could only happen with user style
1056 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1060 // Try implicit word selection
1061 // If there is a change in the language the implicit word selection
1063 LyXCursor resetCursor = cursor;
1064 bool implicitSelection = (font.language() == ignore_language
1065 && font.number() == LyXFont::IGNORE)
1066 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1069 setFont(bview, font, toggleall);
1071 // Implicit selections are cleared afterwards
1072 //and cursor is set to the original position.
1073 if (implicitSelection) {
1075 cursor = resetCursor;
1076 setCursor(bview, cursor.par(), cursor.pos());
1077 selection.cursor = cursor;
1080 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1084 string LyXText::getStringToIndex(BufferView * bview)
1088 // Try implicit word selection
1089 // If there is a change in the language the implicit word selection
1091 LyXCursor const reset_cursor = cursor;
1092 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1094 if (!selection.set()) {
1095 bview->owner()->message(_("Nothing to index!"));
1098 if (selection.start.par() != selection.end.par()) {
1099 bview->owner()->message(_("Cannot index more than one paragraph!"));
1103 idxstring = selectionAsString(bview->buffer(), false);
1105 // Implicit selections are cleared afterwards
1106 //and cursor is set to the original position.
1107 if (implicitSelection) {
1109 cursor = reset_cursor;
1110 setCursor(bview, cursor.par(), cursor.pos());
1111 selection.cursor = cursor;
1117 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1118 Paragraph const * par) const
1120 if (par->layout()->labeltype != LABEL_MANUAL)
1123 return par->beginningOfMainBody();
1127 // the DTP switches for paragraphs. LyX will store them in the first
1128 // physicla paragraph. When a paragraph is broken, the top settings rest,
1129 // the bottom settings are given to the new one. So I can make shure,
1130 // they do not duplicate themself and you cannnot make dirty things with
1133 void LyXText::setParagraph(BufferView * bview,
1134 bool line_top, bool line_bottom,
1135 bool pagebreak_top, bool pagebreak_bottom,
1136 VSpace const & space_top,
1137 VSpace const & space_bottom,
1138 Spacing const & spacing,
1140 string labelwidthstring,
1143 LyXCursor tmpcursor = cursor;
1144 if (!selection.set()) {
1145 selection.start = cursor;
1146 selection.end = cursor;
1149 // make sure that the depth behind the selection are restored, too
1150 Paragraph * endpar = selection.end.par()->next();
1151 Paragraph * undoendpar = endpar;
1153 if (endpar && endpar->getDepth()) {
1154 while (endpar && endpar->getDepth()) {
1155 endpar = endpar->next();
1156 undoendpar = endpar;
1160 // because of parindents etc.
1161 endpar = endpar->next();
1164 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1167 Paragraph * tmppar = selection.end.par();
1169 while (tmppar != selection.start.par()->previous()) {
1170 setCursor(bview, tmppar, 0);
1171 status(bview, LyXText::NEED_MORE_REFRESH);
1172 refresh_row = cursor.row();
1173 refresh_y = cursor.y() - cursor.row()->baseline();
1174 cursor.par()->params().lineTop(line_top);
1175 cursor.par()->params().lineBottom(line_bottom);
1176 cursor.par()->params().pagebreakTop(pagebreak_top);
1177 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1178 cursor.par()->params().spaceTop(space_top);
1179 cursor.par()->params().spaceBottom(space_bottom);
1180 cursor.par()->params().spacing(spacing);
1181 // does the layout allow the new alignment?
1182 LyXLayout_ptr const & layout = cursor.par()->layout();
1184 if (align == LYX_ALIGN_LAYOUT)
1185 align = layout->align;
1186 if (align & layout->alignpossible) {
1187 if (align == layout->align)
1188 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1190 cursor.par()->params().align(align);
1192 cursor.par()->setLabelWidthString(labelwidthstring);
1193 cursor.par()->params().noindent(noindent);
1194 tmppar = cursor.par()->previous();
1197 redoParagraphs(bview, selection.start, endpar);
1200 setCursor(bview, selection.start.par(), selection.start.pos());
1201 selection.cursor = cursor;
1202 setCursor(bview, selection.end.par(), selection.end.pos());
1203 setSelection(bview);
1204 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1206 bview->updateInset(inset_owner, true);
1210 // set the counter of a paragraph. This includes the labels
1211 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1213 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1214 LyXLayout_ptr const & layout = par->layout();
1216 if (par->previous()) {
1218 par->params().appendix(par->previous()->params().appendix());
1219 if (!par->params().appendix() && par->params().startOfAppendix()) {
1220 par->params().appendix(true);
1221 buf->counters().reset();
1223 par->enumdepth = par->previous()->enumdepth;
1224 par->itemdepth = par->previous()->itemdepth;
1226 par->params().appendix(par->params().startOfAppendix());
1231 /* Maybe we have to increment the enumeration depth.
1232 * BUT, enumeration in a footnote is considered in isolation from its
1233 * surrounding paragraph so don't increment if this is the
1234 * first line of the footnote
1235 * AND, bibliographies can't have their depth changed ie. they
1236 * are always of depth 0
1239 && par->previous()->getDepth() < par->getDepth()
1240 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1241 && par->enumdepth < 3
1242 && layout->labeltype != LABEL_BIBLIO) {
1246 // Maybe we have to decrement the enumeration depth, see note above
1248 && par->previous()->getDepth() > par->getDepth()
1249 && layout->labeltype != LABEL_BIBLIO) {
1250 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1253 if (!par->params().labelString().empty()) {
1254 par->params().labelString(string());
1257 if (layout->margintype == MARGIN_MANUAL) {
1258 if (par->params().labelWidthString().empty()) {
1259 par->setLabelWidthString(layout->labelstring());
1262 par->setLabelWidthString(string());
1265 // is it a layout that has an automatic label?
1266 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1268 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1269 string numbertype, langtype;
1272 if (i >= 0 && i <= buf->params.secnumdepth) {
1274 buf->counters().step(buf->counters().sects[i]);
1276 // Is there a label? Useful for Chapter layout
1277 if (!par->params().appendix()) {
1278 if (!layout->labelstring().empty())
1279 par->params().labelString(layout->labelstring());
1281 par->params().labelString(string());
1283 if (!layout->labelstring_appendix().empty())
1284 par->params().labelString(layout->labelstring_appendix());
1286 par->params().labelString(string());
1289 // Use if an integer is here less than elegant. For now.
1290 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1291 if (!par->params().appendix()) {
1292 numbertype = "sectioning";
1294 numbertype = "appendix";
1295 if (par->isRightToLeftPar(buf->params))
1296 langtype = "hebrew";
1301 s << buf->counters().numberLabel(buf->counters().sects[i],
1302 numbertype, langtype, head);
1304 par->params().labelString(par->params().labelString() + s.str().c_str());
1305 // We really want to remove the c_str as soon as
1308 // reset enum counters
1309 buf->counters().reset("enum");
1310 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1311 buf->counters().reset("enum");
1312 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1313 buf->counters().step(buf->counters().enums[par->enumdepth]);
1315 s << buf->counters().numberLabel(buf->counters().enums[par->enumdepth],
1316 "enumeration", langtype);
1317 par->params().labelString(s.str().c_str());
1320 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1321 buf->counters().step("bibitem");
1322 int number = buf->counters().value("bibitem");
1324 InsetCommandParams p("bibitem" );
1325 par->bibkey = new InsetBibKey(p);
1327 par->bibkey->setCounter(number);
1328 par->params().labelString(layout->labelstring());
1330 // In biblio should't be following counters but...
1332 string s = layout->labelstring();
1334 // the caption hack:
1335 if (layout->labeltype == LABEL_SENSITIVE) {
1336 Paragraph * tmppar = par;
1339 while (tmppar && tmppar->inInset()
1340 // the single '=' is intended below
1341 && (in = tmppar->inInset()->owner())) {
1342 if (in->lyxCode() == Inset::FLOAT_CODE) {
1346 tmppar = in->parOwner();
1352 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1354 buf->counters().step(fl.name());
1356 // Doesn't work... yet.
1358 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1359 o << fl.name() << " #:";
1362 // par->SetLayout(0);
1363 // s = layout->labelstring;
1364 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1365 ? " :úåòîùî øñç" : "Senseless: ";
1368 par->params().labelString(s);
1370 // reset the enumeration counter. They are always resetted
1371 // when there is any other layout between
1372 for (int i = par->enumdepth; i < 4; ++i) {
1373 buf->counters().set(buf->counters().enums[i], 0);
1379 // Updates all counters BEHIND the row. Changed paragraphs
1380 // with a dynamic left margin will be rebroken.
1381 void LyXText::updateCounters(BufferView * bview) const
1385 Row * row = firstrow;
1388 bview->buffer()->counters().reset();
1390 while (row->par() != par)
1393 setCounter(bview->buffer(), par);
1395 // now check for the headline layouts. remember that they
1396 // have a dynamic left margin
1397 LyXLayout_ptr const & layout = par->layout();
1399 if (layout->margintype == MARGIN_DYNAMIC
1400 || layout->labeltype == LABEL_SENSITIVE) {
1401 // Rebreak the paragraph
1402 removeParagraph(row);
1403 appendParagraph(bview, row);
1410 void LyXText::insertInset(BufferView * bview, Inset * inset)
1412 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1414 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1416 cursor.par()->insertInset(cursor.pos(), inset);
1417 // Just to rebreak and refresh correctly.
1418 // The character will not be inserted a second time
1419 insertChar(bview, Paragraph::META_INSET);
1420 // If we enter a highly editable inset the cursor should be to before
1421 // the inset. This couldn't happen before as Undo was not handled inside
1422 // inset now after the Undo LyX tries to call inset->Edit(...) again
1423 // and cannot do this as the cursor is behind the inset and GetInset
1424 // does not return the inset!
1425 if (isHighlyEditableInset(inset)) {
1426 cursorLeft(bview, true);
1432 void LyXText::copyEnvironmentType()
1434 copylayouttype = cursor.par()->layout()->name();
1438 void LyXText::pasteEnvironmentType(BufferView * bview)
1440 setLayout(bview, copylayouttype);
1444 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1446 // Stuff what we got on the clipboard. Even if there is no selection.
1448 // There is a problem with having the stuffing here in that the
1449 // larger the selection the slower LyX will get. This can be
1450 // solved by running the line below only when the selection has
1451 // finished. The solution used currently just works, to make it
1452 // faster we need to be more clever and probably also have more
1453 // calls to stuffClipboard. (Lgb)
1454 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1456 // This doesn't make sense, if there is no selection
1457 if (!selection.set())
1460 // OK, we have a selection. This is always between selection.start
1461 // and selection.end
1463 // make sure that the depth behind the selection are restored, too
1464 Paragraph * endpar = selection.end.par()->next();
1465 Paragraph * undoendpar = endpar;
1467 if (endpar && endpar->getDepth()) {
1468 while (endpar && endpar->getDepth()) {
1469 endpar = endpar->next();
1470 undoendpar = endpar;
1472 } else if (endpar) {
1473 endpar = endpar->next(); // because of parindents etc.
1476 setUndo(bview, Undo::DELETE,
1477 selection.start.par(), undoendpar);
1479 // there are two cases: cut only within one paragraph or
1480 // more than one paragraph
1481 if (selection.start.par() == selection.end.par()) {
1482 // only within one paragraph
1483 endpar = selection.end.par();
1484 int pos = selection.end.pos();
1485 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1486 selection.start.pos(), pos,
1487 bview->buffer()->params.textclass,
1489 selection.end.pos(pos);
1491 endpar = selection.end.par();
1492 int pos = selection.end.pos();
1493 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1494 selection.start.pos(), pos,
1495 bview->buffer()->params.textclass,
1498 selection.end.par(endpar);
1499 selection.end.pos(pos);
1500 cursor.pos(selection.end.pos());
1502 endpar = endpar->next();
1504 // sometimes necessary
1506 selection.start.par()->stripLeadingSpaces();
1508 redoParagraphs(bview, selection.start, endpar);
1510 // cutSelection can invalidate the cursor so we need to set
1512 cursor = selection.start;
1514 // need a valid cursor. (Lgb)
1517 setCursor(bview, cursor.par(), cursor.pos());
1518 selection.cursor = cursor;
1519 updateCounters(bview);
1523 void LyXText::copySelection(BufferView * bview)
1525 // stuff the selection onto the X clipboard, from an explicit copy request
1526 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1528 // this doesnt make sense, if there is no selection
1529 if (!selection.set())
1532 // ok we have a selection. This is always between selection.start
1533 // and sel_end cursor
1535 // copy behind a space if there is one
1536 while (selection.start.par()->size() > selection.start.pos()
1537 && selection.start.par()->isLineSeparator(selection.start.pos())
1538 && (selection.start.par() != selection.end.par()
1539 || selection.start.pos() < selection.end.pos()))
1540 selection.start.pos(selection.start.pos() + 1);
1542 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1543 selection.start.pos(), selection.end.pos(),
1544 bview->buffer()->params.textclass);
1548 void LyXText::pasteSelection(BufferView * bview)
1550 // this does not make sense, if there is nothing to paste
1551 if (!CutAndPaste::checkPastePossible(cursor.par()))
1554 setUndo(bview, Undo::INSERT,
1555 cursor.par(), cursor.par()->next());
1558 Paragraph * actpar = cursor.par();
1559 int pos = cursor.pos();
1561 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1562 bview->buffer()->params.textclass);
1564 redoParagraphs(bview, cursor, endpar);
1566 setCursor(bview, cursor.par(), cursor.pos());
1569 selection.cursor = cursor;
1570 setCursor(bview, actpar, pos);
1571 setSelection(bview);
1572 updateCounters(bview);
1576 // sets the selection over the number of characters of string, no check!!
1577 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1582 selection.cursor = cursor;
1583 for (string::size_type i = 0; i < str.length(); ++i)
1585 setSelection(bview);
1589 // simple replacing. The font of the first selected character is used
1590 void LyXText::replaceSelectionWithString(BufferView * bview,
1593 setCursorParUndo(bview);
1596 if (!selection.set()) { // create a dummy selection
1597 selection.end = cursor;
1598 selection.start = cursor;
1601 // Get font setting before we cut
1602 pos_type pos = selection.end.pos();
1603 LyXFont const font = selection.start.par()
1604 ->getFontSettings(bview->buffer()->params,
1605 selection.start.pos());
1607 // Insert the new string
1608 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1609 selection.end.par()->insertChar(pos, (*cit), font);
1613 // Cut the selection
1614 cutSelection(bview, true, false);
1620 // needed to insert the selection
1621 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1623 Paragraph * par = cursor.par();
1624 pos_type pos = cursor.pos();
1625 Paragraph * endpar = cursor.par()->next();
1627 setCursorParUndo(bview);
1629 // only to be sure, should not be neccessary
1632 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1634 redoParagraphs(bview, cursor, endpar);
1635 setCursor(bview, cursor.par(), cursor.pos());
1636 selection.cursor = cursor;
1637 setCursor(bview, par, pos);
1638 setSelection(bview);
1642 // turns double-CR to single CR, others where converted into one
1643 // blank. Then InsertStringAsLines is called
1644 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1646 string linestr(str);
1647 bool newline_inserted = false;
1648 for (string::size_type i = 0; i < linestr.length(); ++i) {
1649 if (linestr[i] == '\n') {
1650 if (newline_inserted) {
1651 // we know that \r will be ignored by
1652 // InsertStringA. Of course, it is a dirty
1653 // trick, but it works...
1654 linestr[i - 1] = '\r';
1658 newline_inserted = true;
1660 } else if (IsPrintable(linestr[i])) {
1661 newline_inserted = false;
1664 insertStringAsLines(bview, linestr);
1668 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1671 LyXCursor tmpcursor;
1675 Row * row = getRow(par, pos, y);
1677 // is there a break one row above
1678 if (row->previous() && row->previous()->par() == row->par()) {
1679 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1680 if (z >= row->pos()) {
1681 // set the dimensions of the row above
1682 y -= row->previous()->height();
1684 refresh_row = row->previous();
1685 status(bview, LyXText::NEED_MORE_REFRESH);
1687 breakAgain(bview, row->previous());
1689 // set the cursor again. Otherwise
1690 // dangling pointers are possible
1691 setCursor(bview, cursor.par(), cursor.pos(),
1692 false, cursor.boundary());
1693 selection.cursor = cursor;
1698 int const tmpheight = row->height();
1699 pos_type const tmplast = rowLast(row);
1703 breakAgain(bview, row);
1704 if (row->height() == tmpheight && rowLast(row) == tmplast)
1705 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1707 status(bview, LyXText::NEED_MORE_REFRESH);
1709 // check the special right address boxes
1710 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1717 redoDrawingOfParagraph(bview, tmpcursor);
1720 // set the cursor again. Otherwise dangling pointers are possible
1721 // also set the selection
1723 if (selection.set()) {
1725 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1726 false, selection.cursor.boundary());
1727 selection.cursor = cursor;
1728 setCursorIntern(bview, selection.start.par(),
1729 selection.start.pos(),
1730 false, selection.start.boundary());
1731 selection.start = cursor;
1732 setCursorIntern(bview, selection.end.par(),
1733 selection.end.pos(),
1734 false, selection.end.boundary());
1735 selection.end = cursor;
1736 setCursorIntern(bview, last_sel_cursor.par(),
1737 last_sel_cursor.pos(),
1738 false, last_sel_cursor.boundary());
1739 last_sel_cursor = cursor;
1742 setCursorIntern(bview, cursor.par(), cursor.pos(),
1743 false, cursor.boundary());
1747 // returns false if inset wasn't found
1748 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1750 // first check the current paragraph
1751 int pos = cursor.par()->getPositionOfInset(inset);
1753 checkParagraph(bview, cursor.par(), pos);
1757 // check every paragraph
1759 Paragraph * par = ownerParagraph();
1761 pos = par->getPositionOfInset(inset);
1763 checkParagraph(bview, par, pos);
1773 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1775 bool setfont, bool boundary) const
1777 LyXCursor old_cursor = cursor;
1778 setCursorIntern(bview, par, pos, setfont, boundary);
1779 return deleteEmptyParagraphMechanism(bview, old_cursor);
1783 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1784 pos_type pos, bool boundary) const
1791 cur.boundary(boundary);
1793 // get the cursor y position in text
1795 Row * row = getRow(par, pos, y);
1796 Row * old_row = row;
1798 // if we are before the first char of this row and are still in the
1799 // same paragraph and there is a previous row then put the cursor on
1800 // the end of the previous row
1801 cur.iy(y + row->baseline());
1803 if (row->previous() && pos &&
1804 row->previous()->par() == row->par() &&
1805 par->getChar(pos) == Paragraph::META_INSET &&
1806 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1808 row = row->previous();
1813 // y is now the beginning of the cursor row
1814 y += row->baseline();
1815 // y is now the cursor baseline
1818 pos_type last = rowLastPrintable(old_row);
1820 if (pos > last + 1) {
1821 // This shouldn't happen.
1824 } else if (pos < row->pos()) {
1829 // now get the cursors x position
1830 float x = getCursorX(bview, row, pos, last, boundary);
1833 if (old_row != row) {
1834 x = getCursorX(bview, old_row, pos, last, boundary);
1841 float LyXText::getCursorX(BufferView * bview, Row * row,
1842 pos_type pos, pos_type last, bool boundary) const
1844 pos_type cursor_vpos = 0;
1846 float fill_separator;
1848 float fill_label_hfill;
1849 // This call HAS to be here because of the BidiTables!!!
1850 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1853 if (last < row->pos())
1854 cursor_vpos = row->pos();
1855 else if (pos > last && !boundary)
1856 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1857 ? row->pos() : last + 1;
1858 else if (pos > row->pos() &&
1859 (pos > last || boundary))
1860 /// Place cursor after char at (logical) position pos - 1
1861 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1862 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1864 /// Place cursor before char at (logical) position pos
1865 cursor_vpos = (bidi_level(pos) % 2 == 0)
1866 ? log2vis(pos) : log2vis(pos) + 1;
1868 pos_type main_body =
1869 beginningOfMainBody(bview->buffer(), row->par());
1870 if ((main_body > 0) &&
1871 ((main_body-1 > last) ||
1872 !row->par()->isLineSeparator(main_body-1)))
1875 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1876 pos_type pos = vis2log(vpos);
1877 if (main_body > 0 && pos == main_body - 1) {
1878 x += fill_label_hfill +
1879 font_metrics::width(
1880 row->par()->layout()->labelsep,
1881 getLabelFont(bview->buffer(),
1883 if (row->par()->isLineSeparator(main_body - 1))
1884 x -= singleWidth(bview,
1885 row->par(), main_body - 1);
1887 if (hfillExpansion(bview->buffer(), row, pos)) {
1888 x += singleWidth(bview, row->par(), pos);
1889 if (pos >= main_body)
1892 x += fill_label_hfill;
1893 } else if (row->par()->isSeparator(pos)) {
1894 x += singleWidth(bview, row->par(), pos);
1895 if (pos >= main_body)
1896 x += fill_separator;
1898 x += singleWidth(bview, row->par(), pos);
1904 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1905 pos_type pos, bool setfont, bool boundary) const
1907 InsetText * it = static_cast<InsetText *>(par->inInset());
1909 if (it != inset_owner) {
1910 lyxerr[Debug::INSETS] << "InsetText is " << it
1912 << "inset_owner is "
1913 << inset_owner << endl;
1914 #ifdef WITH_WARNINGS
1915 #warning I believe this code is wrong. (Lgb)
1916 #warning Jürgen, have a look at this. (Lgb)
1917 #warning Hmmm, I guess you are right but we
1918 #warning should verify when this is needed
1920 // Jürgen, would you like to have a look?
1921 // I guess we need to move the outer cursor
1922 // and open and lock the inset (bla bla bla)
1923 // stuff I don't know... so can you have a look?
1925 // I moved the lyxerr stuff in here so we can see if
1926 // this is actually really needed and where!
1928 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1933 setCursor(bview, cursor, par, pos, boundary);
1935 setCurrentFont(bview);
1939 void LyXText::setCurrentFont(BufferView * bview) const
1941 pos_type pos = cursor.pos();
1942 if (cursor.boundary() && pos > 0)
1946 if (pos == cursor.par()->size())
1948 else // potentional bug... BUG (Lgb)
1949 if (cursor.par()->isSeparator(pos)) {
1950 if (pos > cursor.row()->pos() &&
1951 bidi_level(pos) % 2 ==
1952 bidi_level(pos - 1) % 2)
1954 else if (pos + 1 < cursor.par()->size())
1960 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1961 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1963 if (cursor.pos() == cursor.par()->size() &&
1964 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1965 !cursor.boundary()) {
1966 Language const * lang =
1967 cursor.par()->getParLanguage(bview->buffer()->params);
1968 current_font.setLanguage(lang);
1969 current_font.setNumber(LyXFont::OFF);
1970 real_current_font.setLanguage(lang);
1971 real_current_font.setNumber(LyXFont::OFF);
1976 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1978 LyXCursor old_cursor = cursor;
1980 setCursorFromCoordinates(bview, cursor, x, y);
1981 setCurrentFont(bview);
1982 deleteEmptyParagraphMechanism(bview, old_cursor);
1989 * return true if the cursor given is at the end of a row,
1990 * and the next row is filled by an inset that spans an entire
1993 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1996 Row const & next = *row.next();
1998 if (next.pos() != cur.pos() || next.par() != cur.par())
2000 if (!cur.par()->isInset(cur.pos()))
2002 Inset const * inset = cur.par()->getInset(cur.pos());
2003 if (inset->needFullRow() || inset->display())
2010 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2013 // Get the row first.
2015 Row * row = getRowNearY(y);
2017 pos_type const column = getColumnNearX(bview, row, x, bound);
2018 cur.par(row->par());
2019 cur.pos(row->pos() + column);
2021 cur.y(y + row->baseline());
2024 if (beforeFullRowInset(*row, cur)) {
2025 pos_type last = rowLastPrintable(row);
2026 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2028 cur.iy(y + row->height() + row->next()->baseline());
2029 cur.irow(row->next());
2035 cur.boundary(bound);
2039 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2041 if (cursor.pos() > 0) {
2042 bool boundary = cursor.boundary();
2043 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2044 if (!internal && !boundary &&
2045 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2046 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2047 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2048 Paragraph * par = cursor.par()->previous();
2049 setCursor(bview, par, par->size());
2054 void LyXText::cursorRight(BufferView * bview, bool internal) const
2056 if (!internal && cursor.boundary() &&
2057 !cursor.par()->isNewline(cursor.pos()))
2058 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2059 else if (cursor.pos() < cursor.par()->size()) {
2060 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2062 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2063 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2064 } else if (cursor.par()->next())
2065 setCursor(bview, cursor.par()->next(), 0);
2069 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2072 int x = cursor.x_fix();
2073 int y = cursor.y() - cursor.row()->baseline() - 1;
2074 setCursorFromCoordinates(bview, x, y);
2076 int y1 = cursor.iy() - first_y;
2079 Inset * inset_hit = checkInsetHit(bview, x, y1);
2080 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2081 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2085 setCursorFromCoordinates(bview, cursor.x_fix(),
2086 cursor.y() - cursor.row()->baseline() - 1);
2091 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2094 int x = cursor.x_fix();
2095 int y = cursor.y() - cursor.row()->baseline() +
2096 cursor.row()->height() + 1;
2097 setCursorFromCoordinates(bview, x, y);
2098 if (!selecting && cursor.row() == cursor.irow()) {
2099 int y1 = cursor.iy() - first_y;
2102 Inset * inset_hit = checkInsetHit(bview, x, y1);
2103 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2104 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2108 setCursorFromCoordinates(bview, cursor.x_fix(),
2109 cursor.y() - cursor.row()->baseline()
2110 + cursor.row()->height() + 1);
2115 void LyXText::cursorUpParagraph(BufferView * bview) const
2117 if (cursor.pos() > 0) {
2118 setCursor(bview, cursor.par(), 0);
2120 else if (cursor.par()->previous()) {
2121 setCursor(bview, cursor.par()->previous(), 0);
2126 void LyXText::cursorDownParagraph(BufferView * bview) const
2128 if (cursor.par()->next()) {
2129 setCursor(bview, cursor.par()->next(), 0);
2131 setCursor(bview, cursor.par(), cursor.par()->size());
2135 // fix the cursor `cur' after a characters has been deleted at `where'
2136 // position. Called by deleteEmptyParagraphMechanism
2137 void LyXText::fixCursorAfterDelete(BufferView * bview,
2139 LyXCursor const & where) const
2141 // if cursor is not in the paragraph where the delete occured,
2143 if (cur.par() != where.par())
2146 // if cursor position is after the place where the delete occured,
2148 if (cur.pos() > where.pos())
2149 cur.pos(cur.pos()-1);
2151 // check also if we don't want to set the cursor on a spot behind the
2152 // pagragraph because we erased the last character.
2153 if (cur.pos() > cur.par()->size())
2154 cur.pos(cur.par()->size());
2156 // recompute row et al. for this cursor
2157 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2161 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2162 LyXCursor const & old_cursor) const
2164 // Would be wrong to delete anything if we have a selection.
2165 if (selection.set())
2168 // We allow all kinds of "mumbo-jumbo" when freespacing.
2169 if (old_cursor.par()->layout()->free_spacing
2170 || old_cursor.par()->isFreeSpacing()) {
2174 /* Ok I'll put some comments here about what is missing.
2175 I have fixed BackSpace (and thus Delete) to not delete
2176 double-spaces automagically. I have also changed Cut,
2177 Copy and Paste to hopefully do some sensible things.
2178 There are still some small problems that can lead to
2179 double spaces stored in the document file or space at
2180 the beginning of paragraphs. This happens if you have
2181 the cursor betwenn to spaces and then save. Or if you
2182 cut and paste and the selection have a space at the
2183 beginning and then save right after the paste. I am
2184 sure none of these are very hard to fix, but I will
2185 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2186 that I can get some feedback. (Lgb)
2189 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2190 // delete the LineSeparator.
2193 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2194 // delete the LineSeparator.
2197 // If the pos around the old_cursor were spaces, delete one of them.
2198 if (old_cursor.par() != cursor.par()
2199 || old_cursor.pos() != cursor.pos()) {
2200 // Only if the cursor has really moved
2202 if (old_cursor.pos() > 0
2203 && old_cursor.pos() < old_cursor.par()->size()
2204 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2205 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2206 old_cursor.par()->erase(old_cursor.pos() - 1);
2207 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2209 #ifdef WITH_WARNINGS
2210 #warning This will not work anymore when we have multiple views of the same buffer
2211 // In this case, we will have to correct also the cursors held by
2212 // other bufferviews. It will probably be easier to do that in a more
2213 // automated way in LyXCursor code. (JMarc 26/09/2001)
2215 // correct all cursors held by the LyXText
2216 fixCursorAfterDelete(bview, cursor, old_cursor);
2217 fixCursorAfterDelete(bview, selection.cursor,
2219 fixCursorAfterDelete(bview, selection.start,
2221 fixCursorAfterDelete(bview, selection.end, old_cursor);
2222 fixCursorAfterDelete(bview, last_sel_cursor,
2224 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2225 fixCursorAfterDelete(bview, toggle_end_cursor,
2231 // don't delete anything if this is the ONLY paragraph!
2232 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2235 // Do not delete empty paragraphs with keepempty set.
2236 if (old_cursor.par()->layout()->keepempty)
2239 // only do our magic if we changed paragraph
2240 if (old_cursor.par() == cursor.par())
2243 // record if we have deleted a paragraph
2244 // we can't possibly have deleted a paragraph before this point
2245 bool deleted = false;
2247 if ((old_cursor.par()->empty()
2248 || (old_cursor.par()->size() == 1
2249 && old_cursor.par()->isLineSeparator(0)))) {
2250 // ok, we will delete anything
2251 LyXCursor tmpcursor;
2253 // make sure that you do not delete any environments
2254 status(bview, LyXText::NEED_MORE_REFRESH);
2257 if (old_cursor.row()->previous()) {
2258 refresh_row = old_cursor.row()->previous();
2259 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2261 cursor = old_cursor; // that undo can restore the right cursor position
2262 Paragraph * endpar = old_cursor.par()->next();
2263 if (endpar && endpar->getDepth()) {
2264 while (endpar && endpar->getDepth()) {
2265 endpar = endpar->next();
2268 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2272 removeRow(old_cursor.row());
2273 if (ownerParagraph() == old_cursor.par()) {
2274 ownerParagraph(ownerParagraph()->next());
2277 delete old_cursor.par();
2279 /* Breakagain the next par. Needed because of
2280 * the parindent that can occur or dissappear.
2281 * The next row can change its height, if
2282 * there is another layout before */
2283 if (refresh_row->next()) {
2284 breakAgain(bview, refresh_row->next());
2285 updateCounters(bview);
2287 setHeightOfRow(bview, refresh_row);
2289 refresh_row = old_cursor.row()->next();
2290 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2293 cursor = old_cursor; // that undo can restore the right cursor position
2294 Paragraph * endpar = old_cursor.par()->next();
2295 if (endpar && endpar->getDepth()) {
2296 while (endpar && endpar->getDepth()) {
2297 endpar = endpar->next();
2300 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2304 removeRow(old_cursor.row());
2306 if (ownerParagraph() == old_cursor.par()) {
2307 ownerParagraph(ownerParagraph()->next());
2310 delete old_cursor.par();
2312 /* Breakagain the next par. Needed because of
2313 the parindent that can occur or dissappear.
2314 The next row can change its height, if
2315 there is another layout before */
2317 breakAgain(bview, refresh_row);
2318 updateCounters(bview);
2323 setCursorIntern(bview, cursor.par(), cursor.pos());
2325 if (selection.cursor.par() == old_cursor.par()
2326 && selection.cursor.pos() == old_cursor.pos()) {
2327 // correct selection
2328 selection.cursor = cursor;
2332 if (old_cursor.par()->stripLeadingSpaces()) {
2333 redoParagraphs(bview, old_cursor,
2334 old_cursor.par()->next());
2336 setCursorIntern(bview, cursor.par(), cursor.pos());
2337 selection.cursor = cursor;
2344 Paragraph * LyXText::ownerParagraph() const
2347 return inset_owner->paragraph();
2349 return &*(bv_owner->buffer()->paragraphs.begin());
2353 void LyXText::ownerParagraph(Paragraph * p) const
2356 inset_owner->paragraph(p);
2358 bv_owner->buffer()->paragraphs.set(p);
2363 void LyXText::ownerParagraph(int id, Paragraph * p) const
2365 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2366 if (op && op->inInset()) {
2367 static_cast<InsetText *>(op->inInset())->paragraph(p);
2374 LyXText::text_status LyXText::status() const
2380 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2382 LyXText * t = bview->text;
2384 // We should only go up with refreshing code so this means that if
2385 // we have a MORE refresh we should never set it to LITTLE if we still
2386 // didn't handle it (and then it will be UNCHANGED. Now as long as
2387 // we stay inside one LyXText this may work but we need to tell the
2388 // outermost LyXText that it should REALLY draw us if there is some
2389 // change in a Inset::LyXText. So you see that when we are inside a
2390 // inset's LyXText we give the LITTLE to the outermost LyXText to
2391 // tell'em that it should redraw the actual row (where the inset
2392 // resides! Capito?!
2394 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2396 if (inset_owner && st != UNCHANGED) {
2397 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2398 if (!t->refresh_row) {
2399 t->refresh_row = t->cursor.row();
2400 t->refresh_y = t->cursor.y() -
2401 t->cursor.row()->baseline();