1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
28 #include "CutAndPaste.h"
34 #include "FloatList.h"
36 #include "ParagraphParameters.h"
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
66 LyXText::LyXText(InsetText * inset)
67 : number_of_rows(0), height(0), width(0), first(0),
68 bv_owner(0), inset_owner(inset), the_locking_inset(0),
69 need_break_row(0), refresh_y(0), refresh_row(0),
70 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
74 void LyXText::init(BufferView * bview, bool reinit)
77 // Delete all rows, this does not touch the paragraphs!
78 Row * tmprow = firstrow;
80 tmprow = firstrow->next();
84 lastrow = refresh_row = need_break_row = 0;
85 width = height = copylayouttype = 0;
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font);
132 tmpfont.realize(textclasslist.
133 Style(buf->params.textclass,
134 par->getLayout()).font,
135 buf->params.language);
137 par_depth = par->getDepth();
141 #ifndef INHERIT_LANGUAGE
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
144 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
145 buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout const & layout =
167 textclasslist.Style(buf->params.textclass, par->getLayout());
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout.labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params, pos);
177 par->inInset()->getDrawFont(f);
178 #ifndef INHERIT_LANGUAGE
179 return f.realize(layout.reslabelfont);
181 return f.realize(layout.reslabelfont, buf->params.language);
184 LyXFont f = par->getFontSettings(buf->params, pos);
186 par->inInset()->getDrawFont(f);
187 #ifndef INHERIT_LANGUAGE
188 return f.realize(layout.resfont);
190 return f.realize(layout.resfont, buf->params.language);
195 // The uncommon case need not be optimized as much
199 if (pos < beginningOfMainBody(buf, par)) {
201 layoutfont = layout.labelfont;
204 layoutfont = layout.font;
207 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
208 #ifndef INHERIT_LANGUAGE
209 tmpfont.realize(layoutfont);
211 tmpfont.realize(layoutfont, buf->params.language);
214 par->inInset()->getDrawFont(tmpfont);
216 return realizeFont(tmpfont, buf, par);
220 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
222 LyXLayout const & layout =
223 textclasslist.Style(buf->params.textclass, par->getLayout());
225 Paragraph::depth_type par_depth = par->getDepth();
228 return layout.resfont;
231 return realizeFont(layout.font, buf, par);
235 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
237 LyXLayout const & layout =
238 textclasslist.Style(buf->params.textclass, par->getLayout());
240 Paragraph::depth_type par_depth = par->getDepth();
243 return layout.reslabelfont;
246 return realizeFont(layout.labelfont, buf, par);
250 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
251 pos_type pos, LyXFont const & fnt,
254 Buffer const * buf = bv->buffer();
255 LyXFont font = getFont(buf, par, pos);
256 font.update(fnt, buf->params.language, toggleall);
257 // Let the insets convert their font
258 if (par->isInset(pos)) {
259 Inset * inset = par->getInset(pos);
260 if (isEditableInset(inset)) {
261 UpdatableInset * uinset =
262 static_cast<UpdatableInset *>(inset);
263 uinset->setFont(bv, fnt, toggleall, true);
267 // Plug thru to version below:
268 setCharFont(buf, par, pos, font);
273 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
274 pos_type pos, LyXFont const & fnt)
278 LyXLayout const & layout =
279 textclasslist.Style(buf->params.textclass,
282 // Get concrete layout font to reduce against
285 if (pos < beginningOfMainBody(buf, par))
286 layoutfont = layout.labelfont;
288 layoutfont = layout.font;
290 // Realize against environment font information
291 if (par->getDepth()) {
292 Paragraph * tp = par;
293 while (!layoutfont.resolved() && tp && tp->getDepth()) {
294 tp = tp->outerHook();
296 #ifndef INHERIT_LANGUAGE
297 layoutfont.realize(textclasslist.
298 Style(buf->params.textclass,
299 tp->getLayout()).font);
301 layoutfont.realize(textclasslist.
302 Style(buf->params.textclass,
303 tp->getLayout()).font,
304 buf->params.language);
309 #ifndef INHERIT_LANGUAGE
310 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
312 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
313 buf->params.language);
316 // Now, reduce font against full layout font
317 font.reduce(layoutfont);
319 par->setFont(pos, font);
323 // inserts a new row behind the specified row, increments
324 // the touched counters
325 void LyXText::insertRow(Row * row, Paragraph * par,
328 Row * tmprow = new Row;
331 tmprow->next(firstrow);
334 tmprow->previous(row);
335 tmprow->next(row->next());
340 tmprow->next()->previous(tmprow);
342 if (tmprow->previous())
343 tmprow->previous()->next(tmprow);
355 // removes the row and reset the touched counters
356 void LyXText::removeRow(Row * row) const
358 Row * row_prev = row->previous();
360 row->next()->previous(row_prev);
362 firstrow = row->next();
363 // lyx::Assert(firstrow);
365 row_prev->next(row->next());
367 if (row == lastrow) {
368 lyx::Assert(!row->next());
371 if (refresh_row == row) {
372 refresh_row = row_prev ? row_prev : row->next();
373 // what about refresh_y, refresh_height
376 height -= row->height(); // the text becomes smaller
379 --number_of_rows; // one row less
383 // remove all following rows of the paragraph of the specified row.
384 void LyXText::removeParagraph(Row * row) const
386 Paragraph * tmppar = row->par();
390 while (row && row->par() == tmppar) {
391 tmprow = row->next();
398 // insert the specified paragraph behind the specified row
399 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
402 insertRow(row, par, 0); /* insert a new row, starting
405 setCounter(bview->buffer(), par); // set the counters
407 // and now append the whole paragraph behind the new row
410 appendParagraph(bview, firstrow);
412 row->next()->height(0);
413 appendParagraph(bview, row->next());
418 Inset * LyXText::getInset() const
421 if (cursor.pos() == 0 && cursor.par()->bibkey) {
422 inset = cursor.par()->bibkey;
423 } else if (cursor.pos() < cursor.par()->size()
424 && cursor.par()->isInset(cursor.pos())) {
425 inset = cursor.par()->getInset(cursor.pos());
431 void LyXText::toggleInset(BufferView * bview)
433 Inset * inset = getInset();
434 // is there an editable inset at cursor position?
435 if (!isEditableInset(inset)) {
436 // No, try to see if we are inside a collapsable inset
437 if (inset_owner && inset_owner->owner()
438 && inset_owner->owner()->isOpen()) {
439 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
440 inset_owner->owner()->close(bview);
444 //bview->owner()->message(inset->editMessage());
446 // do we want to keep this?? (JMarc)
447 if (!isHighlyEditableInset(inset))
448 setCursorParUndo(bview);
450 if (inset->isOpen()) {
456 inset->open(bview, !inset->isOpen());
461 /* used in setlayout */
462 // Asger is not sure we want to do this...
463 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
466 LyXLayout const & layout =
467 textclasslist.Style(buf->params.textclass, par->getLayout());
470 for (pos_type pos = 0; pos < par->size(); ++pos) {
471 if (pos < beginningOfMainBody(buf, par))
472 layoutfont = layout.labelfont;
474 layoutfont = layout.font;
476 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
477 tmpfont.reduce(layoutfont);
478 par->setFont(pos, tmpfont);
483 Paragraph * LyXText::setLayout(BufferView * bview,
484 LyXCursor & cur, LyXCursor & sstart_cur,
485 LyXCursor & send_cur,
486 lyx::layout_type layout)
488 Paragraph * endpar = send_cur.par()->next();
489 Paragraph * undoendpar = endpar;
491 if (endpar && endpar->getDepth()) {
492 while (endpar && endpar->getDepth()) {
493 endpar = endpar->next();
497 endpar = endpar->next(); // because of parindents etc.
500 setUndo(bview, Undo::EDIT,
501 sstart_cur.par(), undoendpar);
503 // ok we have a selection. This is always between sstart_cur
504 // and sel_end cursor
507 LyXLayout const & lyxlayout =
508 textclasslist.Style(bview->buffer()->params.textclass, layout);
511 cur.par()->setLayout(layout);
512 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
513 Paragraph * fppar = cur.par();
514 fppar->params().spaceTop(lyxlayout.fill_top ?
515 VSpace(VSpace::VFILL)
516 : VSpace(VSpace::NONE));
517 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
518 VSpace(VSpace::VFILL)
519 : VSpace(VSpace::NONE));
520 if (lyxlayout.margintype == MARGIN_MANUAL)
521 cur.par()->setLabelWidthString(lyxlayout.labelstring());
522 if (lyxlayout.labeltype != LABEL_BIBLIO
524 delete fppar->bibkey;
527 if (cur.par() != send_cur.par())
528 cur.par(cur.par()->next());
529 } while (cur.par() != send_cur.par());
535 // set layout over selection and make a total rebreak of those paragraphs
536 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
538 LyXCursor tmpcursor = cursor; /* store the current cursor */
540 // if there is no selection just set the layout
541 // of the current paragraph */
542 if (!selection.set()) {
543 selection.start = cursor; // dummy selection
544 selection.end = cursor;
546 Paragraph * endpar = setLayout(bview, cursor, selection.start,
547 selection.end, layout);
548 redoParagraphs(bview, selection.start, endpar);
550 // we have to reset the selection, because the
551 // geometry could have changed
552 setCursor(bview, selection.start.par(),
553 selection.start.pos(), false);
554 selection.cursor = cursor;
555 setCursor(bview, selection.end.par(), selection.end.pos(), false);
556 updateCounters(bview, cursor.row());
559 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
563 // increment depth over selection and
564 // make a total rebreak of those paragraphs
565 void LyXText::incDepth(BufferView * bview)
567 // If there is no selection, just use the current paragraph
568 if (!selection.set()) {
569 selection.start = cursor; // dummy selection
570 selection.end = cursor;
573 // We end at the next paragraph with depth 0
574 Paragraph * endpar = selection.end.par()->next();
576 Paragraph * undoendpar = endpar;
578 if (endpar && endpar->getDepth()) {
579 while (endpar && endpar->getDepth()) {
580 endpar = endpar->next();
584 endpar = endpar->next(); // because of parindents etc.
587 setUndo(bview, Undo::EDIT,
588 selection.start.par(), undoendpar);
590 LyXCursor tmpcursor = cursor; // store the current cursor
592 // ok we have a selection. This is always between sel_start_cursor
593 // and sel_end cursor
594 cursor = selection.start;
596 bool anything_changed = false;
599 // NOTE: you can't change the depth of a bibliography entry
600 if (textclasslist.Style(bview->buffer()->params.textclass,
601 cursor.par()->getLayout()).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.Style(buf->params.textclass,
1142 par->getLayout()).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 while (tmppar != selection.start.par()->previous()) {
1191 setCursor(bview, tmppar, 0);
1192 status(bview, LyXText::NEED_MORE_REFRESH);
1193 refresh_row = cursor.row();
1194 refresh_y = cursor.y() - cursor.row()->baseline();
1195 cursor.par()->params().lineTop(line_top);
1196 cursor.par()->params().lineBottom(line_bottom);
1197 cursor.par()->params().pagebreakTop(pagebreak_top);
1198 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1199 cursor.par()->params().spaceTop(space_top);
1200 cursor.par()->params().spaceBottom(space_bottom);
1201 cursor.par()->params().spacing(spacing);
1202 // does the layout allow the new alignment?
1203 if (align == LYX_ALIGN_LAYOUT)
1204 align = textclasslist
1205 .Style(bview->buffer()->params.textclass,
1206 cursor.par()->getLayout()).align;
1207 if (align & textclasslist
1208 .Style(bview->buffer()->params.textclass,
1209 cursor.par()->getLayout()).alignpossible) {
1210 if (align == textclasslist
1211 .Style(bview->buffer()->params.textclass,
1212 cursor.par()->getLayout()).align)
1213 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1215 cursor.par()->params().align(align);
1217 cursor.par()->setLabelWidthString(labelwidthstring);
1218 cursor.par()->params().noindent(noindent);
1219 tmppar = cursor.par()->previous();
1222 redoParagraphs(bview, selection.start, endpar);
1225 setCursor(bview, selection.start.par(), selection.start.pos());
1226 selection.cursor = cursor;
1227 setCursor(bview, selection.end.par(), selection.end.pos());
1228 setSelection(bview);
1229 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1231 bview->updateInset(inset_owner, true);
1235 char loweralphaCounter(int n)
1237 if (n < 1 || n > 26)
1247 char alphaCounter(int n)
1249 if (n < 1 || n > 26)
1257 char hebrewCounter(int n)
1259 static const char hebrew[22] = {
1260 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1261 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1262 '÷', 'ø', 'ù', 'ú'
1264 if (n < 1 || n > 22)
1272 string const romanCounter(int n)
1274 static char const * roman[20] = {
1275 "i", "ii", "iii", "iv", "v",
1276 "vi", "vii", "viii", "ix", "x",
1277 "xi", "xii", "xiii", "xiv", "xv",
1278 "xvi", "xvii", "xviii", "xix", "xx"
1280 if (n < 1 || n > 20)
1289 // set the counter of a paragraph. This includes the labels
1290 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1292 LyXLayout const & layout =
1293 textclasslist.Style(buf->params.textclass,
1296 LyXTextClass const & textclass =
1297 textclasslist.TextClass(buf->params.textclass);
1299 // copy the prev-counters to this one,
1300 // unless this is the first paragraph
1301 if (par->previous()) {
1302 for (int i = 0; i < 10; ++i) {
1303 par->setCounter(i, par->previous()->getFirstCounter(i));
1305 par->params().appendix(par->previous()->params().appendix());
1306 if (!par->params().appendix() && par->params().startOfAppendix()) {
1307 par->params().appendix(true);
1308 for (int i = 0; i < 10; ++i) {
1309 par->setCounter(i, 0);
1312 par->enumdepth = par->previous()->enumdepth;
1313 par->itemdepth = par->previous()->itemdepth;
1315 for (int i = 0; i < 10; ++i) {
1316 par->setCounter(i, 0);
1318 par->params().appendix(par->params().startOfAppendix());
1323 /* Maybe we have to increment the enumeration depth.
1324 * BUT, enumeration in a footnote is considered in isolation from its
1325 * surrounding paragraph so don't increment if this is the
1326 * first line of the footnote
1327 * AND, bibliographies can't have their depth changed ie. they
1328 * are always of depth 0
1331 && par->previous()->getDepth() < par->getDepth()
1332 && textclasslist.Style(buf->params.textclass,
1333 par->previous()->getLayout()
1334 ).labeltype == LABEL_COUNTER_ENUMI
1335 && par->enumdepth < 3
1336 && layout.labeltype != LABEL_BIBLIO) {
1340 // Maybe we have to decrement the enumeration depth, see note above
1342 && par->previous()->getDepth() > par->getDepth()
1343 && layout.labeltype != LABEL_BIBLIO) {
1344 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1345 par->setCounter(6 + par->enumdepth,
1346 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1347 /* reset the counters.
1348 * A depth change is like a breaking layout
1350 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1351 par->setCounter(i, 0);
1354 if (!par->params().labelString().empty()) {
1355 par->params().labelString(string());
1358 if (layout.margintype == MARGIN_MANUAL) {
1359 if (par->params().labelWidthString().empty()) {
1360 par->setLabelWidthString(layout.labelstring());
1363 par->setLabelWidthString(string());
1366 // is it a layout that has an automatic label?
1367 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1369 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1370 if (i >= 0 && i<= buf->params.secnumdepth) {
1371 par->incCounter(i); // increment the counter
1373 // Is there a label? Useful for Chapter layout
1374 if (!par->params().appendix()) {
1375 if (!layout.labelstring().empty())
1376 par->params().labelString(layout.labelstring());
1378 par->params().labelString(string());
1380 if (!layout.labelstring_appendix().empty())
1381 par->params().labelString(layout.labelstring_appendix());
1383 par->params().labelString(string());
1388 if (!par->params().appendix()) {
1389 switch (2 * LABEL_COUNTER_CHAPTER -
1390 textclass.maxcounter() + i) {
1391 case LABEL_COUNTER_CHAPTER:
1392 s << par->getCounter(i);
1394 case LABEL_COUNTER_SECTION:
1395 s << par->getCounter(i - 1) << '.'
1396 << par->getCounter(i);
1398 case LABEL_COUNTER_SUBSECTION:
1399 s << par->getCounter(i - 2) << '.'
1400 << par->getCounter(i - 1) << '.'
1401 << par->getCounter(i);
1403 case LABEL_COUNTER_SUBSUBSECTION:
1404 s << par->getCounter(i - 3) << '.'
1405 << par->getCounter(i - 2) << '.'
1406 << par->getCounter(i - 1) << '.'
1407 << par->getCounter(i);
1410 case LABEL_COUNTER_PARAGRAPH:
1411 s << par->getCounter(i - 4) << '.'
1412 << par->getCounter(i - 3) << '.'
1413 << par->getCounter(i - 2) << '.'
1414 << par->getCounter(i - 1) << '.'
1415 << par->getCounter(i);
1417 case LABEL_COUNTER_SUBPARAGRAPH:
1418 s << par->getCounter(i - 5) << '.'
1419 << par->getCounter(i - 4) << '.'
1420 << par->getCounter(i - 3) << '.'
1421 << par->getCounter(i - 2) << '.'
1422 << par->getCounter(i - 1) << '.'
1423 << par->getCounter(i);
1427 // Can this ever be reached? And in the
1428 // case it is, how can this be correct?
1430 s << par->getCounter(i) << '.';
1433 } else { // appendix
1434 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1435 case LABEL_COUNTER_CHAPTER:
1436 if (par->isRightToLeftPar(buf->params))
1437 s << hebrewCounter(par->getCounter(i));
1439 s << alphaCounter(par->getCounter(i));
1441 case LABEL_COUNTER_SECTION:
1442 if (par->isRightToLeftPar(buf->params))
1443 s << hebrewCounter(par->getCounter(i - 1));
1445 s << alphaCounter(par->getCounter(i - 1));
1448 << par->getCounter(i);
1451 case LABEL_COUNTER_SUBSECTION:
1452 if (par->isRightToLeftPar(buf->params))
1453 s << hebrewCounter(par->getCounter(i - 2));
1455 s << alphaCounter(par->getCounter(i - 2));
1458 << par->getCounter(i-1) << '.'
1459 << par->getCounter(i);
1462 case LABEL_COUNTER_SUBSUBSECTION:
1463 if (par->isRightToLeftPar(buf->params))
1464 s << hebrewCounter(par->getCounter(i-3));
1466 s << alphaCounter(par->getCounter(i-3));
1469 << par->getCounter(i-2) << '.'
1470 << par->getCounter(i-1) << '.'
1471 << par->getCounter(i);
1474 case LABEL_COUNTER_PARAGRAPH:
1475 if (par->isRightToLeftPar(buf->params))
1476 s << hebrewCounter(par->getCounter(i-4));
1478 s << alphaCounter(par->getCounter(i-4));
1481 << par->getCounter(i-3) << '.'
1482 << par->getCounter(i-2) << '.'
1483 << par->getCounter(i-1) << '.'
1484 << par->getCounter(i);
1487 case LABEL_COUNTER_SUBPARAGRAPH:
1488 if (par->isRightToLeftPar(buf->params))
1489 s << hebrewCounter(par->getCounter(i-5));
1491 s << alphaCounter(par->getCounter(i-5));
1494 << par->getCounter(i-4) << '.'
1495 << par->getCounter(i-3) << '.'
1496 << par->getCounter(i-2) << '.'
1497 << par->getCounter(i-1) << '.'
1498 << par->getCounter(i);
1502 // Can this ever be reached? And in the
1503 // case it is, how can this be correct?
1505 s << par->getCounter(i) << '.';
1511 par->params().labelString(par->params().labelString() +s.str().c_str());
1512 // We really want to remove the c_str as soon as
1515 for (i++; i < 10; ++i) {
1516 // reset the following counters
1517 par->setCounter(i, 0);
1519 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1520 for (i++; i < 10; ++i) {
1521 // reset the following counters
1522 par->setCounter(i, 0);
1524 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1525 par->incCounter(i + par->enumdepth);
1526 int number = par->getCounter(i + par->enumdepth);
1530 switch (par->enumdepth) {
1532 if (par->isRightToLeftPar(buf->params))
1534 << hebrewCounter(number)
1538 << loweralphaCounter(number)
1542 if (par->isRightToLeftPar(buf->params))
1543 s << '.' << romanCounter(number);
1545 s << romanCounter(number) << '.';
1548 if (par->isRightToLeftPar(buf->params))
1550 << alphaCounter(number);
1552 s << alphaCounter(number)
1556 if (par->isRightToLeftPar(buf->params))
1563 par->params().labelString(s.str().c_str());
1565 for (i += par->enumdepth + 1; i < 10; ++i) {
1566 // reset the following counters
1567 par->setCounter(i, 0);
1571 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1572 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1574 int number = par->getCounter(i);
1576 InsetCommandParams p("bibitem" );
1577 par->bibkey = new InsetBibKey(p);
1579 par->bibkey->setCounter(number);
1580 par->params().labelString(layout.labelstring());
1582 // In biblio should't be following counters but...
1584 string s = layout.labelstring();
1586 // the caption hack:
1587 if (layout.labeltype == LABEL_SENSITIVE) {
1588 bool isOK (par->inInset() && par->inInset()->owner() &&
1589 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1592 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1594 = floatList.getType(tmp->type());
1595 // We should get the correct number here too.
1596 s = fl.name() + " #:";
1598 /* par->SetLayout(0);
1599 s = layout->labelstring; */
1600 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1601 ? " :úåòîùî øñç" : "Senseless: ";
1604 par->params().labelString(s);
1606 /* reset the enumeration counter. They are always resetted
1607 * when there is any other layout between */
1608 for (int i = 6 + par->enumdepth; i < 10; ++i)
1609 par->setCounter(i, 0);
1614 // Updates all counters BEHIND the row. Changed paragraphs
1615 // with a dynamic left margin will be rebroken.
1616 void LyXText::updateCounters(BufferView * bview, Row * row) const
1624 par = row->par()->next();
1628 while (row->par() != par)
1631 setCounter(bview->buffer(), par);
1633 // now check for the headline layouts. remember that they
1634 // have a dynamic left margin
1635 if ((textclasslist.Style(bview->buffer()->params.textclass,
1636 par->layout).margintype == MARGIN_DYNAMIC
1637 || textclasslist.Style(bview->buffer()->params.textclass,
1638 par->layout).labeltype == LABEL_SENSITIVE)) {
1640 // Rebreak the paragraph
1641 removeParagraph(row);
1642 appendParagraph(bview, row);
1649 void LyXText::insertInset(BufferView * bview, Inset * inset)
1651 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1653 // I don't know if this is necessary here (Jug 20020102)
1654 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1655 cursor.par()->insertInset(cursor.pos(), inset);
1656 // Just to rebreak and refresh correctly.
1657 // The character will not be inserted a second time
1658 insertChar(bview, Paragraph::META_INSET);
1660 // If we enter a highly editable inset the cursor should be to before
1661 // the inset. This couldn't happen before as Undo was not handled inside
1662 // inset now after the Undo LyX tries to call inset->Edit(...) again
1663 // and cannot do this as the cursor is behind the inset and GetInset
1664 // does not return the inset!
1665 if (isHighlyEditableInset(inset)) {
1666 cursorLeft(bview, true);
1672 void LyXText::copyEnvironmentType()
1674 copylayouttype = cursor.par()->getLayout();
1678 void LyXText::pasteEnvironmentType(BufferView * bview)
1680 setLayout(bview, copylayouttype);
1684 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1686 // Stuff what we got on the clipboard. Even if there is no selection.
1688 // There is a problem with having the stuffing here in that the
1689 // larger the selection the slower LyX will get. This can be
1690 // solved by running the line below only when the selection has
1691 // finished. The solution used currently just works, to make it
1692 // faster we need to be more clever and probably also have more
1693 // calls to stuffClipboard. (Lgb)
1694 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1696 // This doesn't make sense, if there is no selection
1697 if (!selection.set())
1700 // OK, we have a selection. This is always between selection.start
1701 // and selection.end
1703 // make sure that the depth behind the selection are restored, too
1704 Paragraph * endpar = selection.end.par()->next();
1705 Paragraph * undoendpar = endpar;
1707 if (endpar && endpar->getDepth()) {
1708 while (endpar && endpar->getDepth()) {
1709 endpar = endpar->next();
1710 undoendpar = endpar;
1712 } else if (endpar) {
1713 endpar = endpar->next(); // because of parindents etc.
1716 setUndo(bview, Undo::DELETE,
1717 selection.start.par(), undoendpar);
1719 // there are two cases: cut only within one paragraph or
1720 // more than one paragraph
1721 if (selection.start.par() == selection.end.par()) {
1722 // only within one paragraph
1723 endpar = selection.end.par();
1724 int pos = selection.end.pos();
1725 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1726 selection.start.pos(), pos,
1727 bview->buffer()->params.textclass,
1729 selection.end.pos(pos);
1731 endpar = selection.end.par();
1732 int pos = selection.end.pos();
1733 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1734 selection.start.pos(), pos,
1735 bview->buffer()->params.textclass,
1738 selection.end.par(endpar);
1739 selection.end.pos(pos);
1740 cursor.pos(selection.end.pos());
1742 endpar = endpar->next();
1744 // sometimes necessary
1746 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1748 redoParagraphs(bview, selection.start, endpar);
1750 // cutSelection can invalidate the cursor so we need to set
1752 cursor = selection.start;
1754 // need a valid cursor. (Lgb)
1757 setCursor(bview, cursor.par(), cursor.pos());
1758 selection.cursor = cursor;
1759 updateCounters(bview, cursor.row());
1763 void LyXText::copySelection(BufferView * bview)
1765 // stuff the selection onto the X clipboard, from an explicit copy request
1766 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1768 // this doesnt make sense, if there is no selection
1769 if (!selection.set())
1772 // ok we have a selection. This is always between selection.start
1773 // and sel_end cursor
1775 // copy behind a space if there is one
1776 while (selection.start.par()->size() > selection.start.pos()
1777 && selection.start.par()->isLineSeparator(selection.start.pos())
1778 && (selection.start.par() != selection.end.par()
1779 || selection.start.pos() < selection.end.pos()))
1780 selection.start.pos(selection.start.pos() + 1);
1782 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1783 selection.start.pos(), selection.end.pos(),
1784 bview->buffer()->params.textclass);
1788 void LyXText::pasteSelection(BufferView * bview)
1790 // this does not make sense, if there is nothing to paste
1791 if (!CutAndPaste::checkPastePossible(cursor.par()))
1794 setUndo(bview, Undo::INSERT,
1795 cursor.par(), cursor.par()->next());
1798 Paragraph * actpar = cursor.par();
1799 int pos = cursor.pos();
1801 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1802 bview->buffer()->params.textclass);
1804 redoParagraphs(bview, cursor, endpar);
1806 setCursor(bview, cursor.par(), cursor.pos());
1809 setCursor(bview, actpar, pos);
1810 updateCounters(bview, cursor.row());
1814 // sets the selection over the number of characters of string, no check!!
1815 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1820 selection.cursor = cursor;
1821 for (string::size_type i = 0; i < str.length(); ++i)
1823 setSelection(bview);
1827 // simple replacing. The font of the first selected character is used
1828 void LyXText::replaceSelectionWithString(BufferView * bview,
1831 setCursorParUndo(bview);
1834 if (!selection.set()) { // create a dummy selection
1835 selection.end = cursor;
1836 selection.start = cursor;
1839 // Get font setting before we cut
1840 pos_type pos = selection.end.pos();
1841 LyXFont const font = selection.start.par()
1842 ->getFontSettings(bview->buffer()->params,
1843 selection.start.pos());
1845 // Insert the new string
1846 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1847 selection.end.par()->insertChar(pos, (*cit), font);
1851 // Cut the selection
1852 cutSelection(bview, true, false);
1858 // needed to insert the selection
1859 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1861 Paragraph * par = cursor.par();
1862 pos_type pos = cursor.pos();
1863 Paragraph * endpar = cursor.par()->next();
1865 setCursorParUndo(bview);
1867 // only to be sure, should not be neccessary
1870 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1872 redoParagraphs(bview, cursor, endpar);
1873 setCursor(bview, cursor.par(), cursor.pos());
1874 selection.cursor = cursor;
1875 setCursor(bview, par, pos);
1876 setSelection(bview);
1880 // turns double-CR to single CR, others where converted into one
1881 // blank. Then InsertStringAsLines is called
1882 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1884 string linestr(str);
1885 bool newline_inserted = false;
1886 for (string::size_type i = 0; i < linestr.length(); ++i) {
1887 if (linestr[i] == '\n') {
1888 if (newline_inserted) {
1889 // we know that \r will be ignored by
1890 // InsertStringA. Of course, it is a dirty
1891 // trick, but it works...
1892 linestr[i - 1] = '\r';
1896 newline_inserted = true;
1898 } else if (IsPrintable(linestr[i])) {
1899 newline_inserted = false;
1902 insertStringAsLines(bview, linestr);
1906 bool LyXText::gotoNextInset(BufferView * bview,
1907 vector<Inset::Code> const & codes,
1908 string const & contents) const
1910 LyXCursor res = cursor;
1913 if (res.pos() < res.par()->size() - 1) {
1914 res.pos(res.pos() + 1);
1916 res.par(res.par()->next());
1920 } while (res.par() &&
1921 !(res.par()->isInset(res.pos())
1922 && (inset = res.par()->getInset(res.pos())) != 0
1923 && find(codes.begin(), codes.end(), inset->lyxCode())
1925 && (contents.empty() ||
1926 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1930 setCursor(bview, res.par(), res.pos(), false);
1937 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1940 LyXCursor tmpcursor;
1944 Row * row = getRow(par, pos, y);
1946 // is there a break one row above
1947 if (row->previous() && row->previous()->par() == row->par()) {
1948 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1949 if (z >= row->pos()) {
1950 // set the dimensions of the row above
1951 y -= row->previous()->height();
1953 refresh_row = row->previous();
1954 status(bview, LyXText::NEED_MORE_REFRESH);
1956 breakAgain(bview, row->previous());
1958 // set the cursor again. Otherwise
1959 // dangling pointers are possible
1960 setCursor(bview, cursor.par(), cursor.pos(),
1961 false, cursor.boundary());
1962 selection.cursor = cursor;
1967 int const tmpheight = row->height();
1968 pos_type const tmplast = rowLast(row);
1972 breakAgain(bview, row);
1973 if (row->height() == tmpheight && rowLast(row) == tmplast)
1974 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1976 status(bview, LyXText::NEED_MORE_REFRESH);
1978 // check the special right address boxes
1979 if (textclasslist.Style(bview->buffer()->params.textclass,
1980 par->getLayout()).margintype
1981 == MARGIN_RIGHT_ADDRESS_BOX)
1989 redoDrawingOfParagraph(bview, tmpcursor);
1992 // set the cursor again. Otherwise dangling pointers are possible
1993 // also set the selection
1995 if (selection.set()) {
1997 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1998 false, selection.cursor.boundary());
1999 selection.cursor = cursor;
2000 setCursorIntern(bview, selection.start.par(),
2001 selection.start.pos(),
2002 false, selection.start.boundary());
2003 selection.start = cursor;
2004 setCursorIntern(bview, selection.end.par(),
2005 selection.end.pos(),
2006 false, selection.end.boundary());
2007 selection.end = cursor;
2008 setCursorIntern(bview, last_sel_cursor.par(),
2009 last_sel_cursor.pos(),
2010 false, last_sel_cursor.boundary());
2011 last_sel_cursor = cursor;
2014 setCursorIntern(bview, cursor.par(), cursor.pos(),
2015 false, cursor.boundary());
2019 // returns false if inset wasn't found
2020 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2022 // first check the current paragraph
2023 int pos = cursor.par()->getPositionOfInset(inset);
2025 checkParagraph(bview, cursor.par(), pos);
2029 // check every paragraph
2031 Paragraph * par = ownerParagraph();
2033 pos = par->getPositionOfInset(inset);
2035 checkParagraph(bview, par, pos);
2045 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2047 bool setfont, bool boundary) const
2049 LyXCursor old_cursor = cursor;
2050 setCursorIntern(bview, par, pos, setfont, boundary);
2051 return deleteEmptyParagraphMechanism(bview, old_cursor);
2055 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2056 pos_type pos, bool boundary) const
2063 cur.boundary(boundary);
2065 // get the cursor y position in text
2067 Row * row = getRow(par, pos, y);
2068 // y is now the beginning of the cursor row
2069 y += row->baseline();
2070 // y is now the cursor baseline
2073 // now get the cursors x position
2075 float fill_separator;
2077 float fill_label_hfill;
2078 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2080 pos_type cursor_vpos = 0;
2081 pos_type last = rowLastPrintable(row);
2083 if (pos > last + 1) {
2084 // This shouldn't happen.
2087 } else if (pos < row->pos()) {
2092 if (last < row->pos())
2093 cursor_vpos = row->pos();
2094 else if (pos > last && !boundary)
2095 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2096 ? row->pos() : last + 1;
2097 else if (pos > row->pos() &&
2098 (pos > last || boundary))
2099 /// Place cursor after char at (logical) position pos - 1
2100 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2101 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2103 /// Place cursor before char at (logical) position pos
2104 cursor_vpos = (bidi_level(pos) % 2 == 0)
2105 ? log2vis(pos) : log2vis(pos) + 1;
2107 pos_type main_body =
2108 beginningOfMainBody(bview->buffer(), row->par());
2109 if ((main_body > 0) &&
2110 ((main_body-1 > last) ||
2111 !row->par()->isLineSeparator(main_body-1)))
2114 for (pos_type vpos = row->pos();
2115 vpos < cursor_vpos; ++vpos) {
2116 pos = vis2log(vpos);
2117 if (main_body > 0 && pos == main_body - 1) {
2118 x += fill_label_hfill +
2119 lyxfont::width(textclasslist.Style(
2120 bview->buffer()->params.textclass,
2121 row->par()->getLayout())
2123 getLabelFont(bview->buffer(), row->par()));
2124 if (row->par()->isLineSeparator(main_body-1))
2125 x -= singleWidth(bview, row->par(),main_body-1);
2127 if (hfillExpansion(bview->buffer(), row, pos)) {
2128 x += singleWidth(bview, row->par(), pos);
2129 if (pos >= main_body)
2132 x += fill_label_hfill;
2133 } else if (row->par()->isSeparator(pos)) {
2134 x += singleWidth(bview, row->par(), pos);
2135 if (pos >= main_body)
2136 x += fill_separator;
2138 x += singleWidth(bview, row->par(), pos);
2147 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2148 pos_type pos, bool setfont, bool boundary) const
2150 InsetText * it = static_cast<InsetText *>(par->inInset());
2152 if (it != inset_owner) {
2153 lyxerr << "InsetText is " << it << endl;
2154 lyxerr << "inset_owner is " << inset_owner << endl;
2155 #ifdef WITH_WARNINGS
2156 #warning I believe this code is wrong. (Lgb)
2157 #warning Jürgen, have a look at this. (Lgb)
2158 #warning Hmmm, I guess you are right but we
2159 #warning should verify when this is needed
2161 // Jürgen, would you like to have a look?
2162 // I guess we need to move the outer cursor
2163 // and open and lock the inset (bla bla bla)
2164 // stuff I don't know... so can you have a look?
2166 // I moved the lyxerr stuff in here so we can see if
2167 // this is actually really needed and where!
2169 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2174 setCursor(bview, cursor, par, pos, boundary);
2176 setCurrentFont(bview);
2180 void LyXText::setCurrentFont(BufferView * bview) const
2182 pos_type pos = cursor.pos();
2183 if (cursor.boundary() && pos > 0)
2187 if (pos == cursor.par()->size())
2189 else // potentional bug... BUG (Lgb)
2190 if (cursor.par()->isSeparator(pos)) {
2191 if (pos > cursor.row()->pos() &&
2192 bidi_level(pos) % 2 ==
2193 bidi_level(pos - 1) % 2)
2195 else if (pos + 1 < cursor.par()->size())
2201 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2202 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2204 if (cursor.pos() == cursor.par()->size() &&
2205 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2206 !cursor.boundary()) {
2207 Language const * lang =
2208 cursor.par()->getParLanguage(bview->buffer()->params);
2209 current_font.setLanguage(lang);
2210 current_font.setNumber(LyXFont::OFF);
2211 real_current_font.setLanguage(lang);
2212 real_current_font.setNumber(LyXFont::OFF);
2217 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2219 LyXCursor old_cursor = cursor;
2221 setCursorFromCoordinates(bview, cursor, x, y);
2222 setCurrentFont(bview);
2223 deleteEmptyParagraphMechanism(bview, old_cursor);
2227 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2230 // Get the row first.
2232 Row * row = getRowNearY(y);
2234 pos_type const column = getColumnNearX(bview, row, x, bound);
2235 cur.par(row->par());
2236 cur.pos(row->pos() + column);
2238 cur.y(y + row->baseline());
2240 cur.boundary(bound);
2244 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2246 if (cursor.pos() > 0) {
2247 bool boundary = cursor.boundary();
2248 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2249 if (!internal && !boundary &&
2250 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2251 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2252 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2253 Paragraph * par = cursor.par()->previous();
2254 setCursor(bview, par, par->size());
2259 void LyXText::cursorRight(BufferView * bview, bool internal) const
2261 if (!internal && cursor.boundary() &&
2262 !cursor.par()->isNewline(cursor.pos()))
2263 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2264 else if (cursor.pos() < cursor.par()->size()) {
2265 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2267 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2268 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2269 } else if (cursor.par()->next())
2270 setCursor(bview, cursor.par()->next(), 0);
2274 void LyXText::cursorUp(BufferView * bview) const
2276 setCursorFromCoordinates(bview, cursor.x_fix(),
2277 cursor.y() - cursor.row()->baseline() - 1);
2281 void LyXText::cursorDown(BufferView * bview) const
2283 setCursorFromCoordinates(bview, cursor.x_fix(),
2284 cursor.y() - cursor.row()->baseline()
2285 + cursor.row()->height() + 1);
2289 void LyXText::cursorUpParagraph(BufferView * bview) const
2291 if (cursor.pos() > 0) {
2292 setCursor(bview, cursor.par(), 0);
2294 else if (cursor.par()->previous()) {
2295 setCursor(bview, cursor.par()->previous(), 0);
2300 void LyXText::cursorDownParagraph(BufferView * bview) const
2302 if (cursor.par()->next()) {
2303 setCursor(bview, cursor.par()->next(), 0);
2305 setCursor(bview, cursor.par(), cursor.par()->size());
2309 // fix the cursor `cur' after a characters has been deleted at `where'
2310 // position. Called by deleteEmptyParagraphMechanism
2311 void LyXText::fixCursorAfterDelete(BufferView * bview,
2313 LyXCursor const & where) const
2315 // if cursor is not in the paragraph where the delete occured,
2317 if (cur.par() != where.par())
2320 // if cursor position is after the place where the delete occured,
2322 if (cur.pos() > where.pos())
2323 cur.pos(cur.pos()-1);
2325 // recompute row et al. for this cursor
2326 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2330 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2331 LyXCursor const & old_cursor) const
2333 // Would be wrong to delete anything if we have a selection.
2334 if (selection.set()) return false;
2336 // We allow all kinds of "mumbo-jumbo" when freespacing.
2337 if (textclasslist.Style(bview->buffer()->params.textclass,
2338 old_cursor.par()->getLayout()).free_spacing
2339 || old_cursor.par()->isFreeSpacing())
2344 /* Ok I'll put some comments here about what is missing.
2345 I have fixed BackSpace (and thus Delete) to not delete
2346 double-spaces automagically. I have also changed Cut,
2347 Copy and Paste to hopefully do some sensible things.
2348 There are still some small problems that can lead to
2349 double spaces stored in the document file or space at
2350 the beginning of paragraphs. This happens if you have
2351 the cursor betwenn to spaces and then save. Or if you
2352 cut and paste and the selection have a space at the
2353 beginning and then save right after the paste. I am
2354 sure none of these are very hard to fix, but I will
2355 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2356 that I can get some feedback. (Lgb)
2359 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2360 // delete the LineSeparator.
2363 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2364 // delete the LineSeparator.
2367 // If the pos around the old_cursor were spaces, delete one of them.
2368 if (old_cursor.par() != cursor.par()
2369 || old_cursor.pos() != cursor.pos()) {
2370 // Only if the cursor has really moved
2372 if (old_cursor.pos() > 0
2373 && old_cursor.pos() < old_cursor.par()->size()
2374 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2375 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2376 old_cursor.par()->erase(old_cursor.pos() - 1);
2377 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2379 #ifdef WITH_WARNINGS
2380 #warning This will not work anymore when we have multiple views of the same buffer
2381 // In this case, we will have to correct also the cursors held by
2382 // other bufferviews. It will probably be easier to do that in a more
2383 // automated way in LyXCursor code. (JMarc 26/09/2001)
2385 // correct all cursors held by the LyXText
2386 fixCursorAfterDelete(bview, cursor, old_cursor);
2387 fixCursorAfterDelete(bview, selection.cursor,
2389 fixCursorAfterDelete(bview, selection.start,
2391 fixCursorAfterDelete(bview, selection.end, old_cursor);
2392 fixCursorAfterDelete(bview, last_sel_cursor,
2394 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2395 fixCursorAfterDelete(bview, toggle_end_cursor,
2401 // don't delete anything if this is the ONLY paragraph!
2402 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2405 // Do not delete empty paragraphs with keepempty set.
2406 if ((textclasslist.Style(bview->buffer()->params.textclass,
2407 old_cursor.par()->getLayout())).keepempty)
2410 // only do our magic if we changed paragraph
2411 if (old_cursor.par() == cursor.par())
2414 // record if we have deleted a paragraph
2415 // we can't possibly have deleted a paragraph before this point
2416 bool deleted = false;
2418 if ((old_cursor.par()->size() == 0
2419 || (old_cursor.par()->size() == 1
2420 && old_cursor.par()->isLineSeparator(0)))) {
2421 // ok, we will delete anything
2422 LyXCursor tmpcursor;
2424 // make sure that you do not delete any environments
2425 status(bview, LyXText::NEED_MORE_REFRESH);
2428 if (old_cursor.row()->previous()) {
2429 refresh_row = old_cursor.row()->previous();
2430 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2432 cursor = old_cursor; // that undo can restore the right cursor position
2433 Paragraph * endpar = old_cursor.par()->next();
2434 if (endpar && endpar->getDepth()) {
2435 while (endpar && endpar->getDepth()) {
2436 endpar = endpar->next();
2439 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2443 removeRow(old_cursor.row());
2444 if (ownerParagraph() == old_cursor.par()) {
2445 ownerParagraph(ownerParagraph()->next());
2448 delete old_cursor.par();
2450 /* Breakagain the next par. Needed because of
2451 * the parindent that can occur or dissappear.
2452 * The next row can change its height, if
2453 * there is another layout before */
2454 if (refresh_row->next()) {
2455 breakAgain(bview, refresh_row->next());
2456 updateCounters(bview, refresh_row);
2458 setHeightOfRow(bview, refresh_row);
2460 refresh_row = old_cursor.row()->next();
2461 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2464 cursor = old_cursor; // that undo can restore the right cursor position
2465 Paragraph * endpar = old_cursor.par()->next();
2466 if (endpar && endpar->getDepth()) {
2467 while (endpar && endpar->getDepth()) {
2468 endpar = endpar->next();
2471 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2475 removeRow(old_cursor.row());
2477 if (ownerParagraph() == old_cursor.par()) {
2478 ownerParagraph(ownerParagraph()->next());
2481 delete old_cursor.par();
2483 /* Breakagain the next par. Needed because of
2484 the parindent that can occur or dissappear.
2485 The next row can change its height, if
2486 there is another layout before */
2488 breakAgain(bview, refresh_row);
2489 updateCounters(bview, refresh_row->previous());
2494 setCursorIntern(bview, cursor.par(), cursor.pos());
2496 if (selection.cursor.par() == old_cursor.par()
2497 && selection.cursor.pos() == old_cursor.pos()) {
2498 // correct selection
2499 selection.cursor = cursor;
2503 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2504 redoParagraphs(bview, old_cursor,
2505 old_cursor.par()->next());
2507 setCursorIntern(bview, cursor.par(), cursor.pos());
2508 selection.cursor = cursor;
2515 void LyXText::toggleAppendix(BufferView * bview)
2517 Paragraph * par = cursor.par();
2518 bool start = !par->params().startOfAppendix();
2520 // ensure that we have only one start_of_appendix in this document
2521 Paragraph * tmp = ownerParagraph();
2522 for (; tmp; tmp = tmp->next()) {
2523 tmp->params().startOfAppendix(false);
2526 par->params().startOfAppendix(start);
2528 // we can set the refreshing parameters now
2529 status(bview, LyXText::NEED_MORE_REFRESH);
2531 refresh_row = 0; // not needed for full update
2532 updateCounters(bview, 0);
2533 setCursor(bview, cursor.par(), cursor.pos());
2537 Paragraph * LyXText::ownerParagraph() const
2540 return inset_owner->paragraph();
2542 return bv_owner->buffer()->paragraph;
2546 void LyXText::ownerParagraph(Paragraph * p) const
2549 inset_owner->paragraph(p);
2551 bv_owner->buffer()->paragraph = p;
2556 void LyXText::ownerParagraph(int id, Paragraph * p) const
2558 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2559 if (op && op->inInset()) {
2560 static_cast<InsetText *>(op->inInset())->paragraph(p);
2567 LyXText::text_status LyXText::status() const
2573 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2575 // well as much as I know && binds more then || so the above and the
2576 // below are identical (this for your known use of parentesis!)
2577 // Now some explanation:
2578 // We should only go up with refreshing code so this means that if
2579 // we have a MORE refresh we should never set it to LITTLE if we still
2580 // didn't handle it (and then it will be UNCHANGED. Now as long as
2581 // we stay inside one LyXText this may work but we need to tell the
2582 // outermost LyXText that it should REALLY draw us if there is some
2583 // change in a Inset::LyXText. So you see that when we are inside a
2584 // inset's LyXText we give the LITTLE to the outermost LyXText to
2585 // tell'em that it should redraw the actual row (where the inset
2586 // resides! Capito?!
2588 if ((status_ != NEED_MORE_REFRESH)
2589 || (status_ == NEED_MORE_REFRESH
2590 && st != NEED_VERY_LITTLE_REFRESH))
2593 if (inset_owner && st != UNCHANGED) {
2594 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2595 if (!bview->text->refresh_row) {
2596 bview->text->refresh_row = bview->text->cursor.row();
2597 bview->text->refresh_y = bview->text->cursor.y() -
2598 bview->text->cursor.row()->baseline();