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 "lyxtextclasslist.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
28 #include "CutAndPaste.h"
34 #include "FloatList.h"
36 #include "ParagraphParameters.h"
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
66 LyXText::LyXText(InsetText * inset)
67 : number_of_rows(0), height(0), width(0), first(0),
68 bv_owner(0), inset_owner(inset), the_locking_inset(0),
69 need_break_row(0), refresh_y(0), refresh_row(0),
70 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
74 void LyXText::init(BufferView * bview, bool reinit)
77 // Delete all rows, this does not touch the paragraphs!
78 Row * tmprow = firstrow;
80 tmprow = firstrow->next();
84 lastrow = refresh_row = need_break_row = 0;
85 width = height = copylayouttype = 0;
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font);
132 tmpfont.realize(textclasslist.
133 Style(buf->params.textclass,
134 par->getLayout()).font,
135 buf->params.language);
137 par_depth = par->getDepth();
141 #ifndef INHERIT_LANGUAGE
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
144 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
145 buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout const & layout =
167 textclasslist.Style(buf->params.textclass, par->getLayout());
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout.labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params, pos);
177 par->inInset()->getDrawFont(f);
178 #ifndef INHERIT_LANGUAGE
179 return f.realize(layout.reslabelfont);
181 return f.realize(layout.reslabelfont, buf->params.language);
184 LyXFont f = par->getFontSettings(buf->params, pos);
186 par->inInset()->getDrawFont(f);
187 #ifndef INHERIT_LANGUAGE
188 return f.realize(layout.resfont);
190 return f.realize(layout.resfont, buf->params.language);
195 // The uncommon case need not be optimized as much
199 if (pos < beginningOfMainBody(buf, par)) {
201 layoutfont = layout.labelfont;
204 layoutfont = layout.font;
207 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
208 #ifndef INHERIT_LANGUAGE
209 tmpfont.realize(layoutfont);
211 tmpfont.realize(layoutfont, buf->params.language);
214 par->inInset()->getDrawFont(tmpfont);
216 return realizeFont(tmpfont, buf, par);
220 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
222 LyXLayout const & layout =
223 textclasslist.Style(buf->params.textclass, par->getLayout());
225 Paragraph::depth_type par_depth = par->getDepth();
228 return layout.resfont;
231 return realizeFont(layout.font, buf, par);
235 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
237 LyXLayout const & layout =
238 textclasslist.Style(buf->params.textclass, par->getLayout());
240 Paragraph::depth_type par_depth = par->getDepth();
243 return layout.reslabelfont;
246 return realizeFont(layout.labelfont, buf, par);
250 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
251 pos_type pos, LyXFont const & fnt,
254 Buffer const * buf = bv->buffer();
255 LyXFont font = getFont(buf, par, pos);
256 font.update(fnt, buf->params.language, toggleall);
257 // Let the insets convert their font
258 if (par->isInset(pos)) {
259 Inset * inset = par->getInset(pos);
260 if (isEditableInset(inset)) {
261 UpdatableInset * uinset =
262 static_cast<UpdatableInset *>(inset);
263 uinset->setFont(bv, fnt, toggleall, true);
267 LyXLayout const & layout =
268 textclasslist.Style(buf->params.textclass,
271 // Get concrete layout font to reduce against
274 if (pos < beginningOfMainBody(buf, par))
275 layoutfont = layout.labelfont;
277 layoutfont = layout.font;
279 // Realize against environment font information
280 if (par->getDepth()) {
281 Paragraph * tp = par;
282 while (!layoutfont.resolved() && tp && tp->getDepth()) {
283 tp = tp->outerHook();
285 #ifndef INHERIT_LANGUAGE
286 layoutfont.realize(textclasslist.
287 Style(buf->params.textclass,
288 tp->getLayout()).font);
290 layoutfont.realize(textclasslist.
291 Style(buf->params.textclass,
292 tp->getLayout()).font,
293 buf->params.language);
298 #ifndef INHERIT_LANGUAGE
299 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
301 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
302 buf->params.language);
305 // Now, reduce font against full layout font
306 font.reduce(layoutfont);
308 par->setFont(pos, font);
312 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
313 pos_type pos, LyXFont const & fnt)
317 LyXLayout const & layout =
318 textclasslist.Style(buf->params.textclass,
321 // Get concrete layout font to reduce against
324 if (pos < beginningOfMainBody(buf, par))
325 layoutfont = layout.labelfont;
327 layoutfont = layout.font;
329 // Realize against environment font information
330 if (par->getDepth()) {
331 Paragraph * tp = par;
332 while (!layoutfont.resolved() && tp && tp->getDepth()) {
333 tp = tp->outerHook();
335 #ifndef INHERIT_LANGUAGE
336 layoutfont.realize(textclasslist.
337 Style(buf->params.textclass,
338 tp->getLayout()).font);
340 layoutfont.realize(textclasslist.
341 Style(buf->params.textclass,
342 tp->getLayout()).font,
343 buf->params.language);
348 #ifndef INHERIT_LANGUAGE
349 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
351 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
352 buf->params.language);
355 // Now, reduce font against full layout font
356 font.reduce(layoutfont);
358 par->setFont(pos, font);
362 // inserts a new row behind the specified row, increments
363 // the touched counters
364 void LyXText::insertRow(Row * row, Paragraph * par,
367 Row * tmprow = new Row;
370 tmprow->next(firstrow);
373 tmprow->previous(row);
374 tmprow->next(row->next());
379 tmprow->next()->previous(tmprow);
381 if (tmprow->previous())
382 tmprow->previous()->next(tmprow);
394 // removes the row and reset the touched counters
395 void LyXText::removeRow(Row * row) const
397 Row * row_prev = row->previous();
399 row->next()->previous(row_prev);
401 firstrow = row->next();
402 // lyx::Assert(firstrow);
404 row_prev->next(row->next());
406 if (row == lastrow) {
407 lyx::Assert(!row->next());
410 if (refresh_row == row) {
411 refresh_row = row_prev ? row_prev : row->next();
412 // what about refresh_y, refresh_height
415 height -= row->height(); // the text becomes smaller
418 --number_of_rows; // one row less
422 // remove all following rows of the paragraph of the specified row.
423 void LyXText::removeParagraph(Row * row) const
425 Paragraph * tmppar = row->par();
429 while (row && row->par() == tmppar) {
430 tmprow = row->next();
437 // insert the specified paragraph behind the specified row
438 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
441 insertRow(row, par, 0); /* insert a new row, starting
444 setCounter(bview->buffer(), par); // set the counters
446 // and now append the whole paragraph behind the new row
449 appendParagraph(bview, firstrow);
451 row->next()->height(0);
452 appendParagraph(bview, row->next());
457 Inset * LyXText::getInset() const
460 if (cursor.pos() == 0 && cursor.par()->bibkey) {
461 inset = cursor.par()->bibkey;
462 } else if (cursor.pos() < cursor.par()->size()
463 && cursor.par()->isInset(cursor.pos())) {
464 inset = cursor.par()->getInset(cursor.pos());
470 void LyXText::toggleInset(BufferView * bview)
472 Inset * inset = getInset();
473 // is there an editable inset at cursor position?
474 if (!isEditableInset(inset)) {
475 // No, try to see if we are inside a collapsable inset
476 if (inset_owner && inset_owner->owner()
477 && inset_owner->owner()->isOpen()) {
478 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
479 inset_owner->owner()->close(bview);
483 //bview->owner()->message(inset->editMessage());
485 // do we want to keep this?? (JMarc)
486 if (!isHighlyEditableInset(inset))
487 setCursorParUndo(bview);
489 if (inset->isOpen()) {
495 inset->open(bview, !inset->isOpen());
500 /* used in setlayout */
501 // Asger is not sure we want to do this...
502 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
505 LyXLayout const & layout =
506 textclasslist.Style(buf->params.textclass, par->getLayout());
509 for (pos_type pos = 0; pos < par->size(); ++pos) {
510 if (pos < beginningOfMainBody(buf, par))
511 layoutfont = layout.labelfont;
513 layoutfont = layout.font;
515 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
516 tmpfont.reduce(layoutfont);
517 par->setFont(pos, tmpfont);
522 Paragraph * LyXText::setLayout(BufferView * bview,
523 LyXCursor & cur, LyXCursor & sstart_cur,
524 LyXCursor & send_cur,
525 lyx::layout_type layout)
527 Paragraph * endpar = send_cur.par()->next();
528 Paragraph * undoendpar = endpar;
530 if (endpar && endpar->getDepth()) {
531 while (endpar && endpar->getDepth()) {
532 endpar = endpar->next();
536 endpar = endpar->next(); // because of parindents etc.
539 setUndo(bview, Undo::EDIT,
540 sstart_cur.par(), undoendpar);
542 // ok we have a selection. This is always between sstart_cur
543 // and sel_end cursor
546 LyXLayout const & lyxlayout =
547 textclasslist.Style(bview->buffer()->params.textclass, layout);
550 cur.par()->setLayout(layout);
551 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
552 Paragraph * fppar = cur.par();
553 fppar->params().spaceTop(lyxlayout.fill_top ?
554 VSpace(VSpace::VFILL)
555 : VSpace(VSpace::NONE));
556 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
557 VSpace(VSpace::VFILL)
558 : VSpace(VSpace::NONE));
559 if (lyxlayout.margintype == MARGIN_MANUAL)
560 cur.par()->setLabelWidthString(lyxlayout.labelstring());
561 if (lyxlayout.labeltype != LABEL_BIBLIO
563 delete fppar->bibkey;
566 if (cur.par() != send_cur.par())
567 cur.par(cur.par()->next());
568 } while (cur.par() != send_cur.par());
574 // set layout over selection and make a total rebreak of those paragraphs
575 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
577 LyXCursor tmpcursor = cursor; /* store the current cursor */
579 // if there is no selection just set the layout
580 // of the current paragraph */
581 if (!selection.set()) {
582 selection.start = cursor; // dummy selection
583 selection.end = cursor;
585 Paragraph * endpar = setLayout(bview, cursor, selection.start,
586 selection.end, layout);
587 redoParagraphs(bview, selection.start, endpar);
589 // we have to reset the selection, because the
590 // geometry could have changed
591 setCursor(bview, selection.start.par(),
592 selection.start.pos(), false);
593 selection.cursor = cursor;
594 setCursor(bview, selection.end.par(), selection.end.pos(), false);
595 updateCounters(bview, cursor.row());
598 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
602 // increment depth over selection and
603 // make a total rebreak of those paragraphs
604 void LyXText::incDepth(BufferView * bview)
606 // If there is no selection, just use the current paragraph
607 if (!selection.set()) {
608 selection.start = cursor; // dummy selection
609 selection.end = cursor;
612 // We end at the next paragraph with depth 0
613 Paragraph * endpar = selection.end.par()->next();
615 Paragraph * undoendpar = endpar;
617 if (endpar && endpar->getDepth()) {
618 while (endpar && endpar->getDepth()) {
619 endpar = endpar->next();
623 endpar = endpar->next(); // because of parindents etc.
626 setUndo(bview, Undo::EDIT,
627 selection.start.par(), undoendpar);
629 LyXCursor tmpcursor = cursor; // store the current cursor
631 // ok we have a selection. This is always between sel_start_cursor
632 // and sel_end cursor
633 cursor = selection.start;
635 bool anything_changed = false;
638 // NOTE: you can't change the depth of a bibliography entry
639 if (textclasslist.Style(bview->buffer()->params.textclass,
640 cursor.par()->getLayout()).labeltype != LABEL_BIBLIO) {
641 Paragraph * prev = cursor.par()->previous();
644 if (cursor.par()->getDepth()
645 < prev->getMaxDepthAfter(bview->buffer())){
646 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
647 anything_changed = true;
651 if (cursor.par() == selection.end.par())
653 cursor.par(cursor.par()->next());
656 // if nothing changed set all depth to 0
657 if (!anything_changed) {
658 cursor = selection.start;
659 while (cursor.par() != selection.end.par()) {
660 cursor.par()->params().depth(0);
661 cursor.par(cursor.par()->next());
663 cursor.par()->params().depth(0);
666 redoParagraphs(bview, selection.start, endpar);
668 // we have to reset the selection, because the
669 // geometry could have changed
670 setCursor(bview, selection.start.par(), selection.start.pos());
671 selection.cursor = cursor;
672 setCursor(bview, selection.end.par(), selection.end.pos());
673 updateCounters(bview, cursor.row());
676 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
680 // decrement depth over selection and
681 // make a total rebreak of those paragraphs
682 void LyXText::decDepth(BufferView * bview)
684 // if there is no selection just set the layout
685 // of the current paragraph
686 if (!selection.set()) {
687 selection.start = cursor; // dummy selection
688 selection.end = cursor;
690 Paragraph * endpar = selection.end.par()->next();
691 Paragraph * undoendpar = endpar;
693 if (endpar && endpar->getDepth()) {
694 while (endpar && endpar->getDepth()) {
695 endpar = endpar->next();
699 endpar = endpar->next(); // because of parindents etc.
702 setUndo(bview, Undo::EDIT,
703 selection.start.par(), undoendpar);
705 LyXCursor tmpcursor = cursor; // store the current cursor
707 // ok we have a selection. This is always between sel_start_cursor
708 // and sel_end cursor
709 cursor = selection.start;
712 if (cursor.par()->params().depth()) {
713 cursor.par()->params()
714 .depth(cursor.par()->params().depth() - 1);
716 if (cursor.par() == selection.end.par()) {
719 cursor.par(cursor.par()->next());
722 redoParagraphs(bview, selection.start, endpar);
724 // we have to reset the selection, because the
725 // geometry could have changed
726 setCursor(bview, selection.start.par(),
727 selection.start.pos());
728 selection.cursor = cursor;
729 setCursor(bview, selection.end.par(), selection.end.pos());
730 updateCounters(bview, cursor.row());
733 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
737 // set font over selection and make a total rebreak of those paragraphs
738 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
740 // if there is no selection just set the current_font
741 if (!selection.set()) {
742 // Determine basis font
744 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
746 layoutfont = getLabelFont(bview->buffer(),
749 layoutfont = getLayoutFont(bview->buffer(),
752 // Update current font
753 real_current_font.update(font,
754 bview->buffer()->params.language,
757 // Reduce to implicit settings
758 current_font = real_current_font;
759 current_font.reduce(layoutfont);
760 // And resolve it completely
761 #ifndef INHERIT_LANGUAGE
762 real_current_font.realize(layoutfont);
764 real_current_font.realize(layoutfont,
765 bview->buffer()->params.language);
770 LyXCursor tmpcursor = cursor; // store the current cursor
772 // ok we have a selection. This is always between sel_start_cursor
773 // and sel_end cursor
775 setUndo(bview, Undo::EDIT,
776 selection.start.par(), selection.end.par()->next());
778 cursor = selection.start;
779 while (cursor.par() != selection.end.par() ||
780 (cursor.pos() < selection.end.pos()))
782 if (cursor.pos() < cursor.par()->size()) {
783 // an open footnote should behave
785 setCharFont(bview, cursor.par(), cursor.pos(),
787 cursor.pos(cursor.pos() + 1);
790 cursor.par(cursor.par()->next());
795 redoParagraphs(bview, selection.start, selection.end.par()->next());
797 // we have to reset the selection, because the
798 // geometry could have changed, but we keep
799 // it for user convenience
800 setCursor(bview, selection.start.par(), selection.start.pos());
801 selection.cursor = cursor;
802 setCursor(bview, selection.end.par(), selection.end.pos());
804 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
805 tmpcursor.boundary());
809 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
811 Row * tmprow = cur.row();
812 int y = cur.y() - tmprow->baseline();
814 setHeightOfRow(bview, tmprow);
816 while (tmprow->previous()
817 && tmprow->previous()->par() == tmprow->par()) {
818 tmprow = tmprow->previous();
819 y -= tmprow->height();
820 setHeightOfRow(bview, tmprow);
823 // we can set the refreshing parameters now
824 status(bview, LyXText::NEED_MORE_REFRESH);
826 refresh_row = tmprow;
827 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
831 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
833 Row * tmprow = cur.row();
835 int y = cur.y() - tmprow->baseline();
836 setHeightOfRow(bview, tmprow);
838 while (tmprow->previous()
839 && tmprow->previous()->par() == tmprow->par()) {
840 tmprow = tmprow->previous();
841 y -= tmprow->height();
844 // we can set the refreshing parameters now
845 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
847 refresh_row = tmprow;
849 status(bview, LyXText::NEED_MORE_REFRESH);
850 setCursor(bview, cur.par(), cur.pos());
854 // deletes and inserts again all paragaphs between the cursor
855 // and the specified par
856 // This function is needed after SetLayout and SetFont etc.
857 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
858 Paragraph const * endpar) const
861 Paragraph * tmppar = 0;
862 Paragraph * first_phys_par = 0;
864 Row * tmprow = cur.row();
866 int y = cur.y() - tmprow->baseline();
868 if (!tmprow->previous()) {
869 // a trick/hack for UNDO
870 // This is needed because in an UNDO/REDO we could have changed
871 // the ownerParagrah() so the paragraph inside the row is NOT
872 // my really first par anymore. Got it Lars ;) (Jug 20011206)
873 first_phys_par = ownerParagraph();
875 first_phys_par = tmprow->par();
876 while (tmprow->previous()
877 && tmprow->previous()->par() == first_phys_par)
879 tmprow = tmprow->previous();
880 y -= tmprow->height();
884 // we can set the refreshing parameters now
885 status(bview, LyXText::NEED_MORE_REFRESH);
887 refresh_row = tmprow->previous(); /* the real refresh row will
888 be deleted, so I store
892 tmppar = tmprow->next()->par();
895 while (tmprow->next() && tmppar != endpar) {
896 removeRow(tmprow->next());
897 if (tmprow->next()) {
898 tmppar = tmprow->next()->par();
904 // remove the first one
905 tmprow2 = tmprow; /* this is because tmprow->previous()
907 tmprow = tmprow->previous();
910 tmppar = first_phys_par;
914 insertParagraph(bview, tmppar, tmprow);
918 while (tmprow->next()
919 && tmprow->next()->par() == tmppar) {
920 tmprow = tmprow->next();
922 tmppar = tmppar->next();
924 } while (tmppar && tmppar != endpar);
926 // this is because of layout changes
928 refresh_y -= refresh_row->height();
929 setHeightOfRow(bview, refresh_row);
931 refresh_row = firstrow;
933 setHeightOfRow(bview, refresh_row);
936 if (tmprow && tmprow->next())
937 setHeightOfRow(bview, tmprow->next());
941 bool LyXText::fullRebreak(BufferView * bview)
947 if (need_break_row) {
948 breakAgain(bview, need_break_row);
956 // important for the screen
959 /* the cursor set functions have a special mechanism. When they
960 * realize, that you left an empty paragraph, they will delete it.
961 * They also delete the corresponding row */
963 // need the selection cursor:
964 void LyXText::setSelection(BufferView * bview)
966 bool const lsel = selection.set();
968 if (!selection.set()) {
969 last_sel_cursor = selection.cursor;
970 selection.start = selection.cursor;
971 selection.end = selection.cursor;
976 // first the toggling area
977 if (cursor.y() < last_sel_cursor.y()
978 || (cursor.y() == last_sel_cursor.y()
979 && cursor.x() < last_sel_cursor.x())) {
980 toggle_end_cursor = last_sel_cursor;
981 toggle_cursor = cursor;
983 toggle_end_cursor = cursor;
984 toggle_cursor = last_sel_cursor;
987 last_sel_cursor = cursor;
989 // and now the whole selection
991 if (selection.cursor.par() == cursor.par())
992 if (selection.cursor.pos() < cursor.pos()) {
993 selection.end = cursor;
994 selection.start = selection.cursor;
996 selection.end = selection.cursor;
997 selection.start = cursor;
999 else if (selection.cursor.y() < cursor.y() ||
1000 (selection.cursor.y() == cursor.y()
1001 && selection.cursor.x() < cursor.x())) {
1002 selection.end = cursor;
1003 selection.start = selection.cursor;
1006 selection.end = selection.cursor;
1007 selection.start = cursor;
1010 // a selection with no contents is not a selection
1011 if (selection.start.par() == selection.end.par() &&
1012 selection.start.pos() == selection.end.pos())
1013 selection.set(false);
1015 if (inset_owner && (selection.set() || lsel))
1016 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1020 string const LyXText::selectionAsString(Buffer const * buffer,
1023 if (!selection.set()) return string();
1026 // Special handling if the whole selection is within one paragraph
1027 if (selection.start.par() == selection.end.par()) {
1028 result += selection.start.par()->asString(buffer,
1029 selection.start.pos(),
1030 selection.end.pos(),
1035 // The selection spans more than one paragraph
1037 // First paragraph in selection
1038 result += selection.start.par()->asString(buffer,
1039 selection.start.pos(),
1040 selection.start.par()->size(),
1044 // The paragraphs in between (if any)
1045 LyXCursor tmpcur(selection.start);
1046 tmpcur.par(tmpcur.par()->next());
1047 while (tmpcur.par() != selection.end.par()) {
1048 result += tmpcur.par()->asString(buffer, 0,
1049 tmpcur.par()->size(),
1051 tmpcur.par(tmpcur.par()->next());
1054 // Last paragraph in selection
1055 result += selection.end.par()->asString(buffer, 0,
1056 selection.end.pos(), label);
1062 void LyXText::clearSelection() const
1064 selection.set(false);
1065 selection.mark(false);
1066 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1070 void LyXText::cursorHome(BufferView * bview) const
1072 setCursor(bview, cursor.par(), cursor.row()->pos());
1076 void LyXText::cursorEnd(BufferView * bview) const
1078 if (!cursor.row()->next()
1079 || cursor.row()->next()->par() != cursor.row()->par()) {
1080 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1082 if (cursor.par()->size() &&
1083 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1084 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1085 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1087 setCursor(bview,cursor.par(),
1088 rowLast(cursor.row()) + 1);
1094 void LyXText::cursorTop(BufferView * bview) const
1096 while (cursor.par()->previous())
1097 cursor.par(cursor.par()->previous());
1098 setCursor(bview, cursor.par(), 0);
1102 void LyXText::cursorBottom(BufferView * bview) const
1104 while (cursor.par()->next())
1105 cursor.par(cursor.par()->next());
1106 setCursor(bview, cursor.par(), cursor.par()->size());
1110 void LyXText::toggleFree(BufferView * bview,
1111 LyXFont const & font, bool toggleall)
1113 // If the mask is completely neutral, tell user
1114 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1115 // Could only happen with user style
1116 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1120 // Try implicit word selection
1121 // If there is a change in the language the implicit word selection
1123 LyXCursor resetCursor = cursor;
1124 bool implicitSelection = (font.language() == ignore_language
1125 && font.number() == LyXFont::IGNORE)
1126 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1129 setFont(bview, font, toggleall);
1131 // Implicit selections are cleared afterwards
1132 //and cursor is set to the original position.
1133 if (implicitSelection) {
1135 cursor = resetCursor;
1136 setCursor(bview, cursor.par(), cursor.pos());
1137 selection.cursor = cursor;
1140 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1144 string LyXText::getStringToIndex(BufferView * bview)
1148 // Try implicit word selection
1149 // If there is a change in the language the implicit word selection
1151 LyXCursor resetCursor = cursor;
1152 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1154 if (!selection.set()) {
1155 bview->owner()->message(_("Nothing to index!"));
1158 if (selection.start.par() != selection.end.par()) {
1159 bview->owner()->message(_("Cannot index more than one paragraph!"));
1163 idxstring = selectionAsString(bview->buffer(), false);
1165 // Implicit selections are cleared afterwards
1166 //and cursor is set to the original position.
1167 if (implicitSelection) {
1169 cursor = resetCursor;
1170 setCursor(bview, cursor.par(), cursor.pos());
1171 selection.cursor = cursor;
1177 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1178 Paragraph const * par) const
1180 if (textclasslist.Style(buf->params.textclass,
1181 par->getLayout()).labeltype != LABEL_MANUAL)
1184 return par->beginningOfMainBody();
1188 /* the DTP switches for paragraphs. LyX will store them in the
1189 * first physicla paragraph. When a paragraph is broken, the top settings
1190 * rest, the bottom settings are given to the new one. So I can make shure,
1191 * they do not duplicate themself and you cannnot make dirty things with
1194 void LyXText::setParagraph(BufferView * bview,
1195 bool line_top, bool line_bottom,
1196 bool pagebreak_top, bool pagebreak_bottom,
1197 VSpace const & space_top,
1198 VSpace const & space_bottom,
1199 Spacing const & spacing,
1201 string labelwidthstring,
1204 LyXCursor tmpcursor = cursor;
1205 if (!selection.set()) {
1206 selection.start = cursor;
1207 selection.end = cursor;
1210 // make sure that the depth behind the selection are restored, too
1211 Paragraph * endpar = selection.end.par()->next();
1212 Paragraph * undoendpar = endpar;
1214 if (endpar && endpar->getDepth()) {
1215 while (endpar && endpar->getDepth()) {
1216 endpar = endpar->next();
1217 undoendpar = endpar;
1221 // because of parindents etc.
1222 endpar = endpar->next();
1225 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1228 Paragraph * tmppar = selection.end.par();
1229 while (tmppar != selection.start.par()->previous()) {
1230 setCursor(bview, tmppar, 0);
1231 status(bview, LyXText::NEED_MORE_REFRESH);
1232 refresh_row = cursor.row();
1233 refresh_y = cursor.y() - cursor.row()->baseline();
1234 cursor.par()->params().lineTop(line_top);
1235 cursor.par()->params().lineBottom(line_bottom);
1236 cursor.par()->params().pagebreakTop(pagebreak_top);
1237 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1238 cursor.par()->params().spaceTop(space_top);
1239 cursor.par()->params().spaceBottom(space_bottom);
1240 cursor.par()->params().spacing(spacing);
1241 // does the layout allow the new alignment?
1242 if (align == LYX_ALIGN_LAYOUT)
1243 align = textclasslist
1244 .Style(bview->buffer()->params.textclass,
1245 cursor.par()->getLayout()).align;
1246 if (align & textclasslist
1247 .Style(bview->buffer()->params.textclass,
1248 cursor.par()->getLayout()).alignpossible) {
1249 if (align == textclasslist
1250 .Style(bview->buffer()->params.textclass,
1251 cursor.par()->getLayout()).align)
1252 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1254 cursor.par()->params().align(align);
1256 cursor.par()->setLabelWidthString(labelwidthstring);
1257 cursor.par()->params().noindent(noindent);
1258 tmppar = cursor.par()->previous();
1261 redoParagraphs(bview, selection.start, endpar);
1264 setCursor(bview, selection.start.par(), selection.start.pos());
1265 selection.cursor = cursor;
1266 setCursor(bview, selection.end.par(), selection.end.pos());
1267 setSelection(bview);
1268 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1270 bview->updateInset(inset_owner, true);
1274 char loweralphaCounter(int n)
1276 if (n < 1 || n > 26)
1286 char alphaCounter(int n)
1288 if (n < 1 || n > 26)
1296 char hebrewCounter(int n)
1298 static const char hebrew[22] = {
1299 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1300 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1301 '÷', 'ø', 'ù', 'ú'
1303 if (n < 1 || n > 22)
1311 string const romanCounter(int n)
1313 static char const * roman[20] = {
1314 "i", "ii", "iii", "iv", "v",
1315 "vi", "vii", "viii", "ix", "x",
1316 "xi", "xii", "xiii", "xiv", "xv",
1317 "xvi", "xvii", "xviii", "xix", "xx"
1319 if (n < 1 || n > 20)
1328 // set the counter of a paragraph. This includes the labels
1329 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1331 LyXLayout const & layout =
1332 textclasslist.Style(buf->params.textclass,
1335 LyXTextClass const & textclass =
1336 textclasslist.TextClass(buf->params.textclass);
1338 // copy the prev-counters to this one,
1339 // unless this is the first paragraph
1340 if (par->previous()) {
1341 for (int i = 0; i < 10; ++i) {
1342 par->setCounter(i, par->previous()->getFirstCounter(i));
1344 par->params().appendix(par->previous()->params().appendix());
1345 if (!par->params().appendix() && par->params().startOfAppendix()) {
1346 par->params().appendix(true);
1347 for (int i = 0; i < 10; ++i) {
1348 par->setCounter(i, 0);
1351 par->enumdepth = par->previous()->enumdepth;
1352 par->itemdepth = par->previous()->itemdepth;
1354 for (int i = 0; i < 10; ++i) {
1355 par->setCounter(i, 0);
1357 par->params().appendix(par->params().startOfAppendix());
1362 /* Maybe we have to increment the enumeration depth.
1363 * BUT, enumeration in a footnote is considered in isolation from its
1364 * surrounding paragraph so don't increment if this is the
1365 * first line of the footnote
1366 * AND, bibliographies can't have their depth changed ie. they
1367 * are always of depth 0
1370 && par->previous()->getDepth() < par->getDepth()
1371 && textclasslist.Style(buf->params.textclass,
1372 par->previous()->getLayout()
1373 ).labeltype == LABEL_COUNTER_ENUMI
1374 && par->enumdepth < 3
1375 && layout.labeltype != LABEL_BIBLIO) {
1379 // Maybe we have to decrement the enumeration depth, see note above
1381 && par->previous()->getDepth() > par->getDepth()
1382 && layout.labeltype != LABEL_BIBLIO) {
1383 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1384 par->setCounter(6 + par->enumdepth,
1385 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1386 /* reset the counters.
1387 * A depth change is like a breaking layout
1389 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1390 par->setCounter(i, 0);
1393 if (!par->params().labelString().empty()) {
1394 par->params().labelString(string());
1397 if (layout.margintype == MARGIN_MANUAL) {
1398 if (par->params().labelWidthString().empty()) {
1399 par->setLabelWidthString(layout.labelstring());
1402 par->setLabelWidthString(string());
1405 // is it a layout that has an automatic label?
1406 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1408 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1409 if (i >= 0 && i<= buf->params.secnumdepth) {
1410 par->incCounter(i); // increment the counter
1412 // Is there a label? Useful for Chapter layout
1413 if (!par->params().appendix()) {
1414 if (!layout.labelstring().empty())
1415 par->params().labelString(layout.labelstring());
1417 par->params().labelString(string());
1419 if (!layout.labelstring_appendix().empty())
1420 par->params().labelString(layout.labelstring_appendix());
1422 par->params().labelString(string());
1427 if (!par->params().appendix()) {
1428 switch (2 * LABEL_COUNTER_CHAPTER -
1429 textclass.maxcounter() + i) {
1430 case LABEL_COUNTER_CHAPTER:
1431 s << par->getCounter(i);
1433 case LABEL_COUNTER_SECTION:
1434 s << par->getCounter(i - 1) << '.'
1435 << par->getCounter(i);
1437 case LABEL_COUNTER_SUBSECTION:
1438 s << par->getCounter(i - 2) << '.'
1439 << par->getCounter(i - 1) << '.'
1440 << par->getCounter(i);
1442 case LABEL_COUNTER_SUBSUBSECTION:
1443 s << par->getCounter(i - 3) << '.'
1444 << par->getCounter(i - 2) << '.'
1445 << par->getCounter(i - 1) << '.'
1446 << par->getCounter(i);
1449 case LABEL_COUNTER_PARAGRAPH:
1450 s << par->getCounter(i - 4) << '.'
1451 << par->getCounter(i - 3) << '.'
1452 << par->getCounter(i - 2) << '.'
1453 << par->getCounter(i - 1) << '.'
1454 << par->getCounter(i);
1456 case LABEL_COUNTER_SUBPARAGRAPH:
1457 s << par->getCounter(i - 5) << '.'
1458 << par->getCounter(i - 4) << '.'
1459 << par->getCounter(i - 3) << '.'
1460 << par->getCounter(i - 2) << '.'
1461 << par->getCounter(i - 1) << '.'
1462 << par->getCounter(i);
1466 // Can this ever be reached? And in the
1467 // case it is, how can this be correct?
1469 s << par->getCounter(i) << '.';
1472 } else { // appendix
1473 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1474 case LABEL_COUNTER_CHAPTER:
1475 if (par->isRightToLeftPar(buf->params))
1476 s << hebrewCounter(par->getCounter(i));
1478 s << alphaCounter(par->getCounter(i));
1480 case LABEL_COUNTER_SECTION:
1481 if (par->isRightToLeftPar(buf->params))
1482 s << hebrewCounter(par->getCounter(i - 1));
1484 s << alphaCounter(par->getCounter(i - 1));
1487 << par->getCounter(i);
1490 case LABEL_COUNTER_SUBSECTION:
1491 if (par->isRightToLeftPar(buf->params))
1492 s << hebrewCounter(par->getCounter(i - 2));
1494 s << alphaCounter(par->getCounter(i - 2));
1497 << par->getCounter(i-1) << '.'
1498 << par->getCounter(i);
1501 case LABEL_COUNTER_SUBSUBSECTION:
1502 if (par->isRightToLeftPar(buf->params))
1503 s << hebrewCounter(par->getCounter(i-3));
1505 s << alphaCounter(par->getCounter(i-3));
1508 << par->getCounter(i-2) << '.'
1509 << par->getCounter(i-1) << '.'
1510 << par->getCounter(i);
1513 case LABEL_COUNTER_PARAGRAPH:
1514 if (par->isRightToLeftPar(buf->params))
1515 s << hebrewCounter(par->getCounter(i-4));
1517 s << alphaCounter(par->getCounter(i-4));
1520 << par->getCounter(i-3) << '.'
1521 << par->getCounter(i-2) << '.'
1522 << par->getCounter(i-1) << '.'
1523 << par->getCounter(i);
1526 case LABEL_COUNTER_SUBPARAGRAPH:
1527 if (par->isRightToLeftPar(buf->params))
1528 s << hebrewCounter(par->getCounter(i-5));
1530 s << alphaCounter(par->getCounter(i-5));
1533 << par->getCounter(i-4) << '.'
1534 << par->getCounter(i-3) << '.'
1535 << par->getCounter(i-2) << '.'
1536 << par->getCounter(i-1) << '.'
1537 << par->getCounter(i);
1541 // Can this ever be reached? And in the
1542 // case it is, how can this be correct?
1544 s << par->getCounter(i) << '.';
1550 par->params().labelString(par->params().labelString() +s.str().c_str());
1551 // We really want to remove the c_str as soon as
1554 for (i++; i < 10; ++i) {
1555 // reset the following counters
1556 par->setCounter(i, 0);
1558 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1559 for (i++; i < 10; ++i) {
1560 // reset the following counters
1561 par->setCounter(i, 0);
1563 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1564 par->incCounter(i + par->enumdepth);
1565 int number = par->getCounter(i + par->enumdepth);
1569 switch (par->enumdepth) {
1571 if (par->isRightToLeftPar(buf->params))
1573 << hebrewCounter(number)
1577 << loweralphaCounter(number)
1581 if (par->isRightToLeftPar(buf->params))
1582 s << '.' << romanCounter(number);
1584 s << romanCounter(number) << '.';
1587 if (par->isRightToLeftPar(buf->params))
1589 << alphaCounter(number);
1591 s << alphaCounter(number)
1595 if (par->isRightToLeftPar(buf->params))
1602 par->params().labelString(s.str().c_str());
1604 for (i += par->enumdepth + 1; i < 10; ++i) {
1605 // reset the following counters
1606 par->setCounter(i, 0);
1610 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1611 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1613 int number = par->getCounter(i);
1615 InsetCommandParams p("bibitem" );
1616 par->bibkey = new InsetBibKey(p);
1618 par->bibkey->setCounter(number);
1619 par->params().labelString(layout.labelstring());
1621 // In biblio should't be following counters but...
1623 string s = layout.labelstring();
1625 // the caption hack:
1626 if (layout.labeltype == LABEL_SENSITIVE) {
1627 bool isOK (par->inInset() && par->inInset()->owner() &&
1628 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1631 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1633 = floatList.getType(tmp->type());
1634 // We should get the correct number here too.
1635 s = fl.name() + " #:";
1637 /* par->SetLayout(0);
1638 s = layout->labelstring; */
1639 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1640 ? " :úåòîùî øñç" : "Senseless: ";
1643 par->params().labelString(s);
1645 /* reset the enumeration counter. They are always resetted
1646 * when there is any other layout between */
1647 for (int i = 6 + par->enumdepth; i < 10; ++i)
1648 par->setCounter(i, 0);
1653 // Updates all counters BEHIND the row. Changed paragraphs
1654 // with a dynamic left margin will be rebroken.
1655 void LyXText::updateCounters(BufferView * bview, Row * row) const
1663 par = row->par()->next();
1667 while (row->par() != par)
1670 setCounter(bview->buffer(), par);
1672 // now check for the headline layouts. remember that they
1673 // have a dynamic left margin
1674 if ((textclasslist.Style(bview->buffer()->params.textclass,
1675 par->layout).margintype == MARGIN_DYNAMIC
1676 || textclasslist.Style(bview->buffer()->params.textclass,
1677 par->layout).labeltype == LABEL_SENSITIVE)) {
1679 // Rebreak the paragraph
1680 removeParagraph(row);
1681 appendParagraph(bview, row);
1688 void LyXText::insertInset(BufferView * bview, Inset * inset)
1690 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1692 // I don't know if this is necessary here (Jug 20020102)
1693 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1694 cursor.par()->insertInset(cursor.pos(), inset);
1695 // Just to rebreak and refresh correctly.
1696 // The character will not be inserted a second time
1697 insertChar(bview, Paragraph::META_INSET);
1699 // If we enter a highly editable inset the cursor should be to before
1700 // the inset. This couldn't happen before as Undo was not handled inside
1701 // inset now after the Undo LyX tries to call inset->Edit(...) again
1702 // and cannot do this as the cursor is behind the inset and GetInset
1703 // does not return the inset!
1704 if (isHighlyEditableInset(inset)) {
1705 cursorLeft(bview, true);
1711 void LyXText::copyEnvironmentType()
1713 copylayouttype = cursor.par()->getLayout();
1717 void LyXText::pasteEnvironmentType(BufferView * bview)
1719 setLayout(bview, copylayouttype);
1723 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1725 // Stuff what we got on the clipboard. Even if there is no selection.
1727 // There is a problem with having the stuffing here in that the
1728 // larger the selection the slower LyX will get. This can be
1729 // solved by running the line below only when the selection has
1730 // finished. The solution used currently just works, to make it
1731 // faster we need to be more clever and probably also have more
1732 // calls to stuffClipboard. (Lgb)
1733 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1735 // This doesn't make sense, if there is no selection
1736 if (!selection.set())
1739 // OK, we have a selection. This is always between selection.start
1740 // and selection.end
1742 // make sure that the depth behind the selection are restored, too
1743 Paragraph * endpar = selection.end.par()->next();
1744 Paragraph * undoendpar = endpar;
1746 if (endpar && endpar->getDepth()) {
1747 while (endpar && endpar->getDepth()) {
1748 endpar = endpar->next();
1749 undoendpar = endpar;
1751 } else if (endpar) {
1752 endpar = endpar->next(); // because of parindents etc.
1755 setUndo(bview, Undo::DELETE,
1756 selection.start.par(), undoendpar);
1758 // there are two cases: cut only within one paragraph or
1759 // more than one paragraph
1760 if (selection.start.par() == selection.end.par()) {
1761 // only within one paragraph
1762 endpar = selection.end.par();
1763 int pos = selection.end.pos();
1764 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1765 selection.start.pos(), pos,
1766 bview->buffer()->params.textclass,
1768 selection.end.pos(pos);
1770 endpar = selection.end.par();
1771 int pos = selection.end.pos();
1772 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1773 selection.start.pos(), pos,
1774 bview->buffer()->params.textclass,
1777 selection.end.par(endpar);
1778 selection.end.pos(pos);
1779 cursor.pos(selection.end.pos());
1781 endpar = endpar->next();
1783 // sometimes necessary
1785 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1787 redoParagraphs(bview, selection.start, endpar);
1789 // cutSelection can invalidate the cursor so we need to set
1791 cursor = selection.start;
1793 // need a valid cursor. (Lgb)
1796 setCursor(bview, cursor.par(), cursor.pos());
1797 selection.cursor = cursor;
1798 updateCounters(bview, cursor.row());
1802 void LyXText::copySelection(BufferView * bview)
1804 // stuff the selection onto the X clipboard, from an explicit copy request
1805 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1807 // this doesnt make sense, if there is no selection
1808 if (!selection.set())
1811 // ok we have a selection. This is always between selection.start
1812 // and sel_end cursor
1814 // copy behind a space if there is one
1815 while (selection.start.par()->size() > selection.start.pos()
1816 && selection.start.par()->isLineSeparator(selection.start.pos())
1817 && (selection.start.par() != selection.end.par()
1818 || selection.start.pos() < selection.end.pos()))
1819 selection.start.pos(selection.start.pos() + 1);
1821 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1822 selection.start.pos(), selection.end.pos(),
1823 bview->buffer()->params.textclass);
1827 void LyXText::pasteSelection(BufferView * bview)
1829 // this does not make sense, if there is nothing to paste
1830 if (!CutAndPaste::checkPastePossible(cursor.par()))
1833 setUndo(bview, Undo::INSERT,
1834 cursor.par(), cursor.par()->next());
1837 Paragraph * actpar = cursor.par();
1838 int pos = cursor.pos();
1840 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1841 bview->buffer()->params.textclass);
1843 redoParagraphs(bview, cursor, endpar);
1845 setCursor(bview, cursor.par(), cursor.pos());
1848 setCursor(bview, actpar, pos);
1849 updateCounters(bview, cursor.row());
1853 // sets the selection over the number of characters of string, no check!!
1854 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1859 selection.cursor = cursor;
1860 for (string::size_type i = 0; i < str.length(); ++i)
1862 setSelection(bview);
1866 // simple replacing. The font of the first selected character is used
1867 void LyXText::replaceSelectionWithString(BufferView * bview,
1870 setCursorParUndo(bview);
1873 if (!selection.set()) { // create a dummy selection
1874 selection.end = cursor;
1875 selection.start = cursor;
1878 // Get font setting before we cut
1879 pos_type pos = selection.end.pos();
1880 LyXFont const font = selection.start.par()
1881 ->getFontSettings(bview->buffer()->params,
1882 selection.start.pos());
1884 // Insert the new string
1885 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1886 selection.end.par()->insertChar(pos, (*cit), font);
1890 // Cut the selection
1891 cutSelection(bview, true, false);
1897 // needed to insert the selection
1898 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1900 Paragraph * par = cursor.par();
1901 pos_type pos = cursor.pos();
1902 Paragraph * endpar = cursor.par()->next();
1904 setCursorParUndo(bview);
1906 // only to be sure, should not be neccessary
1909 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1911 redoParagraphs(bview, cursor, endpar);
1912 setCursor(bview, cursor.par(), cursor.pos());
1913 selection.cursor = cursor;
1914 setCursor(bview, par, pos);
1915 setSelection(bview);
1919 // turns double-CR to single CR, others where converted into one
1920 // blank. Then InsertStringAsLines is called
1921 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1923 string linestr(str);
1924 bool newline_inserted = false;
1925 for (string::size_type i = 0; i < linestr.length(); ++i) {
1926 if (linestr[i] == '\n') {
1927 if (newline_inserted) {
1928 // we know that \r will be ignored by
1929 // InsertStringA. Of course, it is a dirty
1930 // trick, but it works...
1931 linestr[i - 1] = '\r';
1935 newline_inserted = true;
1937 } else if (IsPrintable(linestr[i])) {
1938 newline_inserted = false;
1941 insertStringAsLines(bview, linestr);
1945 bool LyXText::gotoNextInset(BufferView * bview,
1946 vector<Inset::Code> const & codes,
1947 string const & contents) const
1949 LyXCursor res = cursor;
1952 if (res.pos() < res.par()->size() - 1) {
1953 res.pos(res.pos() + 1);
1955 res.par(res.par()->next());
1959 } while (res.par() &&
1960 !(res.par()->isInset(res.pos())
1961 && (inset = res.par()->getInset(res.pos())) != 0
1962 && find(codes.begin(), codes.end(), inset->lyxCode())
1964 && (contents.empty() ||
1965 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1969 setCursor(bview, res.par(), res.pos(), false);
1976 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1979 LyXCursor tmpcursor;
1983 Row * row = getRow(par, pos, y);
1985 // is there a break one row above
1986 if (row->previous() && row->previous()->par() == row->par()) {
1987 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1988 if (z >= row->pos()) {
1989 // set the dimensions of the row above
1990 y -= row->previous()->height();
1992 refresh_row = row->previous();
1993 status(bview, LyXText::NEED_MORE_REFRESH);
1995 breakAgain(bview, row->previous());
1997 // set the cursor again. Otherwise
1998 // dangling pointers are possible
1999 setCursor(bview, cursor.par(), cursor.pos(),
2000 false, cursor.boundary());
2001 selection.cursor = cursor;
2006 int const tmpheight = row->height();
2007 pos_type const tmplast = rowLast(row);
2011 breakAgain(bview, row);
2012 if (row->height() == tmpheight && rowLast(row) == tmplast)
2013 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2015 status(bview, LyXText::NEED_MORE_REFRESH);
2017 // check the special right address boxes
2018 if (textclasslist.Style(bview->buffer()->params.textclass,
2019 par->getLayout()).margintype
2020 == MARGIN_RIGHT_ADDRESS_BOX)
2028 redoDrawingOfParagraph(bview, tmpcursor);
2031 // set the cursor again. Otherwise dangling pointers are possible
2032 // also set the selection
2034 if (selection.set()) {
2036 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2037 false, selection.cursor.boundary());
2038 selection.cursor = cursor;
2039 setCursorIntern(bview, selection.start.par(),
2040 selection.start.pos(),
2041 false, selection.start.boundary());
2042 selection.start = cursor;
2043 setCursorIntern(bview, selection.end.par(),
2044 selection.end.pos(),
2045 false, selection.end.boundary());
2046 selection.end = cursor;
2047 setCursorIntern(bview, last_sel_cursor.par(),
2048 last_sel_cursor.pos(),
2049 false, last_sel_cursor.boundary());
2050 last_sel_cursor = cursor;
2053 setCursorIntern(bview, cursor.par(), cursor.pos(),
2054 false, cursor.boundary());
2058 // returns false if inset wasn't found
2059 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2061 // first check the current paragraph
2062 int pos = cursor.par()->getPositionOfInset(inset);
2064 checkParagraph(bview, cursor.par(), pos);
2068 // check every paragraph
2070 Paragraph * par = ownerParagraph();
2072 pos = par->getPositionOfInset(inset);
2074 checkParagraph(bview, par, pos);
2084 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2086 bool setfont, bool boundary) const
2088 LyXCursor old_cursor = cursor;
2089 setCursorIntern(bview, par, pos, setfont, boundary);
2090 return deleteEmptyParagraphMechanism(bview, old_cursor);
2094 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2095 pos_type pos, bool boundary) const
2102 cur.boundary(boundary);
2104 // get the cursor y position in text
2106 Row * row = getRow(par, pos, y);
2107 // y is now the beginning of the cursor row
2108 y += row->baseline();
2109 // y is now the cursor baseline
2112 // now get the cursors x position
2114 float fill_separator;
2116 float fill_label_hfill;
2117 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2119 pos_type cursor_vpos = 0;
2120 pos_type last = rowLastPrintable(row);
2122 if (pos > last + 1) {
2123 // This shouldn't happen.
2126 } else if (pos < row->pos()) {
2131 if (last < row->pos())
2132 cursor_vpos = row->pos();
2133 else if (pos > last && !boundary)
2134 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2135 ? row->pos() : last + 1;
2136 else if (pos > row->pos() &&
2137 (pos > last || boundary))
2138 /// Place cursor after char at (logical) position pos - 1
2139 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2140 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2142 /// Place cursor before char at (logical) position pos
2143 cursor_vpos = (bidi_level(pos) % 2 == 0)
2144 ? log2vis(pos) : log2vis(pos) + 1;
2146 pos_type main_body =
2147 beginningOfMainBody(bview->buffer(), row->par());
2148 if ((main_body > 0) &&
2149 ((main_body-1 > last) ||
2150 !row->par()->isLineSeparator(main_body-1)))
2153 for (pos_type vpos = row->pos();
2154 vpos < cursor_vpos; ++vpos) {
2155 pos = vis2log(vpos);
2156 if (main_body > 0 && pos == main_body - 1) {
2157 x += fill_label_hfill +
2158 lyxfont::width(textclasslist.Style(
2159 bview->buffer()->params.textclass,
2160 row->par()->getLayout())
2162 getLabelFont(bview->buffer(), row->par()));
2163 if (row->par()->isLineSeparator(main_body-1))
2164 x -= singleWidth(bview, row->par(),main_body-1);
2166 if (hfillExpansion(bview->buffer(), row, pos)) {
2167 x += singleWidth(bview, row->par(), pos);
2168 if (pos >= main_body)
2171 x += fill_label_hfill;
2172 } else if (row->par()->isSeparator(pos)) {
2173 x += singleWidth(bview, row->par(), pos);
2174 if (pos >= main_body)
2175 x += fill_separator;
2177 x += singleWidth(bview, row->par(), pos);
2186 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2187 pos_type pos, bool setfont, bool boundary) const
2189 InsetText * it = static_cast<InsetText *>(par->inInset());
2191 if (it != inset_owner) {
2192 lyxerr << "InsetText is " << it << endl;
2193 lyxerr << "inset_owner is " << inset_owner << endl;
2194 #ifdef WITH_WARNINGS
2195 #warning I believe this code is wrong. (Lgb)
2196 #warning Jürgen, have a look at this. (Lgb)
2197 #warning Hmmm, I guess you are right but we
2198 #warning should verify when this is needed
2200 // Jürgen, would you like to have a look?
2201 // I guess we need to move the outer cursor
2202 // and open and lock the inset (bla bla bla)
2203 // stuff I don't know... so can you have a look?
2205 // I moved the lyxerr stuff in here so we can see if
2206 // this is actually really needed and where!
2208 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2213 setCursor(bview, cursor, par, pos, boundary);
2215 setCurrentFont(bview);
2219 void LyXText::setCurrentFont(BufferView * bview) const
2221 pos_type pos = cursor.pos();
2222 if (cursor.boundary() && pos > 0)
2226 if (pos == cursor.par()->size())
2228 else // potentional bug... BUG (Lgb)
2229 if (cursor.par()->isSeparator(pos)) {
2230 if (pos > cursor.row()->pos() &&
2231 bidi_level(pos) % 2 ==
2232 bidi_level(pos - 1) % 2)
2234 else if (pos + 1 < cursor.par()->size())
2240 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2241 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2243 if (cursor.pos() == cursor.par()->size() &&
2244 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2245 !cursor.boundary()) {
2246 Language const * lang =
2247 cursor.par()->getParLanguage(bview->buffer()->params);
2248 current_font.setLanguage(lang);
2249 current_font.setNumber(LyXFont::OFF);
2250 real_current_font.setLanguage(lang);
2251 real_current_font.setNumber(LyXFont::OFF);
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2258 LyXCursor old_cursor = cursor;
2260 setCursorFromCoordinates(bview, cursor, x, y);
2261 setCurrentFont(bview);
2262 deleteEmptyParagraphMechanism(bview, old_cursor);
2266 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2269 // Get the row first.
2271 Row * row = getRowNearY(y);
2273 pos_type const column = getColumnNearX(bview, row, x, bound);
2274 cur.par(row->par());
2275 cur.pos(row->pos() + column);
2277 cur.y(y + row->baseline());
2279 cur.boundary(bound);
2283 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2285 if (cursor.pos() > 0) {
2286 bool boundary = cursor.boundary();
2287 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2288 if (!internal && !boundary &&
2289 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2290 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2291 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2292 Paragraph * par = cursor.par()->previous();
2293 setCursor(bview, par, par->size());
2298 void LyXText::cursorRight(BufferView * bview, bool internal) const
2300 if (!internal && cursor.boundary() &&
2301 !cursor.par()->isNewline(cursor.pos()))
2302 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2303 else if (cursor.pos() < cursor.par()->size()) {
2304 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2306 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2307 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2308 } else if (cursor.par()->next())
2309 setCursor(bview, cursor.par()->next(), 0);
2313 void LyXText::cursorUp(BufferView * bview) const
2315 setCursorFromCoordinates(bview, cursor.x_fix(),
2316 cursor.y() - cursor.row()->baseline() - 1);
2320 void LyXText::cursorDown(BufferView * bview) const
2322 setCursorFromCoordinates(bview, cursor.x_fix(),
2323 cursor.y() - cursor.row()->baseline()
2324 + cursor.row()->height() + 1);
2328 void LyXText::cursorUpParagraph(BufferView * bview) const
2330 if (cursor.pos() > 0) {
2331 setCursor(bview, cursor.par(), 0);
2333 else if (cursor.par()->previous()) {
2334 setCursor(bview, cursor.par()->previous(), 0);
2339 void LyXText::cursorDownParagraph(BufferView * bview) const
2341 if (cursor.par()->next()) {
2342 setCursor(bview, cursor.par()->next(), 0);
2344 setCursor(bview, cursor.par(), cursor.par()->size());
2348 // fix the cursor `cur' after a characters has been deleted at `where'
2349 // position. Called by deleteEmptyParagraphMechanism
2350 void LyXText::fixCursorAfterDelete(BufferView * bview,
2352 LyXCursor const & where) const
2354 // if cursor is not in the paragraph where the delete occured,
2356 if (cur.par() != where.par())
2359 // if cursor position is after the place where the delete occured,
2361 if (cur.pos() > where.pos())
2362 cur.pos(cur.pos()-1);
2364 // recompute row et al. for this cursor
2365 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2369 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2370 LyXCursor const & old_cursor) const
2372 // Would be wrong to delete anything if we have a selection.
2373 if (selection.set()) return false;
2375 // We allow all kinds of "mumbo-jumbo" when freespacing.
2376 if (textclasslist.Style(bview->buffer()->params.textclass,
2377 old_cursor.par()->getLayout()).free_spacing
2378 || old_cursor.par()->isFreeSpacing())
2383 /* Ok I'll put some comments here about what is missing.
2384 I have fixed BackSpace (and thus Delete) to not delete
2385 double-spaces automagically. I have also changed Cut,
2386 Copy and Paste to hopefully do some sensible things.
2387 There are still some small problems that can lead to
2388 double spaces stored in the document file or space at
2389 the beginning of paragraphs. This happens if you have
2390 the cursor betwenn to spaces and then save. Or if you
2391 cut and paste and the selection have a space at the
2392 beginning and then save right after the paste. I am
2393 sure none of these are very hard to fix, but I will
2394 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2395 that I can get some feedback. (Lgb)
2398 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2399 // delete the LineSeparator.
2402 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2403 // delete the LineSeparator.
2406 // If the pos around the old_cursor were spaces, delete one of them.
2407 if (old_cursor.par() != cursor.par()
2408 || old_cursor.pos() != cursor.pos()) {
2409 // Only if the cursor has really moved
2411 if (old_cursor.pos() > 0
2412 && old_cursor.pos() < old_cursor.par()->size()
2413 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2414 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2415 old_cursor.par()->erase(old_cursor.pos() - 1);
2416 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2418 #ifdef WITH_WARNINGS
2419 #warning This will not work anymore when we have multiple views of the same buffer
2420 // In this case, we will have to correct also the cursors held by
2421 // other bufferviews. It will probably be easier to do that in a more
2422 // automated way in LyXCursor code. (JMarc 26/09/2001)
2424 // correct all cursors held by the LyXText
2425 fixCursorAfterDelete(bview, cursor, old_cursor);
2426 fixCursorAfterDelete(bview, selection.cursor,
2428 fixCursorAfterDelete(bview, selection.start,
2430 fixCursorAfterDelete(bview, selection.end, old_cursor);
2431 fixCursorAfterDelete(bview, last_sel_cursor,
2433 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2434 fixCursorAfterDelete(bview, toggle_end_cursor,
2440 // don't delete anything if this is the ONLY paragraph!
2441 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2444 // Do not delete empty paragraphs with keepempty set.
2445 if ((textclasslist.Style(bview->buffer()->params.textclass,
2446 old_cursor.par()->getLayout())).keepempty)
2449 // only do our magic if we changed paragraph
2450 if (old_cursor.par() == cursor.par())
2453 // record if we have deleted a paragraph
2454 // we can't possibly have deleted a paragraph before this point
2455 bool deleted = false;
2457 if ((old_cursor.par()->size() == 0
2458 || (old_cursor.par()->size() == 1
2459 && old_cursor.par()->isLineSeparator(0)))) {
2460 // ok, we will delete anything
2461 LyXCursor tmpcursor;
2463 // make sure that you do not delete any environments
2464 status(bview, LyXText::NEED_MORE_REFRESH);
2467 if (old_cursor.row()->previous()) {
2468 refresh_row = old_cursor.row()->previous();
2469 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2471 cursor = old_cursor; // that undo can restore the right cursor position
2472 Paragraph * endpar = old_cursor.par()->next();
2473 if (endpar && endpar->getDepth()) {
2474 while (endpar && endpar->getDepth()) {
2475 endpar = endpar->next();
2478 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2482 removeRow(old_cursor.row());
2483 if (ownerParagraph() == old_cursor.par()) {
2484 ownerParagraph(ownerParagraph()->next());
2487 delete old_cursor.par();
2489 /* Breakagain the next par. Needed because of
2490 * the parindent that can occur or dissappear.
2491 * The next row can change its height, if
2492 * there is another layout before */
2493 if (refresh_row->next()) {
2494 breakAgain(bview, refresh_row->next());
2495 updateCounters(bview, refresh_row);
2497 setHeightOfRow(bview, refresh_row);
2499 refresh_row = old_cursor.row()->next();
2500 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2503 cursor = old_cursor; // that undo can restore the right cursor position
2504 Paragraph * endpar = old_cursor.par()->next();
2505 if (endpar && endpar->getDepth()) {
2506 while (endpar && endpar->getDepth()) {
2507 endpar = endpar->next();
2510 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2514 removeRow(old_cursor.row());
2516 if (ownerParagraph() == old_cursor.par()) {
2517 ownerParagraph(ownerParagraph()->next());
2520 delete old_cursor.par();
2522 /* Breakagain the next par. Needed because of
2523 the parindent that can occur or dissappear.
2524 The next row can change its height, if
2525 there is another layout before */
2527 breakAgain(bview, refresh_row);
2528 updateCounters(bview, refresh_row->previous());
2533 setCursorIntern(bview, cursor.par(), cursor.pos());
2535 if (selection.cursor.par() == old_cursor.par()
2536 && selection.cursor.pos() == old_cursor.pos()) {
2537 // correct selection
2538 selection.cursor = cursor;
2542 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2543 redoParagraphs(bview, old_cursor,
2544 old_cursor.par()->next());
2546 setCursorIntern(bview, cursor.par(), cursor.pos());
2547 selection.cursor = cursor;
2554 void LyXText::toggleAppendix(BufferView * bview)
2556 Paragraph * par = cursor.par();
2557 bool start = !par->params().startOfAppendix();
2559 // ensure that we have only one start_of_appendix in this document
2560 Paragraph * tmp = ownerParagraph();
2561 for (; tmp; tmp = tmp->next()) {
2562 tmp->params().startOfAppendix(false);
2565 par->params().startOfAppendix(start);
2567 // we can set the refreshing parameters now
2568 status(bview, LyXText::NEED_MORE_REFRESH);
2570 refresh_row = 0; // not needed for full update
2571 updateCounters(bview, 0);
2572 setCursor(bview, cursor.par(), cursor.pos());
2576 Paragraph * LyXText::ownerParagraph() const
2579 return inset_owner->paragraph();
2581 return bv_owner->buffer()->paragraph;
2585 void LyXText::ownerParagraph(Paragraph * p) const
2588 inset_owner->paragraph(p);
2590 bv_owner->buffer()->paragraph = p;
2595 void LyXText::ownerParagraph(int id, Paragraph * p) const
2597 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2598 if (op && op->inInset()) {
2599 static_cast<InsetText *>(op->inInset())->paragraph(p);
2606 LyXText::text_status LyXText::status() const
2612 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2614 // well as much as I know && binds more then || so the above and the
2615 // below are identical (this for your known use of parentesis!)
2616 // Now some explanation:
2617 // We should only go up with refreshing code so this means that if
2618 // we have a MORE refresh we should never set it to LITTLE if we still
2619 // didn't handle it (and then it will be UNCHANGED. Now as long as
2620 // we stay inside one LyXText this may work but we need to tell the
2621 // outermost LyXText that it should REALLY draw us if there is some
2622 // change in a Inset::LyXText. So you see that when we are inside a
2623 // inset's LyXText we give the LITTLE to the outermost LyXText to
2624 // tell'em that it should redraw the actual row (where the inset
2625 // resides! Capito?!
2627 if ((status_ != NEED_MORE_REFRESH)
2628 || (status_ == NEED_MORE_REFRESH
2629 && st != NEED_VERY_LITTLE_REFRESH))
2632 if (inset_owner && st != UNCHANGED) {
2633 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2634 if (!bview->text->refresh_row) {
2635 bview->text->refresh_row = bview->text->cursor.row();
2636 bview->text->refresh_y = bview->text->cursor.y() -
2637 bview->text->cursor.row()->baseline();