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_y(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)
65 LyXText::LyXText(InsetText * inset)
66 : number_of_rows(0), height(0), width(0), first_y(0),
67 bv_owner(0), inset_owner(inset), the_locking_inset(0),
68 need_break_row(0), refresh_y(0), refresh_row(0),
69 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
73 void LyXText::init(BufferView * bview, bool reinit)
76 // Delete all rows, this does not touch the paragraphs!
77 Row * tmprow = firstrow;
79 tmprow = firstrow->next();
88 copylayouttype.erase();
89 number_of_rows = first_y = refresh_y = 0;
90 status_ = LyXText::UNCHANGED;
94 Paragraph * par = ownerParagraph();
95 current_font = getFont(bview->buffer(), par, 0);
98 insertParagraph(bview, par, lastrow);
101 setCursorIntern(bview, firstrow->par(), 0);
102 selection.cursor = cursor;
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 #ifndef INHERIT_LANGUAGE
132 tmpfont.realize(textclasslist[buf->params.textclass][
133 par->layout()].font);
135 tmpfont.realize(textclasslist.
136 Style(buf->params.textclass,
138 buf->params.language);
140 par_depth = par->getDepth();
144 #ifndef INHERIT_LANGUAGE
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
147 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
148 buf->params.language);
157 // Gets the fully instantiated font at a given position in a paragraph
158 // Basically the same routine as Paragraph::getFont() in paragraph.C.
159 // The difference is that this one is used for displaying, and thus we
160 // are allowed to make cosmetic improvements. For instance make footnotes
162 // If position is -1, we get the layout font of the paragraph.
163 // If position is -2, we get the font of the manual label of the paragraph.
164 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
167 lyx::Assert(pos >= 0);
169 LyXLayout const & layout =
170 textclasslist[buf->params.textclass][par->layout()];
172 Paragraph::depth_type par_depth = par->getDepth();
173 // We specialize the 95% common case:
175 if (layout.labeltype == LABEL_MANUAL
176 && pos < beginningOfMainBody(buf, par)) {
178 LyXFont f = par->getFontSettings(buf->params, pos);
180 par->inInset()->getDrawFont(f);
181 #ifndef INHERIT_LANGUAGE
182 return f.realize(layout.reslabelfont);
184 return f.realize(layout.reslabelfont, buf->params.language);
187 LyXFont f = par->getFontSettings(buf->params, pos);
189 par->inInset()->getDrawFont(f);
190 #ifndef INHERIT_LANGUAGE
191 return f.realize(layout.resfont);
193 return f.realize(layout.resfont, buf->params.language);
198 // The uncommon case need not be optimized as much
202 if (pos < beginningOfMainBody(buf, par)) {
204 layoutfont = layout.labelfont;
207 layoutfont = layout.font;
210 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
211 #ifndef INHERIT_LANGUAGE
212 tmpfont.realize(layoutfont);
214 tmpfont.realize(layoutfont, buf->params.language);
217 par->inInset()->getDrawFont(tmpfont);
219 return realizeFont(tmpfont, buf, par);
223 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
225 LyXLayout const & layout =
226 textclasslist[buf->params.textclass][par->layout()];
228 Paragraph::depth_type par_depth = par->getDepth();
231 return layout.resfont;
234 return realizeFont(layout.font, buf, par);
238 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
240 LyXLayout const & layout =
241 textclasslist[buf->params.textclass][par->layout()];
243 Paragraph::depth_type par_depth = par->getDepth();
246 return layout.reslabelfont;
249 return realizeFont(layout.labelfont, buf, par);
253 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
254 pos_type pos, LyXFont const & fnt,
257 Buffer const * buf = bv->buffer();
258 LyXFont font = getFont(buf, par, pos);
259 font.update(fnt, buf->params.language, toggleall);
260 // Let the insets convert their font
261 if (par->isInset(pos)) {
262 Inset * inset = par->getInset(pos);
263 if (isEditableInset(inset)) {
264 UpdatableInset * uinset =
265 static_cast<UpdatableInset *>(inset);
266 uinset->setFont(bv, fnt, toggleall, true);
270 // Plug thru to version below:
271 setCharFont(buf, par, pos, font);
275 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
276 pos_type pos, LyXFont const & fnt)
280 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
281 LyXLayout const & layout = tclass[par->layout()];
283 // Get concrete layout font to reduce against
286 if (pos < beginningOfMainBody(buf, par))
287 layoutfont = layout.labelfont;
289 layoutfont = layout.font;
291 // Realize against environment font information
292 if (par->getDepth()) {
293 Paragraph * tp = par;
294 while (!layoutfont.resolved() && tp && tp->getDepth()) {
295 tp = tp->outerHook();
297 #ifndef INHERIT_LANGUAGE
298 layoutfont.realize(tclass[tp->layout()].font);
300 layoutfont.realize(textclasslist.
301 Style(buf->params.textclass,
303 buf->params.language);
308 #ifndef INHERIT_LANGUAGE
309 layoutfont.realize(tclass.defaultfont());
311 layoutfont.realize(tclass.defaultfont(), buf->params.language);
314 // Now, reduce font against full layout font
315 font.reduce(layoutfont);
317 par->setFont(pos, font);
321 // inserts a new row behind the specified row, increments
322 // the touched counters
323 void LyXText::insertRow(Row * row, Paragraph * par,
326 Row * tmprow = new Row;
329 tmprow->next(firstrow);
332 tmprow->previous(row);
333 tmprow->next(row->next());
338 tmprow->next()->previous(tmprow);
340 if (tmprow->previous())
341 tmprow->previous()->next(tmprow);
353 // removes the row and reset the touched counters
354 void LyXText::removeRow(Row * row) const
356 Row * row_prev = row->previous();
358 row->next()->previous(row_prev);
360 firstrow = row->next();
361 // lyx::Assert(firstrow);
363 row_prev->next(row->next());
365 if (row == lastrow) {
366 lyx::Assert(!row->next());
369 if (refresh_row == row) {
370 refresh_row = row_prev ? row_prev : row->next();
371 // what about refresh_y, refresh_height
374 height -= row->height(); // the text becomes smaller
377 --number_of_rows; // one row less
381 // remove all following rows of the paragraph of the specified row.
382 void LyXText::removeParagraph(Row * row) const
384 Paragraph * tmppar = row->par();
388 while (row && row->par() == tmppar) {
389 tmprow = row->next();
396 // insert the specified paragraph behind the specified row
397 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
400 insertRow(row, par, 0); /* insert a new row, starting
403 setCounter(bview->buffer(), par); // set the counters
405 // and now append the whole paragraph behind the new row
408 appendParagraph(bview, firstrow);
410 row->next()->height(0);
411 appendParagraph(bview, row->next());
416 Inset * LyXText::getInset() const
419 if (cursor.pos() == 0 && cursor.par()->bibkey) {
420 inset = cursor.par()->bibkey;
421 } else if (cursor.pos() < cursor.par()->size()
422 && cursor.par()->isInset(cursor.pos())) {
423 inset = cursor.par()->getInset(cursor.pos());
429 void LyXText::toggleInset(BufferView * bview)
431 Inset * inset = getInset();
432 // is there an editable inset at cursor position?
433 if (!isEditableInset(inset)) {
434 // No, try to see if we are inside a collapsable inset
435 if (inset_owner && inset_owner->owner()
436 && inset_owner->owner()->isOpen()) {
437 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
438 inset_owner->owner()->close(bview);
442 //bview->owner()->message(inset->editMessage());
444 // do we want to keep this?? (JMarc)
445 if (!isHighlyEditableInset(inset))
446 setCursorParUndo(bview);
448 if (inset->isOpen()) {
454 inset->open(bview, !inset->isOpen());
459 /* used in setlayout */
460 // Asger is not sure we want to do this...
461 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
464 LyXLayout const & layout =
465 textclasslist[buf->params.textclass][par->layout()];
468 for (pos_type pos = 0; pos < par->size(); ++pos) {
469 if (pos < beginningOfMainBody(buf, par))
470 layoutfont = layout.labelfont;
472 layoutfont = layout.font;
474 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
475 tmpfont.reduce(layoutfont);
476 par->setFont(pos, tmpfont);
481 Paragraph * LyXText::setLayout(BufferView * bview,
482 LyXCursor & cur, LyXCursor & sstart_cur,
483 LyXCursor & send_cur,
484 string const & layout)
486 Paragraph * endpar = send_cur.par()->next();
487 Paragraph * undoendpar = endpar;
489 if (endpar && endpar->getDepth()) {
490 while (endpar && endpar->getDepth()) {
491 endpar = endpar->next();
495 endpar = endpar->next(); // because of parindents etc.
498 setUndo(bview, Undo::EDIT,
499 sstart_cur.par(), undoendpar);
501 // ok we have a selection. This is always between sstart_cur
502 // and sel_end cursor
505 LyXLayout const & lyxlayout =
506 textclasslist[bview->buffer()->params.textclass][layout];
509 cur.par()->applyLayout(layout);
510 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
511 Paragraph * fppar = cur.par();
512 fppar->params().spaceTop(lyxlayout.fill_top ?
513 VSpace(VSpace::VFILL)
514 : VSpace(VSpace::NONE));
515 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
516 VSpace(VSpace::VFILL)
517 : VSpace(VSpace::NONE));
518 if (lyxlayout.margintype == MARGIN_MANUAL)
519 cur.par()->setLabelWidthString(lyxlayout.labelstring());
520 if (lyxlayout.labeltype != LABEL_BIBLIO
522 delete fppar->bibkey;
525 if (cur.par() != send_cur.par())
526 cur.par(cur.par()->next());
527 } while (cur.par() != send_cur.par());
533 // set layout over selection and make a total rebreak of those paragraphs
534 void LyXText::setLayout(BufferView * bview, string const & layout)
536 LyXCursor tmpcursor = cursor; /* store the current cursor */
538 // if there is no selection just set the layout
539 // of the current paragraph */
540 if (!selection.set()) {
541 selection.start = cursor; // dummy selection
542 selection.end = cursor;
544 Paragraph * endpar = setLayout(bview, cursor, selection.start,
545 selection.end, layout);
546 redoParagraphs(bview, selection.start, endpar);
548 // we have to reset the selection, because the
549 // geometry could have changed
550 setCursor(bview, selection.start.par(),
551 selection.start.pos(), false);
552 selection.cursor = cursor;
553 setCursor(bview, selection.end.par(), selection.end.pos(), false);
554 updateCounters(bview, cursor.row());
557 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
561 // increment depth over selection and
562 // make a total rebreak of those paragraphs
563 void LyXText::incDepth(BufferView * bview)
565 // If there is no selection, just use the current paragraph
566 if (!selection.set()) {
567 selection.start = cursor; // dummy selection
568 selection.end = cursor;
571 // We end at the next paragraph with depth 0
572 Paragraph * endpar = selection.end.par()->next();
574 Paragraph * undoendpar = endpar;
576 if (endpar && endpar->getDepth()) {
577 while (endpar && endpar->getDepth()) {
578 endpar = endpar->next();
582 endpar = endpar->next(); // because of parindents etc.
585 setUndo(bview, Undo::EDIT,
586 selection.start.par(), undoendpar);
588 LyXCursor tmpcursor = cursor; // store the current cursor
590 // ok we have a selection. This is always between sel_start_cursor
591 // and sel_end cursor
592 cursor = selection.start;
594 bool anything_changed = false;
596 LyXTextClass const & tclass =
597 textclasslist[bview->buffer()->params.textclass];
600 // NOTE: you can't change the depth of a bibliography entry
601 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
602 Paragraph * prev = cursor.par()->previous();
605 if (cursor.par()->getDepth()
606 < prev->getMaxDepthAfter(bview->buffer())){
607 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
608 anything_changed = true;
612 if (cursor.par() == selection.end.par())
614 cursor.par(cursor.par()->next());
617 // if nothing changed set all depth to 0
618 if (!anything_changed) {
619 cursor = selection.start;
620 while (cursor.par() != selection.end.par()) {
621 cursor.par()->params().depth(0);
622 cursor.par(cursor.par()->next());
624 cursor.par()->params().depth(0);
627 redoParagraphs(bview, selection.start, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 setCursor(bview, selection.start.par(), selection.start.pos());
632 selection.cursor = cursor;
633 setCursor(bview, selection.end.par(), selection.end.pos());
634 updateCounters(bview, cursor.row());
637 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
641 // decrement depth over selection and
642 // make a total rebreak of those paragraphs
643 void LyXText::decDepth(BufferView * bview)
645 // if there is no selection just set the layout
646 // of the current paragraph
647 if (!selection.set()) {
648 selection.start = cursor; // dummy selection
649 selection.end = cursor;
651 Paragraph * endpar = selection.end.par()->next();
652 Paragraph * undoendpar = endpar;
654 if (endpar && endpar->getDepth()) {
655 while (endpar && endpar->getDepth()) {
656 endpar = endpar->next();
660 endpar = endpar->next(); // because of parindents etc.
663 setUndo(bview, Undo::EDIT,
664 selection.start.par(), undoendpar);
666 LyXCursor tmpcursor = cursor; // store the current cursor
668 // ok we have a selection. This is always between sel_start_cursor
669 // and sel_end cursor
670 cursor = selection.start;
673 if (cursor.par()->params().depth()) {
674 cursor.par()->params()
675 .depth(cursor.par()->params().depth() - 1);
677 if (cursor.par() == selection.end.par()) {
680 cursor.par(cursor.par()->next());
683 redoParagraphs(bview, selection.start, endpar);
685 // we have to reset the selection, because the
686 // geometry could have changed
687 setCursor(bview, selection.start.par(),
688 selection.start.pos());
689 selection.cursor = cursor;
690 setCursor(bview, selection.end.par(), selection.end.pos());
691 updateCounters(bview, cursor.row());
694 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
698 // set font over selection and make a total rebreak of those paragraphs
699 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
701 // if there is no selection just set the current_font
702 if (!selection.set()) {
703 // Determine basis font
705 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
707 layoutfont = getLabelFont(bview->buffer(),
710 layoutfont = getLayoutFont(bview->buffer(),
713 // Update current font
714 real_current_font.update(font,
715 bview->buffer()->params.language,
718 // Reduce to implicit settings
719 current_font = real_current_font;
720 current_font.reduce(layoutfont);
721 // And resolve it completely
722 #ifndef INHERIT_LANGUAGE
723 real_current_font.realize(layoutfont);
725 real_current_font.realize(layoutfont,
726 bview->buffer()->params.language);
731 LyXCursor tmpcursor = cursor; // store the current cursor
733 // ok we have a selection. This is always between sel_start_cursor
734 // and sel_end cursor
736 setUndo(bview, Undo::EDIT,
737 selection.start.par(), selection.end.par()->next());
739 cursor = selection.start;
740 while (cursor.par() != selection.end.par() ||
741 (cursor.pos() < selection.end.pos()))
743 if (cursor.pos() < cursor.par()->size()) {
744 // an open footnote should behave
746 setCharFont(bview, cursor.par(), cursor.pos(),
748 cursor.pos(cursor.pos() + 1);
751 cursor.par(cursor.par()->next());
756 redoParagraphs(bview, selection.start, selection.end.par()->next());
758 // we have to reset the selection, because the
759 // geometry could have changed, but we keep
760 // it for user convenience
761 setCursor(bview, selection.start.par(), selection.start.pos());
762 selection.cursor = cursor;
763 setCursor(bview, selection.end.par(), selection.end.pos());
765 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
766 tmpcursor.boundary());
770 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
772 Row * tmprow = cur.row();
773 int y = cur.y() - tmprow->baseline();
775 setHeightOfRow(bview, tmprow);
777 while (tmprow->previous()
778 && tmprow->previous()->par() == tmprow->par()) {
779 tmprow = tmprow->previous();
780 y -= tmprow->height();
781 setHeightOfRow(bview, tmprow);
784 // we can set the refreshing parameters now
785 status(bview, LyXText::NEED_MORE_REFRESH);
787 refresh_row = tmprow;
788 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
792 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
794 Row * tmprow = cur.row();
796 int y = cur.y() - tmprow->baseline();
797 setHeightOfRow(bview, tmprow);
799 while (tmprow->previous()
800 && tmprow->previous()->par() == tmprow->par()) {
801 tmprow = tmprow->previous();
802 y -= tmprow->height();
805 // we can set the refreshing parameters now
806 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
808 refresh_row = tmprow;
810 status(bview, LyXText::NEED_MORE_REFRESH);
811 setCursor(bview, cur.par(), cur.pos());
815 // deletes and inserts again all paragaphs between the cursor
816 // and the specified par
817 // This function is needed after SetLayout and SetFont etc.
818 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
819 Paragraph const * endpar) const
822 Paragraph * tmppar = 0;
823 Paragraph * first_phys_par = 0;
825 Row * tmprow = cur.row();
827 int y = cur.y() - tmprow->baseline();
829 if (!tmprow->previous()) {
830 // a trick/hack for UNDO
831 // This is needed because in an UNDO/REDO we could have changed
832 // the ownerParagrah() so the paragraph inside the row is NOT
833 // my really first par anymore. Got it Lars ;) (Jug 20011206)
834 first_phys_par = ownerParagraph();
836 first_phys_par = tmprow->par();
837 while (tmprow->previous()
838 && tmprow->previous()->par() == first_phys_par)
840 tmprow = tmprow->previous();
841 y -= tmprow->height();
845 // we can set the refreshing parameters now
846 status(bview, LyXText::NEED_MORE_REFRESH);
848 refresh_row = tmprow->previous(); /* the real refresh row will
849 be deleted, so I store
853 tmppar = tmprow->next()->par();
856 while (tmprow->next() && tmppar != endpar) {
857 removeRow(tmprow->next());
858 if (tmprow->next()) {
859 tmppar = tmprow->next()->par();
865 // remove the first one
866 tmprow2 = tmprow; /* this is because tmprow->previous()
868 tmprow = tmprow->previous();
871 tmppar = first_phys_par;
875 insertParagraph(bview, tmppar, tmprow);
879 while (tmprow->next()
880 && tmprow->next()->par() == tmppar) {
881 tmprow = tmprow->next();
883 tmppar = tmppar->next();
885 } while (tmppar && tmppar != endpar);
887 // this is because of layout changes
889 refresh_y -= refresh_row->height();
890 setHeightOfRow(bview, refresh_row);
892 refresh_row = firstrow;
894 setHeightOfRow(bview, refresh_row);
897 if (tmprow && tmprow->next())
898 setHeightOfRow(bview, tmprow->next());
902 bool LyXText::fullRebreak(BufferView * bview)
908 if (need_break_row) {
909 breakAgain(bview, need_break_row);
917 // important for the screen
920 /* the cursor set functions have a special mechanism. When they
921 * realize, that you left an empty paragraph, they will delete it.
922 * They also delete the corresponding row */
924 // need the selection cursor:
925 void LyXText::setSelection(BufferView * bview)
927 bool const lsel = selection.set();
929 if (!selection.set()) {
930 last_sel_cursor = selection.cursor;
931 selection.start = selection.cursor;
932 selection.end = selection.cursor;
937 // first the toggling area
938 if (cursor.y() < last_sel_cursor.y()
939 || (cursor.y() == last_sel_cursor.y()
940 && cursor.x() < last_sel_cursor.x())) {
941 toggle_end_cursor = last_sel_cursor;
942 toggle_cursor = cursor;
944 toggle_end_cursor = cursor;
945 toggle_cursor = last_sel_cursor;
948 last_sel_cursor = cursor;
950 // and now the whole selection
952 if (selection.cursor.par() == cursor.par())
953 if (selection.cursor.pos() < cursor.pos()) {
954 selection.end = cursor;
955 selection.start = selection.cursor;
957 selection.end = selection.cursor;
958 selection.start = cursor;
960 else if (selection.cursor.y() < cursor.y() ||
961 (selection.cursor.y() == cursor.y()
962 && selection.cursor.x() < cursor.x())) {
963 selection.end = cursor;
964 selection.start = selection.cursor;
967 selection.end = selection.cursor;
968 selection.start = cursor;
971 // a selection with no contents is not a selection
972 if (selection.start.par() == selection.end.par() &&
973 selection.start.pos() == selection.end.pos())
974 selection.set(false);
976 if (inset_owner && (selection.set() || lsel))
977 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
981 string const LyXText::selectionAsString(Buffer const * buffer,
984 if (!selection.set()) return string();
987 // Special handling if the whole selection is within one paragraph
988 if (selection.start.par() == selection.end.par()) {
989 result += selection.start.par()->asString(buffer,
990 selection.start.pos(),
996 // The selection spans more than one paragraph
998 // First paragraph in selection
999 result += selection.start.par()->asString(buffer,
1000 selection.start.pos(),
1001 selection.start.par()->size(),
1005 // The paragraphs in between (if any)
1006 LyXCursor tmpcur(selection.start);
1007 tmpcur.par(tmpcur.par()->next());
1008 while (tmpcur.par() != selection.end.par()) {
1009 result += tmpcur.par()->asString(buffer, 0,
1010 tmpcur.par()->size(),
1012 tmpcur.par(tmpcur.par()->next());
1015 // Last paragraph in selection
1016 result += selection.end.par()->asString(buffer, 0,
1017 selection.end.pos(), label);
1023 void LyXText::clearSelection() const
1025 selection.set(false);
1026 selection.mark(false);
1027 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1031 void LyXText::cursorHome(BufferView * bview) const
1033 setCursor(bview, cursor.par(), cursor.row()->pos());
1037 void LyXText::cursorEnd(BufferView * bview) const
1039 if (!cursor.row()->next()
1040 || cursor.row()->next()->par() != cursor.row()->par()) {
1041 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1043 if (cursor.par()->size() &&
1044 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1045 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1046 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1048 setCursor(bview,cursor.par(),
1049 rowLast(cursor.row()) + 1);
1055 void LyXText::cursorTop(BufferView * bview) const
1057 while (cursor.par()->previous())
1058 cursor.par(cursor.par()->previous());
1059 setCursor(bview, cursor.par(), 0);
1063 void LyXText::cursorBottom(BufferView * bview) const
1065 while (cursor.par()->next())
1066 cursor.par(cursor.par()->next());
1067 setCursor(bview, cursor.par(), cursor.par()->size());
1071 void LyXText::toggleFree(BufferView * bview,
1072 LyXFont const & font, bool toggleall)
1074 // If the mask is completely neutral, tell user
1075 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1076 // Could only happen with user style
1077 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1081 // Try implicit word selection
1082 // If there is a change in the language the implicit word selection
1084 LyXCursor resetCursor = cursor;
1085 bool implicitSelection = (font.language() == ignore_language
1086 && font.number() == LyXFont::IGNORE)
1087 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1090 setFont(bview, font, toggleall);
1092 // Implicit selections are cleared afterwards
1093 //and cursor is set to the original position.
1094 if (implicitSelection) {
1096 cursor = resetCursor;
1097 setCursor(bview, cursor.par(), cursor.pos());
1098 selection.cursor = cursor;
1101 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1105 string LyXText::getStringToIndex(BufferView * bview)
1109 // Try implicit word selection
1110 // If there is a change in the language the implicit word selection
1112 LyXCursor resetCursor = cursor;
1113 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1115 if (!selection.set()) {
1116 bview->owner()->message(_("Nothing to index!"));
1119 if (selection.start.par() != selection.end.par()) {
1120 bview->owner()->message(_("Cannot index more than one paragraph!"));
1124 idxstring = selectionAsString(bview->buffer(), false);
1126 // Implicit selections are cleared afterwards
1127 //and cursor is set to the original position.
1128 if (implicitSelection) {
1130 cursor = resetCursor;
1131 setCursor(bview, cursor.par(), cursor.pos());
1132 selection.cursor = cursor;
1138 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1139 Paragraph const * par) const
1141 if (textclasslist[buf->params.textclass][
1142 par->layout()].labeltype != LABEL_MANUAL)
1145 return par->beginningOfMainBody();
1149 /* the DTP switches for paragraphs. LyX will store them in the
1150 * first physicla paragraph. When a paragraph is broken, the top settings
1151 * rest, the bottom settings are given to the new one. So I can make shure,
1152 * they do not duplicate themself and you cannnot make dirty things with
1155 void LyXText::setParagraph(BufferView * bview,
1156 bool line_top, bool line_bottom,
1157 bool pagebreak_top, bool pagebreak_bottom,
1158 VSpace const & space_top,
1159 VSpace const & space_bottom,
1160 Spacing const & spacing,
1162 string labelwidthstring,
1165 LyXCursor tmpcursor = cursor;
1166 if (!selection.set()) {
1167 selection.start = cursor;
1168 selection.end = cursor;
1171 // make sure that the depth behind the selection are restored, too
1172 Paragraph * endpar = selection.end.par()->next();
1173 Paragraph * undoendpar = endpar;
1175 if (endpar && endpar->getDepth()) {
1176 while (endpar && endpar->getDepth()) {
1177 endpar = endpar->next();
1178 undoendpar = endpar;
1182 // because of parindents etc.
1183 endpar = endpar->next();
1186 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1189 Paragraph * tmppar = selection.end.par();
1190 LyXTextClass const & tclass =
1191 textclasslist[bview->buffer()->params.textclass];
1193 while (tmppar != selection.start.par()->previous()) {
1194 setCursor(bview, tmppar, 0);
1195 status(bview, LyXText::NEED_MORE_REFRESH);
1196 refresh_row = cursor.row();
1197 refresh_y = cursor.y() - cursor.row()->baseline();
1198 cursor.par()->params().lineTop(line_top);
1199 cursor.par()->params().lineBottom(line_bottom);
1200 cursor.par()->params().pagebreakTop(pagebreak_top);
1201 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1202 cursor.par()->params().spaceTop(space_top);
1203 cursor.par()->params().spaceBottom(space_bottom);
1204 cursor.par()->params().spacing(spacing);
1205 // does the layout allow the new alignment?
1206 LyXLayout const & layout = tclass[cursor.par()->layout()];
1208 if (align == LYX_ALIGN_LAYOUT)
1209 align = layout.align;
1210 if (align & layout.alignpossible) {
1211 if (align == layout.align)
1212 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1214 cursor.par()->params().align(align);
1216 cursor.par()->setLabelWidthString(labelwidthstring);
1217 cursor.par()->params().noindent(noindent);
1218 tmppar = cursor.par()->previous();
1221 redoParagraphs(bview, selection.start, endpar);
1224 setCursor(bview, selection.start.par(), selection.start.pos());
1225 selection.cursor = cursor;
1226 setCursor(bview, selection.end.par(), selection.end.pos());
1227 setSelection(bview);
1228 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1230 bview->updateInset(inset_owner, true);
1234 char loweralphaCounter(int n)
1236 if (n < 1 || n > 26)
1246 char alphaCounter(int n)
1248 if (n < 1 || n > 26)
1256 char hebrewCounter(int n)
1258 static const char hebrew[22] = {
1259 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1260 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1261 '÷', 'ø', 'ù', 'ú'
1263 if (n < 1 || n > 22)
1271 string const romanCounter(int n)
1273 static char const * roman[20] = {
1274 "i", "ii", "iii", "iv", "v",
1275 "vi", "vii", "viii", "ix", "x",
1276 "xi", "xii", "xiii", "xiv", "xv",
1277 "xvi", "xvii", "xviii", "xix", "xx"
1279 if (n < 1 || n > 20)
1288 // set the counter of a paragraph. This includes the labels
1289 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1291 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1292 LyXLayout const & layout = textclass[par->layout()];
1295 // copy the prev-counters to this one,
1296 // unless this is the first paragraph
1297 if (par->previous()) {
1298 for (int i = 0; i < 10; ++i) {
1299 par->setCounter(i, par->previous()->getFirstCounter(i));
1301 par->params().appendix(par->previous()->params().appendix());
1302 if (!par->params().appendix() && par->params().startOfAppendix()) {
1303 par->params().appendix(true);
1304 for (int i = 0; i < 10; ++i) {
1305 par->setCounter(i, 0);
1308 par->enumdepth = par->previous()->enumdepth;
1309 par->itemdepth = par->previous()->itemdepth;
1311 for (int i = 0; i < 10; ++i) {
1312 par->setCounter(i, 0);
1314 par->params().appendix(par->params().startOfAppendix());
1319 /* Maybe we have to increment the enumeration depth.
1320 * BUT, enumeration in a footnote is considered in isolation from its
1321 * surrounding paragraph so don't increment if this is the
1322 * first line of the footnote
1323 * AND, bibliographies can't have their depth changed ie. they
1324 * are always of depth 0
1327 && par->previous()->getDepth() < par->getDepth()
1328 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1329 && par->enumdepth < 3
1330 && layout.labeltype != LABEL_BIBLIO) {
1334 // Maybe we have to decrement the enumeration depth, see note above
1336 && par->previous()->getDepth() > par->getDepth()
1337 && layout.labeltype != LABEL_BIBLIO) {
1338 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1339 par->setCounter(6 + par->enumdepth,
1340 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1341 /* reset the counters.
1342 * A depth change is like a breaking layout
1344 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1345 par->setCounter(i, 0);
1348 if (!par->params().labelString().empty()) {
1349 par->params().labelString(string());
1352 if (layout.margintype == MARGIN_MANUAL) {
1353 if (par->params().labelWidthString().empty()) {
1354 par->setLabelWidthString(layout.labelstring());
1357 par->setLabelWidthString(string());
1360 // is it a layout that has an automatic label?
1361 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1363 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1364 if (i >= 0 && i<= buf->params.secnumdepth) {
1365 par->incCounter(i); // increment the counter
1367 // Is there a label? Useful for Chapter layout
1368 if (!par->params().appendix()) {
1369 if (!layout.labelstring().empty())
1370 par->params().labelString(layout.labelstring());
1372 par->params().labelString(string());
1374 if (!layout.labelstring_appendix().empty())
1375 par->params().labelString(layout.labelstring_appendix());
1377 par->params().labelString(string());
1382 if (!par->params().appendix()) {
1383 switch (2 * LABEL_COUNTER_CHAPTER -
1384 textclass.maxcounter() + i) {
1385 case LABEL_COUNTER_CHAPTER:
1386 s << par->getCounter(i);
1388 case LABEL_COUNTER_SECTION:
1389 s << par->getCounter(i - 1) << '.'
1390 << par->getCounter(i);
1392 case LABEL_COUNTER_SUBSECTION:
1393 s << par->getCounter(i - 2) << '.'
1394 << par->getCounter(i - 1) << '.'
1395 << par->getCounter(i);
1397 case LABEL_COUNTER_SUBSUBSECTION:
1398 s << par->getCounter(i - 3) << '.'
1399 << par->getCounter(i - 2) << '.'
1400 << par->getCounter(i - 1) << '.'
1401 << par->getCounter(i);
1404 case LABEL_COUNTER_PARAGRAPH:
1405 s << par->getCounter(i - 4) << '.'
1406 << par->getCounter(i - 3) << '.'
1407 << par->getCounter(i - 2) << '.'
1408 << par->getCounter(i - 1) << '.'
1409 << par->getCounter(i);
1411 case LABEL_COUNTER_SUBPARAGRAPH:
1412 s << par->getCounter(i - 5) << '.'
1413 << par->getCounter(i - 4) << '.'
1414 << par->getCounter(i - 3) << '.'
1415 << par->getCounter(i - 2) << '.'
1416 << par->getCounter(i - 1) << '.'
1417 << par->getCounter(i);
1421 // Can this ever be reached? And in the
1422 // case it is, how can this be correct?
1424 s << par->getCounter(i) << '.';
1427 } else { // appendix
1428 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1429 case LABEL_COUNTER_CHAPTER:
1430 if (par->isRightToLeftPar(buf->params))
1431 s << hebrewCounter(par->getCounter(i));
1433 s << alphaCounter(par->getCounter(i));
1435 case LABEL_COUNTER_SECTION:
1436 if (par->isRightToLeftPar(buf->params))
1437 s << hebrewCounter(par->getCounter(i - 1));
1439 s << alphaCounter(par->getCounter(i - 1));
1442 << par->getCounter(i);
1445 case LABEL_COUNTER_SUBSECTION:
1446 if (par->isRightToLeftPar(buf->params))
1447 s << hebrewCounter(par->getCounter(i - 2));
1449 s << alphaCounter(par->getCounter(i - 2));
1452 << par->getCounter(i-1) << '.'
1453 << par->getCounter(i);
1456 case LABEL_COUNTER_SUBSUBSECTION:
1457 if (par->isRightToLeftPar(buf->params))
1458 s << hebrewCounter(par->getCounter(i-3));
1460 s << alphaCounter(par->getCounter(i-3));
1463 << par->getCounter(i-2) << '.'
1464 << par->getCounter(i-1) << '.'
1465 << par->getCounter(i);
1468 case LABEL_COUNTER_PARAGRAPH:
1469 if (par->isRightToLeftPar(buf->params))
1470 s << hebrewCounter(par->getCounter(i-4));
1472 s << alphaCounter(par->getCounter(i-4));
1475 << par->getCounter(i-3) << '.'
1476 << par->getCounter(i-2) << '.'
1477 << par->getCounter(i-1) << '.'
1478 << par->getCounter(i);
1481 case LABEL_COUNTER_SUBPARAGRAPH:
1482 if (par->isRightToLeftPar(buf->params))
1483 s << hebrewCounter(par->getCounter(i-5));
1485 s << alphaCounter(par->getCounter(i-5));
1488 << par->getCounter(i-4) << '.'
1489 << par->getCounter(i-3) << '.'
1490 << par->getCounter(i-2) << '.'
1491 << par->getCounter(i-1) << '.'
1492 << par->getCounter(i);
1496 // Can this ever be reached? And in the
1497 // case it is, how can this be correct?
1499 s << par->getCounter(i) << '.';
1505 par->params().labelString(par->params().labelString() +s.str().c_str());
1506 // We really want to remove the c_str as soon as
1509 for (i++; i < 10; ++i) {
1510 // reset the following counters
1511 par->setCounter(i, 0);
1513 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1514 for (i++; i < 10; ++i) {
1515 // reset the following counters
1516 par->setCounter(i, 0);
1518 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1519 par->incCounter(i + par->enumdepth);
1520 int number = par->getCounter(i + par->enumdepth);
1524 switch (par->enumdepth) {
1526 if (par->isRightToLeftPar(buf->params))
1528 << hebrewCounter(number)
1532 << loweralphaCounter(number)
1536 if (par->isRightToLeftPar(buf->params))
1537 s << '.' << romanCounter(number);
1539 s << romanCounter(number) << '.';
1542 if (par->isRightToLeftPar(buf->params))
1544 << alphaCounter(number);
1546 s << alphaCounter(number)
1550 if (par->isRightToLeftPar(buf->params))
1557 par->params().labelString(s.str().c_str());
1559 for (i += par->enumdepth + 1; i < 10; ++i) {
1560 // reset the following counters
1561 par->setCounter(i, 0);
1565 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1566 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1568 int number = par->getCounter(i);
1570 InsetCommandParams p("bibitem" );
1571 par->bibkey = new InsetBibKey(p);
1573 par->bibkey->setCounter(number);
1574 par->params().labelString(layout.labelstring());
1576 // In biblio should't be following counters but...
1578 string s = layout.labelstring();
1580 // the caption hack:
1581 if (layout.labeltype == LABEL_SENSITIVE) {
1582 bool isOK (par->inInset() && par->inInset()->owner() &&
1583 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1586 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1588 = floatList.getType(tmp->type());
1589 // We should get the correct number here too.
1590 s = fl.name() + " #:";
1592 /* par->SetLayout(0);
1593 s = layout->labelstring; */
1594 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1595 ? " :úåòîùî øñç" : "Senseless: ";
1598 par->params().labelString(s);
1600 /* reset the enumeration counter. They are always resetted
1601 * when there is any other layout between */
1602 for (int i = 6 + par->enumdepth; i < 10; ++i)
1603 par->setCounter(i, 0);
1608 // Updates all counters BEHIND the row. Changed paragraphs
1609 // with a dynamic left margin will be rebroken.
1610 void LyXText::updateCounters(BufferView * bview, Row * row) const
1618 par = row->par()->next();
1622 while (row->par() != par)
1625 setCounter(bview->buffer(), par);
1627 // now check for the headline layouts. remember that they
1628 // have a dynamic left margin
1629 LyXTextClass const & tclass =
1630 textclasslist[bview->buffer()->params.textclass];
1631 LyXLayout const & layout = tclass[par->layout()];
1633 if (layout.margintype == MARGIN_DYNAMIC
1634 || layout.labeltype == LABEL_SENSITIVE) {
1635 // Rebreak the paragraph
1636 removeParagraph(row);
1637 appendParagraph(bview, row);
1644 void LyXText::insertInset(BufferView * bview, Inset * inset)
1646 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1648 // I don't know if this is necessary here (Jug 20020102)
1649 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1650 cursor.par()->insertInset(cursor.pos(), inset);
1651 // Just to rebreak and refresh correctly.
1652 // The character will not be inserted a second time
1653 insertChar(bview, Paragraph::META_INSET);
1655 // If we enter a highly editable inset the cursor should be to before
1656 // the inset. This couldn't happen before as Undo was not handled inside
1657 // inset now after the Undo LyX tries to call inset->Edit(...) again
1658 // and cannot do this as the cursor is behind the inset and GetInset
1659 // does not return the inset!
1660 if (isHighlyEditableInset(inset)) {
1661 cursorLeft(bview, true);
1667 void LyXText::copyEnvironmentType()
1669 copylayouttype = cursor.par()->layout();
1673 void LyXText::pasteEnvironmentType(BufferView * bview)
1675 setLayout(bview, copylayouttype);
1679 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1681 // Stuff what we got on the clipboard. Even if there is no selection.
1683 // There is a problem with having the stuffing here in that the
1684 // larger the selection the slower LyX will get. This can be
1685 // solved by running the line below only when the selection has
1686 // finished. The solution used currently just works, to make it
1687 // faster we need to be more clever and probably also have more
1688 // calls to stuffClipboard. (Lgb)
1689 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1691 // This doesn't make sense, if there is no selection
1692 if (!selection.set())
1695 // OK, we have a selection. This is always between selection.start
1696 // and selection.end
1698 // make sure that the depth behind the selection are restored, too
1699 Paragraph * endpar = selection.end.par()->next();
1700 Paragraph * undoendpar = endpar;
1702 if (endpar && endpar->getDepth()) {
1703 while (endpar && endpar->getDepth()) {
1704 endpar = endpar->next();
1705 undoendpar = endpar;
1707 } else if (endpar) {
1708 endpar = endpar->next(); // because of parindents etc.
1711 setUndo(bview, Undo::DELETE,
1712 selection.start.par(), undoendpar);
1714 // there are two cases: cut only within one paragraph or
1715 // more than one paragraph
1716 if (selection.start.par() == selection.end.par()) {
1717 // only within one paragraph
1718 endpar = selection.end.par();
1719 int pos = selection.end.pos();
1720 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1721 selection.start.pos(), pos,
1722 bview->buffer()->params.textclass,
1724 selection.end.pos(pos);
1726 endpar = selection.end.par();
1727 int pos = selection.end.pos();
1728 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1729 selection.start.pos(), pos,
1730 bview->buffer()->params.textclass,
1733 selection.end.par(endpar);
1734 selection.end.pos(pos);
1735 cursor.pos(selection.end.pos());
1737 endpar = endpar->next();
1739 // sometimes necessary
1741 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1743 redoParagraphs(bview, selection.start, endpar);
1745 // cutSelection can invalidate the cursor so we need to set
1747 cursor = selection.start;
1749 // need a valid cursor. (Lgb)
1752 setCursor(bview, cursor.par(), cursor.pos());
1753 selection.cursor = cursor;
1754 updateCounters(bview, cursor.row());
1758 void LyXText::copySelection(BufferView * bview)
1760 // stuff the selection onto the X clipboard, from an explicit copy request
1761 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1763 // this doesnt make sense, if there is no selection
1764 if (!selection.set())
1767 // ok we have a selection. This is always between selection.start
1768 // and sel_end cursor
1770 // copy behind a space if there is one
1771 while (selection.start.par()->size() > selection.start.pos()
1772 && selection.start.par()->isLineSeparator(selection.start.pos())
1773 && (selection.start.par() != selection.end.par()
1774 || selection.start.pos() < selection.end.pos()))
1775 selection.start.pos(selection.start.pos() + 1);
1777 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1778 selection.start.pos(), selection.end.pos(),
1779 bview->buffer()->params.textclass);
1783 void LyXText::pasteSelection(BufferView * bview)
1785 // this does not make sense, if there is nothing to paste
1786 if (!CutAndPaste::checkPastePossible(cursor.par()))
1789 setUndo(bview, Undo::INSERT,
1790 cursor.par(), cursor.par()->next());
1793 Paragraph * actpar = cursor.par();
1794 int pos = cursor.pos();
1796 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1797 bview->buffer()->params.textclass);
1799 redoParagraphs(bview, cursor, endpar);
1801 setCursor(bview, cursor.par(), cursor.pos());
1804 setCursor(bview, actpar, pos);
1805 updateCounters(bview, cursor.row());
1809 // sets the selection over the number of characters of string, no check!!
1810 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1815 selection.cursor = cursor;
1816 for (string::size_type i = 0; i < str.length(); ++i)
1818 setSelection(bview);
1822 // simple replacing. The font of the first selected character is used
1823 void LyXText::replaceSelectionWithString(BufferView * bview,
1826 setCursorParUndo(bview);
1829 if (!selection.set()) { // create a dummy selection
1830 selection.end = cursor;
1831 selection.start = cursor;
1834 // Get font setting before we cut
1835 pos_type pos = selection.end.pos();
1836 LyXFont const font = selection.start.par()
1837 ->getFontSettings(bview->buffer()->params,
1838 selection.start.pos());
1840 // Insert the new string
1841 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1842 selection.end.par()->insertChar(pos, (*cit), font);
1846 // Cut the selection
1847 cutSelection(bview, true, false);
1853 // needed to insert the selection
1854 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1856 Paragraph * par = cursor.par();
1857 pos_type pos = cursor.pos();
1858 Paragraph * endpar = cursor.par()->next();
1860 setCursorParUndo(bview);
1862 // only to be sure, should not be neccessary
1865 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1867 redoParagraphs(bview, cursor, endpar);
1868 setCursor(bview, cursor.par(), cursor.pos());
1869 selection.cursor = cursor;
1870 setCursor(bview, par, pos);
1871 setSelection(bview);
1875 // turns double-CR to single CR, others where converted into one
1876 // blank. Then InsertStringAsLines is called
1877 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1879 string linestr(str);
1880 bool newline_inserted = false;
1881 for (string::size_type i = 0; i < linestr.length(); ++i) {
1882 if (linestr[i] == '\n') {
1883 if (newline_inserted) {
1884 // we know that \r will be ignored by
1885 // InsertStringA. Of course, it is a dirty
1886 // trick, but it works...
1887 linestr[i - 1] = '\r';
1891 newline_inserted = true;
1893 } else if (IsPrintable(linestr[i])) {
1894 newline_inserted = false;
1897 insertStringAsLines(bview, linestr);
1901 bool LyXText::gotoNextInset(BufferView * bview,
1902 vector<Inset::Code> const & codes,
1903 string const & contents) const
1905 LyXCursor res = cursor;
1908 if (res.pos() < res.par()->size() - 1) {
1909 res.pos(res.pos() + 1);
1911 res.par(res.par()->next());
1915 } while (res.par() &&
1916 !(res.par()->isInset(res.pos())
1917 && (inset = res.par()->getInset(res.pos())) != 0
1918 && find(codes.begin(), codes.end(), inset->lyxCode())
1920 && (contents.empty() ||
1921 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1925 setCursor(bview, res.par(), res.pos(), false);
1932 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1935 LyXCursor tmpcursor;
1939 Row * row = getRow(par, pos, y);
1941 // is there a break one row above
1942 if (row->previous() && row->previous()->par() == row->par()) {
1943 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1944 if (z >= row->pos()) {
1945 // set the dimensions of the row above
1946 y -= row->previous()->height();
1948 refresh_row = row->previous();
1949 status(bview, LyXText::NEED_MORE_REFRESH);
1951 breakAgain(bview, row->previous());
1953 // set the cursor again. Otherwise
1954 // dangling pointers are possible
1955 setCursor(bview, cursor.par(), cursor.pos(),
1956 false, cursor.boundary());
1957 selection.cursor = cursor;
1962 int const tmpheight = row->height();
1963 pos_type const tmplast = rowLast(row);
1967 breakAgain(bview, row);
1968 if (row->height() == tmpheight && rowLast(row) == tmplast)
1969 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1971 status(bview, LyXText::NEED_MORE_REFRESH);
1973 // check the special right address boxes
1974 if (textclasslist[bview->buffer()->params.textclass][
1975 par->layout()].margintype
1976 == MARGIN_RIGHT_ADDRESS_BOX)
1984 redoDrawingOfParagraph(bview, tmpcursor);
1987 // set the cursor again. Otherwise dangling pointers are possible
1988 // also set the selection
1990 if (selection.set()) {
1992 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1993 false, selection.cursor.boundary());
1994 selection.cursor = cursor;
1995 setCursorIntern(bview, selection.start.par(),
1996 selection.start.pos(),
1997 false, selection.start.boundary());
1998 selection.start = cursor;
1999 setCursorIntern(bview, selection.end.par(),
2000 selection.end.pos(),
2001 false, selection.end.boundary());
2002 selection.end = cursor;
2003 setCursorIntern(bview, last_sel_cursor.par(),
2004 last_sel_cursor.pos(),
2005 false, last_sel_cursor.boundary());
2006 last_sel_cursor = cursor;
2009 setCursorIntern(bview, cursor.par(), cursor.pos(),
2010 false, cursor.boundary());
2014 // returns false if inset wasn't found
2015 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2017 // first check the current paragraph
2018 int pos = cursor.par()->getPositionOfInset(inset);
2020 checkParagraph(bview, cursor.par(), pos);
2024 // check every paragraph
2026 Paragraph * par = ownerParagraph();
2028 pos = par->getPositionOfInset(inset);
2030 checkParagraph(bview, par, pos);
2040 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2042 bool setfont, bool boundary) const
2044 LyXCursor old_cursor = cursor;
2045 setCursorIntern(bview, par, pos, setfont, boundary);
2046 return deleteEmptyParagraphMechanism(bview, old_cursor);
2050 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2051 pos_type pos, bool boundary) const
2058 cur.boundary(boundary);
2061 if (pos && par->getChar(pos) == Paragraph::META_INSET &&
2062 par->getInset(pos)) {
2063 Inset * ins = par->getInset(pos);
2064 if (ins->needFullRow() || ins->display()) {
2071 // get the cursor y position in text
2073 Row * row = getRow(par, pos, y);
2074 // y is now the beginning of the cursor row
2075 y += row->baseline();
2076 // y is now the cursor baseline
2079 // now get the cursors x position
2081 float fill_separator;
2083 float fill_label_hfill;
2084 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2086 pos_type cursor_vpos = 0;
2087 pos_type last = rowLastPrintable(row);
2089 if (pos > last + 1) {
2090 // This shouldn't happen.
2093 } else if (pos < row->pos()) {
2098 if (last < row->pos())
2099 cursor_vpos = row->pos();
2100 else if (pos > last && !boundary)
2101 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2102 ? row->pos() : last + 1;
2103 else if (pos > row->pos() &&
2104 (pos > last || boundary))
2105 /// Place cursor after char at (logical) position pos - 1
2106 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2107 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2109 /// Place cursor before char at (logical) position pos
2110 cursor_vpos = (bidi_level(pos) % 2 == 0)
2111 ? log2vis(pos) : log2vis(pos) + 1;
2113 pos_type main_body =
2114 beginningOfMainBody(bview->buffer(), row->par());
2115 if ((main_body > 0) &&
2116 ((main_body-1 > last) ||
2117 !row->par()->isLineSeparator(main_body-1)))
2120 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2121 pos = vis2log(vpos);
2122 if (main_body > 0 && pos == main_body - 1) {
2123 x += fill_label_hfill +
2124 lyxfont::width(textclasslist[
2125 bview->buffer()->params.textclass][
2126 row->par()->layout()]
2128 getLabelFont(bview->buffer(), row->par()));
2129 if (row->par()->isLineSeparator(main_body-1))
2130 x -= singleWidth(bview, row->par(),main_body-1);
2132 if (hfillExpansion(bview->buffer(), row, pos)) {
2133 x += singleWidth(bview, row->par(), pos);
2134 if (pos >= main_body)
2137 x += fill_label_hfill;
2138 } else if (row->par()->isSeparator(pos)) {
2139 x += singleWidth(bview, row->par(), pos);
2140 if (pos >= main_body)
2141 x += fill_separator;
2143 x += singleWidth(bview, row->par(), pos);
2152 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2153 pos_type pos, bool setfont, bool boundary) const
2155 InsetText * it = static_cast<InsetText *>(par->inInset());
2157 if (it != inset_owner) {
2158 lyxerr << "InsetText is " << it << endl;
2159 lyxerr << "inset_owner is " << inset_owner << endl;
2160 #ifdef WITH_WARNINGS
2161 #warning I believe this code is wrong. (Lgb)
2162 #warning Jürgen, have a look at this. (Lgb)
2163 #warning Hmmm, I guess you are right but we
2164 #warning should verify when this is needed
2166 // Jürgen, would you like to have a look?
2167 // I guess we need to move the outer cursor
2168 // and open and lock the inset (bla bla bla)
2169 // stuff I don't know... so can you have a look?
2171 // I moved the lyxerr stuff in here so we can see if
2172 // this is actually really needed and where!
2174 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2179 setCursor(bview, cursor, par, pos, boundary);
2181 setCurrentFont(bview);
2185 void LyXText::setCurrentFont(BufferView * bview) const
2187 pos_type pos = cursor.pos();
2188 if (cursor.boundary() && pos > 0)
2192 if (pos == cursor.par()->size())
2194 else // potentional bug... BUG (Lgb)
2195 if (cursor.par()->isSeparator(pos)) {
2196 if (pos > cursor.row()->pos() &&
2197 bidi_level(pos) % 2 ==
2198 bidi_level(pos - 1) % 2)
2200 else if (pos + 1 < cursor.par()->size())
2206 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2207 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2209 if (cursor.pos() == cursor.par()->size() &&
2210 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2211 !cursor.boundary()) {
2212 Language const * lang =
2213 cursor.par()->getParLanguage(bview->buffer()->params);
2214 current_font.setLanguage(lang);
2215 current_font.setNumber(LyXFont::OFF);
2216 real_current_font.setLanguage(lang);
2217 real_current_font.setNumber(LyXFont::OFF);
2222 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2224 LyXCursor old_cursor = cursor;
2226 setCursorFromCoordinates(bview, cursor, x, y);
2227 setCurrentFont(bview);
2228 deleteEmptyParagraphMechanism(bview, old_cursor);
2232 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2235 // Get the row first.
2237 Row * row = getRowNearY(y);
2239 pos_type const column = getColumnNearX(bview, row, x, bound);
2240 cur.par(row->par());
2241 cur.pos(row->pos() + column);
2243 cur.y(y + row->baseline());
2245 cur.boundary(bound);
2249 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2251 if (cursor.pos() > 0) {
2252 bool boundary = cursor.boundary();
2253 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2254 if (!internal && !boundary &&
2255 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2256 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2257 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2258 Paragraph * par = cursor.par()->previous();
2259 setCursor(bview, par, par->size());
2264 void LyXText::cursorRight(BufferView * bview, bool internal) const
2266 if (!internal && cursor.boundary() &&
2267 !cursor.par()->isNewline(cursor.pos()))
2268 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2269 else if (cursor.pos() < cursor.par()->size()) {
2270 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2272 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2273 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2274 } else if (cursor.par()->next())
2275 setCursor(bview, cursor.par()->next(), 0);
2279 void LyXText::cursorUp(BufferView * bview) const
2281 setCursorFromCoordinates(bview, cursor.x_fix(),
2282 cursor.y() - cursor.row()->baseline() - 1);
2286 void LyXText::cursorDown(BufferView * bview) const
2288 setCursorFromCoordinates(bview, cursor.x_fix(),
2289 cursor.y() - cursor.row()->baseline()
2290 + cursor.row()->height() + 1);
2294 void LyXText::cursorUpParagraph(BufferView * bview) const
2296 if (cursor.pos() > 0) {
2297 setCursor(bview, cursor.par(), 0);
2299 else if (cursor.par()->previous()) {
2300 setCursor(bview, cursor.par()->previous(), 0);
2305 void LyXText::cursorDownParagraph(BufferView * bview) const
2307 if (cursor.par()->next()) {
2308 setCursor(bview, cursor.par()->next(), 0);
2310 setCursor(bview, cursor.par(), cursor.par()->size());
2314 // fix the cursor `cur' after a characters has been deleted at `where'
2315 // position. Called by deleteEmptyParagraphMechanism
2316 void LyXText::fixCursorAfterDelete(BufferView * bview,
2318 LyXCursor const & where) const
2320 // if cursor is not in the paragraph where the delete occured,
2322 if (cur.par() != where.par())
2325 // if cursor position is after the place where the delete occured,
2327 if (cur.pos() > where.pos())
2328 cur.pos(cur.pos()-1);
2330 // recompute row et al. for this cursor
2331 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2335 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2336 LyXCursor const & old_cursor) const
2338 // Would be wrong to delete anything if we have a selection.
2339 if (selection.set())
2342 // We allow all kinds of "mumbo-jumbo" when freespacing.
2343 if (textclasslist[bview->buffer()->params.textclass][
2344 old_cursor.par()->layout()].free_spacing
2345 || old_cursor.par()->isFreeSpacing())
2350 /* Ok I'll put some comments here about what is missing.
2351 I have fixed BackSpace (and thus Delete) to not delete
2352 double-spaces automagically. I have also changed Cut,
2353 Copy and Paste to hopefully do some sensible things.
2354 There are still some small problems that can lead to
2355 double spaces stored in the document file or space at
2356 the beginning of paragraphs. This happens if you have
2357 the cursor betwenn to spaces and then save. Or if you
2358 cut and paste and the selection have a space at the
2359 beginning and then save right after the paste. I am
2360 sure none of these are very hard to fix, but I will
2361 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2362 that I can get some feedback. (Lgb)
2365 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2366 // delete the LineSeparator.
2369 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2370 // delete the LineSeparator.
2373 // If the pos around the old_cursor were spaces, delete one of them.
2374 if (old_cursor.par() != cursor.par()
2375 || old_cursor.pos() != cursor.pos()) {
2376 // Only if the cursor has really moved
2378 if (old_cursor.pos() > 0
2379 && old_cursor.pos() < old_cursor.par()->size()
2380 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2381 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2382 old_cursor.par()->erase(old_cursor.pos() - 1);
2383 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2385 #ifdef WITH_WARNINGS
2386 #warning This will not work anymore when we have multiple views of the same buffer
2387 // In this case, we will have to correct also the cursors held by
2388 // other bufferviews. It will probably be easier to do that in a more
2389 // automated way in LyXCursor code. (JMarc 26/09/2001)
2391 // correct all cursors held by the LyXText
2392 fixCursorAfterDelete(bview, cursor, old_cursor);
2393 fixCursorAfterDelete(bview, selection.cursor,
2395 fixCursorAfterDelete(bview, selection.start,
2397 fixCursorAfterDelete(bview, selection.end, old_cursor);
2398 fixCursorAfterDelete(bview, last_sel_cursor,
2400 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2401 fixCursorAfterDelete(bview, toggle_end_cursor,
2407 // don't delete anything if this is the ONLY paragraph!
2408 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2411 // Do not delete empty paragraphs with keepempty set.
2413 [bview->buffer()->params.textclass]
2414 [old_cursor.par()->layout()].keepempty)
2417 // only do our magic if we changed paragraph
2418 if (old_cursor.par() == cursor.par())
2421 // record if we have deleted a paragraph
2422 // we can't possibly have deleted a paragraph before this point
2423 bool deleted = false;
2425 if ((old_cursor.par()->size() == 0
2426 || (old_cursor.par()->size() == 1
2427 && old_cursor.par()->isLineSeparator(0)))) {
2428 // ok, we will delete anything
2429 LyXCursor tmpcursor;
2431 // make sure that you do not delete any environments
2432 status(bview, LyXText::NEED_MORE_REFRESH);
2435 if (old_cursor.row()->previous()) {
2436 refresh_row = old_cursor.row()->previous();
2437 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2439 cursor = old_cursor; // that undo can restore the right cursor position
2440 Paragraph * endpar = old_cursor.par()->next();
2441 if (endpar && endpar->getDepth()) {
2442 while (endpar && endpar->getDepth()) {
2443 endpar = endpar->next();
2446 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2450 removeRow(old_cursor.row());
2451 if (ownerParagraph() == old_cursor.par()) {
2452 ownerParagraph(ownerParagraph()->next());
2455 delete old_cursor.par();
2457 /* Breakagain the next par. Needed because of
2458 * the parindent that can occur or dissappear.
2459 * The next row can change its height, if
2460 * there is another layout before */
2461 if (refresh_row->next()) {
2462 breakAgain(bview, refresh_row->next());
2463 updateCounters(bview, refresh_row);
2465 setHeightOfRow(bview, refresh_row);
2467 refresh_row = old_cursor.row()->next();
2468 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
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());
2484 if (ownerParagraph() == old_cursor.par()) {
2485 ownerParagraph(ownerParagraph()->next());
2488 delete old_cursor.par();
2490 /* Breakagain the next par. Needed because of
2491 the parindent that can occur or dissappear.
2492 The next row can change its height, if
2493 there is another layout before */
2495 breakAgain(bview, refresh_row);
2496 updateCounters(bview, refresh_row->previous());
2501 setCursorIntern(bview, cursor.par(), cursor.pos());
2503 if (selection.cursor.par() == old_cursor.par()
2504 && selection.cursor.pos() == old_cursor.pos()) {
2505 // correct selection
2506 selection.cursor = cursor;
2510 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2511 redoParagraphs(bview, old_cursor,
2512 old_cursor.par()->next());
2514 setCursorIntern(bview, cursor.par(), cursor.pos());
2515 selection.cursor = cursor;
2522 void LyXText::toggleAppendix(BufferView * bview)
2524 Paragraph * par = cursor.par();
2525 bool start = !par->params().startOfAppendix();
2527 // ensure that we have only one start_of_appendix in this document
2528 Paragraph * tmp = ownerParagraph();
2529 for (; tmp; tmp = tmp->next()) {
2530 tmp->params().startOfAppendix(false);
2533 par->params().startOfAppendix(start);
2535 // we can set the refreshing parameters now
2536 status(bview, LyXText::NEED_MORE_REFRESH);
2538 refresh_row = 0; // not needed for full update
2539 updateCounters(bview, 0);
2540 setCursor(bview, cursor.par(), cursor.pos());
2544 Paragraph * LyXText::ownerParagraph() const
2547 return inset_owner->paragraph();
2549 return bv_owner->buffer()->paragraph;
2553 void LyXText::ownerParagraph(Paragraph * p) const
2556 inset_owner->paragraph(p);
2558 bv_owner->buffer()->paragraph = p;
2563 void LyXText::ownerParagraph(int id, Paragraph * p) const
2565 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2566 if (op && op->inInset()) {
2567 static_cast<InsetText *>(op->inInset())->paragraph(p);
2574 LyXText::text_status LyXText::status() const
2580 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2582 // well as much as I know && binds more then || so the above and the
2583 // below are identical (this for your known use of parentesis!)
2584 // Now some explanation:
2585 // We should only go up with refreshing code so this means that if
2586 // we have a MORE refresh we should never set it to LITTLE if we still
2587 // didn't handle it (and then it will be UNCHANGED. Now as long as
2588 // we stay inside one LyXText this may work but we need to tell the
2589 // outermost LyXText that it should REALLY draw us if there is some
2590 // change in a Inset::LyXText. So you see that when we are inside a
2591 // inset's LyXText we give the LITTLE to the outermost LyXText to
2592 // tell'em that it should redraw the actual row (where the inset
2593 // resides! Capito?!
2595 if ((status_ != NEED_MORE_REFRESH)
2596 || (status_ == NEED_MORE_REFRESH
2597 && st != NEED_VERY_LITTLE_REFRESH))
2600 if (inset_owner && st != UNCHANGED) {
2601 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2602 if (!bview->text->refresh_row) {
2603 bview->text->refresh_row = bview->text->cursor.row();
2604 bview->text->refresh_y = bview->text->cursor.y() -
2605 bview->text->cursor.row()->baseline();