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);
2060 // get the cursor y position in text
2062 Row * row = getRow(par, pos, y);
2063 // y is now the beginning of the cursor row
2064 y += row->baseline();
2065 // y is now the cursor baseline
2068 // now get the cursors x position
2070 float fill_separator;
2072 float fill_label_hfill;
2073 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2075 pos_type cursor_vpos = 0;
2076 pos_type last = rowLastPrintable(row);
2078 if (pos > last + 1) {
2079 // This shouldn't happen.
2082 } else if (pos < row->pos()) {
2087 if (last < row->pos())
2088 cursor_vpos = row->pos();
2089 else if (pos > last && !boundary)
2090 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2091 ? row->pos() : last + 1;
2092 else if (pos > row->pos() &&
2093 (pos > last || boundary))
2094 /// Place cursor after char at (logical) position pos - 1
2095 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2096 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2098 /// Place cursor before char at (logical) position pos
2099 cursor_vpos = (bidi_level(pos) % 2 == 0)
2100 ? log2vis(pos) : log2vis(pos) + 1;
2102 pos_type main_body =
2103 beginningOfMainBody(bview->buffer(), row->par());
2104 if ((main_body > 0) &&
2105 ((main_body-1 > last) ||
2106 !row->par()->isLineSeparator(main_body-1)))
2109 for (pos_type vpos = row->pos();
2110 vpos < cursor_vpos; ++vpos) {
2111 pos = vis2log(vpos);
2112 if (main_body > 0 && pos == main_body - 1) {
2113 x += fill_label_hfill +
2114 lyxfont::width(textclasslist[
2115 bview->buffer()->params.textclass][
2116 row->par()->layout()]
2118 getLabelFont(bview->buffer(), row->par()));
2119 if (row->par()->isLineSeparator(main_body-1))
2120 x -= singleWidth(bview, row->par(),main_body-1);
2122 if (hfillExpansion(bview->buffer(), row, pos)) {
2123 x += singleWidth(bview, row->par(), pos);
2124 if (pos >= main_body)
2127 x += fill_label_hfill;
2128 } else if (row->par()->isSeparator(pos)) {
2129 x += singleWidth(bview, row->par(), pos);
2130 if (pos >= main_body)
2131 x += fill_separator;
2133 x += singleWidth(bview, row->par(), pos);
2142 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2143 pos_type pos, bool setfont, bool boundary) const
2145 InsetText * it = static_cast<InsetText *>(par->inInset());
2147 if (it != inset_owner) {
2148 lyxerr << "InsetText is " << it << endl;
2149 lyxerr << "inset_owner is " << inset_owner << endl;
2150 #ifdef WITH_WARNINGS
2151 #warning I believe this code is wrong. (Lgb)
2152 #warning Jürgen, have a look at this. (Lgb)
2153 #warning Hmmm, I guess you are right but we
2154 #warning should verify when this is needed
2156 // Jürgen, would you like to have a look?
2157 // I guess we need to move the outer cursor
2158 // and open and lock the inset (bla bla bla)
2159 // stuff I don't know... so can you have a look?
2161 // I moved the lyxerr stuff in here so we can see if
2162 // this is actually really needed and where!
2164 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2169 setCursor(bview, cursor, par, pos, boundary);
2171 setCurrentFont(bview);
2175 void LyXText::setCurrentFont(BufferView * bview) const
2177 pos_type pos = cursor.pos();
2178 if (cursor.boundary() && pos > 0)
2182 if (pos == cursor.par()->size())
2184 else // potentional bug... BUG (Lgb)
2185 if (cursor.par()->isSeparator(pos)) {
2186 if (pos > cursor.row()->pos() &&
2187 bidi_level(pos) % 2 ==
2188 bidi_level(pos - 1) % 2)
2190 else if (pos + 1 < cursor.par()->size())
2196 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2197 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2199 if (cursor.pos() == cursor.par()->size() &&
2200 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2201 !cursor.boundary()) {
2202 Language const * lang =
2203 cursor.par()->getParLanguage(bview->buffer()->params);
2204 current_font.setLanguage(lang);
2205 current_font.setNumber(LyXFont::OFF);
2206 real_current_font.setLanguage(lang);
2207 real_current_font.setNumber(LyXFont::OFF);
2212 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2214 LyXCursor old_cursor = cursor;
2216 setCursorFromCoordinates(bview, cursor, x, y);
2217 setCurrentFont(bview);
2218 deleteEmptyParagraphMechanism(bview, old_cursor);
2222 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2225 // Get the row first.
2227 Row * row = getRowNearY(y);
2229 pos_type const column = getColumnNearX(bview, row, x, bound);
2230 cur.par(row->par());
2231 cur.pos(row->pos() + column);
2233 cur.y(y + row->baseline());
2235 cur.boundary(bound);
2239 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2241 if (cursor.pos() > 0) {
2242 bool boundary = cursor.boundary();
2243 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2244 if (!internal && !boundary &&
2245 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2246 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2247 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2248 Paragraph * par = cursor.par()->previous();
2249 setCursor(bview, par, par->size());
2254 void LyXText::cursorRight(BufferView * bview, bool internal) const
2256 if (!internal && cursor.boundary() &&
2257 !cursor.par()->isNewline(cursor.pos()))
2258 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2259 else if (cursor.pos() < cursor.par()->size()) {
2260 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2262 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2263 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2264 } else if (cursor.par()->next())
2265 setCursor(bview, cursor.par()->next(), 0);
2269 void LyXText::cursorUp(BufferView * bview) const
2271 setCursorFromCoordinates(bview, cursor.x_fix(),
2272 cursor.y() - cursor.row()->baseline() - 1);
2276 void LyXText::cursorDown(BufferView * bview) const
2278 setCursorFromCoordinates(bview, cursor.x_fix(),
2279 cursor.y() - cursor.row()->baseline()
2280 + cursor.row()->height() + 1);
2284 void LyXText::cursorUpParagraph(BufferView * bview) const
2286 if (cursor.pos() > 0) {
2287 setCursor(bview, cursor.par(), 0);
2289 else if (cursor.par()->previous()) {
2290 setCursor(bview, cursor.par()->previous(), 0);
2295 void LyXText::cursorDownParagraph(BufferView * bview) const
2297 if (cursor.par()->next()) {
2298 setCursor(bview, cursor.par()->next(), 0);
2300 setCursor(bview, cursor.par(), cursor.par()->size());
2304 // fix the cursor `cur' after a characters has been deleted at `where'
2305 // position. Called by deleteEmptyParagraphMechanism
2306 void LyXText::fixCursorAfterDelete(BufferView * bview,
2308 LyXCursor const & where) const
2310 // if cursor is not in the paragraph where the delete occured,
2312 if (cur.par() != where.par())
2315 // if cursor position is after the place where the delete occured,
2317 if (cur.pos() > where.pos())
2318 cur.pos(cur.pos()-1);
2320 // recompute row et al. for this cursor
2321 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2325 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2326 LyXCursor const & old_cursor) const
2328 // Would be wrong to delete anything if we have a selection.
2329 if (selection.set())
2332 // We allow all kinds of "mumbo-jumbo" when freespacing.
2333 if (textclasslist[bview->buffer()->params.textclass][
2334 old_cursor.par()->layout()].free_spacing
2335 || old_cursor.par()->isFreeSpacing())
2340 /* Ok I'll put some comments here about what is missing.
2341 I have fixed BackSpace (and thus Delete) to not delete
2342 double-spaces automagically. I have also changed Cut,
2343 Copy and Paste to hopefully do some sensible things.
2344 There are still some small problems that can lead to
2345 double spaces stored in the document file or space at
2346 the beginning of paragraphs. This happens if you have
2347 the cursor betwenn to spaces and then save. Or if you
2348 cut and paste and the selection have a space at the
2349 beginning and then save right after the paste. I am
2350 sure none of these are very hard to fix, but I will
2351 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2352 that I can get some feedback. (Lgb)
2355 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2356 // delete the LineSeparator.
2359 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2360 // delete the LineSeparator.
2363 // If the pos around the old_cursor were spaces, delete one of them.
2364 if (old_cursor.par() != cursor.par()
2365 || old_cursor.pos() != cursor.pos()) {
2366 // Only if the cursor has really moved
2368 if (old_cursor.pos() > 0
2369 && old_cursor.pos() < old_cursor.par()->size()
2370 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2371 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2372 old_cursor.par()->erase(old_cursor.pos() - 1);
2373 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2375 #ifdef WITH_WARNINGS
2376 #warning This will not work anymore when we have multiple views of the same buffer
2377 // In this case, we will have to correct also the cursors held by
2378 // other bufferviews. It will probably be easier to do that in a more
2379 // automated way in LyXCursor code. (JMarc 26/09/2001)
2381 // correct all cursors held by the LyXText
2382 fixCursorAfterDelete(bview, cursor, old_cursor);
2383 fixCursorAfterDelete(bview, selection.cursor,
2385 fixCursorAfterDelete(bview, selection.start,
2387 fixCursorAfterDelete(bview, selection.end, old_cursor);
2388 fixCursorAfterDelete(bview, last_sel_cursor,
2390 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2391 fixCursorAfterDelete(bview, toggle_end_cursor,
2397 // don't delete anything if this is the ONLY paragraph!
2398 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2401 // Do not delete empty paragraphs with keepempty set.
2403 [bview->buffer()->params.textclass]
2404 [old_cursor.par()->layout()].keepempty)
2407 // only do our magic if we changed paragraph
2408 if (old_cursor.par() == cursor.par())
2411 // record if we have deleted a paragraph
2412 // we can't possibly have deleted a paragraph before this point
2413 bool deleted = false;
2415 if ((old_cursor.par()->size() == 0
2416 || (old_cursor.par()->size() == 1
2417 && old_cursor.par()->isLineSeparator(0)))) {
2418 // ok, we will delete anything
2419 LyXCursor tmpcursor;
2421 // make sure that you do not delete any environments
2422 status(bview, LyXText::NEED_MORE_REFRESH);
2425 if (old_cursor.row()->previous()) {
2426 refresh_row = old_cursor.row()->previous();
2427 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2429 cursor = old_cursor; // that undo can restore the right cursor position
2430 Paragraph * endpar = old_cursor.par()->next();
2431 if (endpar && endpar->getDepth()) {
2432 while (endpar && endpar->getDepth()) {
2433 endpar = endpar->next();
2436 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2440 removeRow(old_cursor.row());
2441 if (ownerParagraph() == old_cursor.par()) {
2442 ownerParagraph(ownerParagraph()->next());
2445 delete old_cursor.par();
2447 /* Breakagain the next par. Needed because of
2448 * the parindent that can occur or dissappear.
2449 * The next row can change its height, if
2450 * there is another layout before */
2451 if (refresh_row->next()) {
2452 breakAgain(bview, refresh_row->next());
2453 updateCounters(bview, refresh_row);
2455 setHeightOfRow(bview, refresh_row);
2457 refresh_row = old_cursor.row()->next();
2458 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2461 cursor = old_cursor; // that undo can restore the right cursor position
2462 Paragraph * endpar = old_cursor.par()->next();
2463 if (endpar && endpar->getDepth()) {
2464 while (endpar && endpar->getDepth()) {
2465 endpar = endpar->next();
2468 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2472 removeRow(old_cursor.row());
2474 if (ownerParagraph() == old_cursor.par()) {
2475 ownerParagraph(ownerParagraph()->next());
2478 delete old_cursor.par();
2480 /* Breakagain the next par. Needed because of
2481 the parindent that can occur or dissappear.
2482 The next row can change its height, if
2483 there is another layout before */
2485 breakAgain(bview, refresh_row);
2486 updateCounters(bview, refresh_row->previous());
2491 setCursorIntern(bview, cursor.par(), cursor.pos());
2493 if (selection.cursor.par() == old_cursor.par()
2494 && selection.cursor.pos() == old_cursor.pos()) {
2495 // correct selection
2496 selection.cursor = cursor;
2500 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2501 redoParagraphs(bview, old_cursor,
2502 old_cursor.par()->next());
2504 setCursorIntern(bview, cursor.par(), cursor.pos());
2505 selection.cursor = cursor;
2512 void LyXText::toggleAppendix(BufferView * bview)
2514 Paragraph * par = cursor.par();
2515 bool start = !par->params().startOfAppendix();
2517 // ensure that we have only one start_of_appendix in this document
2518 Paragraph * tmp = ownerParagraph();
2519 for (; tmp; tmp = tmp->next()) {
2520 tmp->params().startOfAppendix(false);
2523 par->params().startOfAppendix(start);
2525 // we can set the refreshing parameters now
2526 status(bview, LyXText::NEED_MORE_REFRESH);
2528 refresh_row = 0; // not needed for full update
2529 updateCounters(bview, 0);
2530 setCursor(bview, cursor.par(), cursor.pos());
2534 Paragraph * LyXText::ownerParagraph() const
2537 return inset_owner->paragraph();
2539 return bv_owner->buffer()->paragraph;
2543 void LyXText::ownerParagraph(Paragraph * p) const
2546 inset_owner->paragraph(p);
2548 bv_owner->buffer()->paragraph = p;
2553 void LyXText::ownerParagraph(int id, Paragraph * p) const
2555 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2556 if (op && op->inInset()) {
2557 static_cast<InsetText *>(op->inInset())->paragraph(p);
2564 LyXText::text_status LyXText::status() const
2570 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2572 // well as much as I know && binds more then || so the above and the
2573 // below are identical (this for your known use of parentesis!)
2574 // Now some explanation:
2575 // We should only go up with refreshing code so this means that if
2576 // we have a MORE refresh we should never set it to LITTLE if we still
2577 // didn't handle it (and then it will be UNCHANGED. Now as long as
2578 // we stay inside one LyXText this may work but we need to tell the
2579 // outermost LyXText that it should REALLY draw us if there is some
2580 // change in a Inset::LyXText. So you see that when we are inside a
2581 // inset's LyXText we give the LITTLE to the outermost LyXText to
2582 // tell'em that it should redraw the actual row (where the inset
2583 // resides! Capito?!
2585 if ((status_ != NEED_MORE_REFRESH)
2586 || (status_ == NEED_MORE_REFRESH
2587 && st != NEED_VERY_LITTLE_REFRESH))
2590 if (inset_owner && st != UNCHANGED) {
2591 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2592 if (!bview->text->refresh_row) {
2593 bview->text->refresh_row = bview->text->cursor.row();
2594 bview->text->refresh_y = bview->text->cursor.y() -
2595 bview->text->cursor.row()->baseline();