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 "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "undo_funcs.h"
30 #include "bufferparams.h"
31 #include "lyx_gui_misc.h"
33 #include "BufferView.h"
35 #include "CutAndPaste.h"
40 #include "FloatList.h"
42 #include "ParagraphParameters.h"
51 LyXText::LyXText(BufferView * bv)
52 : number_of_rows(0), height(0), width(0), first(0),
53 bv_owner(bv), inset_owner(0), the_locking_inset(0),
54 need_break_row(0), refresh_y(0), refresh_row(0),
55 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
60 LyXText::LyXText(InsetText * inset)
61 : number_of_rows(0), height(0), width(0), first(0),
62 bv_owner(0), inset_owner(inset), the_locking_inset(0),
63 need_break_row(0), refresh_y(0), refresh_row(0),
64 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
68 void LyXText::init(BufferView * bview, bool reinit)
71 // Delete all rows, this does not touch the paragraphs!
72 Row * tmprow = firstrow;
74 tmprow = firstrow->next();
78 lastrow = refresh_row = need_break_row = 0;
79 width = height = copylayouttype = 0;
80 number_of_rows = first = refresh_y = 0;
81 status_ = LyXText::UNCHANGED;
85 Paragraph * par = ownerParagraph();
86 current_font = getFont(bview->buffer(), par, 0);
88 insertParagraph(bview, par, lastrow);
91 setCursorIntern(bview, firstrow->par(), 0);
92 selection.cursor = cursor;
98 // Delete all rows, this does not touch the paragraphs!
99 Row * tmprow = firstrow;
101 tmprow = firstrow->next();
108 // Gets the fully instantiated font at a given position in a paragraph
109 // Basically the same routine as Paragraph::getFont() in paragraph.C.
110 // The difference is that this one is used for displaying, and thus we
111 // are allowed to make cosmetic improvements. For instance make footnotes
113 // If position is -1, we get the layout font of the paragraph.
114 // If position is -2, we get the font of the manual label of the paragraph.
115 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
116 Paragraph::size_type pos) const
118 LyXLayout const & layout =
119 textclasslist.Style(buf->params.textclass, par->getLayout());
121 Paragraph::depth_type par_depth = par->getDepth();
122 // We specialize the 95% common case:
126 if (layout.labeltype == LABEL_MANUAL
127 && pos < beginningOfMainBody(buf, par)) {
129 LyXFont f = par->getFontSettings(buf->params,
131 return f.realize(layout.reslabelfont);
133 LyXFont f = par->getFontSettings(buf->params, pos);
134 return f.realize(layout.resfont);
139 // process layoutfont for pos == -1 and labelfont for pos < -1
141 return layout.resfont;
143 return layout.reslabelfont;
147 // The uncommon case need not be optimized as much
149 LyXFont layoutfont, tmpfont;
153 if (pos < beginningOfMainBody(buf, par)) {
155 layoutfont = layout.labelfont;
158 layoutfont = layout.font;
160 tmpfont = par->getFontSettings(buf->params, pos);
161 tmpfont.realize(layoutfont);
164 // process layoutfont for pos == -1 and labelfont for pos < -1
166 tmpfont = layout.font;
168 tmpfont = layout.labelfont;
171 // Resolve against environment font information
172 while (par && par_depth && !tmpfont.resolved()) {
173 par = par->outerHook();
175 tmpfont.realize(textclasslist.
176 Style(buf->params.textclass,
177 par->getLayout()).font);
178 par_depth = par->getDepth();
182 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
188 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
189 Paragraph::size_type pos, LyXFont const & fnt,
192 Buffer const * buf = bv->buffer();
193 LyXFont font = getFont(buf, par, pos);
194 font.update(fnt, buf->params.language, toggleall);
195 // Let the insets convert their font
196 if (par->getChar(pos) == Paragraph::META_INSET) {
197 Inset * inset = par->getInset(pos);
199 if (inset->editable()==Inset::HIGHLY_EDITABLE) {
200 UpdatableInset * uinset =
201 static_cast<UpdatableInset *>(inset);
202 uinset->setFont(bv, fnt, toggleall, true);
204 font = inset->convertFont(font);
208 LyXLayout const & layout =
209 textclasslist.Style(buf->params.textclass,
212 // Get concrete layout font to reduce against
215 if (pos < beginningOfMainBody(buf, par))
216 layoutfont = layout.labelfont;
218 layoutfont = layout.font;
220 // Realize against environment font information
221 if (par->getDepth()){
222 Paragraph * tp = par;
223 while (!layoutfont.resolved() && tp && tp->getDepth()) {
224 tp = tp->outerHook();
226 layoutfont.realize(textclasslist.
227 Style(buf->params.textclass,
228 tp->getLayout()).font);
232 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
234 // Now, reduce font against full layout font
235 font.reduce(layoutfont);
237 par->setFont(pos, font);
241 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
242 Paragraph::size_type pos, LyXFont const & fnt)
245 // Let the insets convert their font
246 if (par->getChar(pos) == Paragraph::META_INSET) {
247 font = par->getInset(pos)->convertFont(font);
250 LyXLayout const & layout =
251 textclasslist.Style(buf->params.textclass,
254 // Get concrete layout font to reduce against
257 if (pos < beginningOfMainBody(buf, par))
258 layoutfont = layout.labelfont;
260 layoutfont = layout.font;
262 // Realize against environment font information
263 if (par->getDepth()){
264 Paragraph * tp = par;
265 while (!layoutfont.resolved() && tp && tp->getDepth()) {
266 tp = tp->outerHook();
268 layoutfont.realize(textclasslist.
269 Style(buf->params.textclass,
270 tp->getLayout()).font);
274 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
276 // Now, reduce font against full layout font
277 font.reduce(layoutfont);
279 par->setFont(pos, font);
283 // inserts a new row behind the specified row, increments
284 // the touched counters
285 void LyXText::insertRow(Row * row, Paragraph * par,
286 Paragraph::size_type pos) const
288 Row * tmprow = new Row;
291 tmprow->next(firstrow);
294 tmprow->previous(row);
295 tmprow->next(row->next());
300 tmprow->next()->previous(tmprow);
302 if (tmprow->previous())
303 tmprow->previous()->next(tmprow);
315 // removes the row and reset the touched counters
316 void LyXText::removeRow(Row * row) const
318 /* this must not happen before the currentrow for clear reasons.
319 so the trick is just to set the current row onto the previous
322 getRow(row->par(), row->pos(), unused_y);
325 row->next()->previous(row->previous());
326 if (!row->previous()) {
327 firstrow = row->next();
329 row->previous()->next(row->next());
332 lastrow = row->previous();
334 height -= row->height(); // the text becomes smaller
337 --number_of_rows; // one row less
341 // remove all following rows of the paragraph of the specified row.
342 void LyXText::removeParagraph(Row * row) const
344 Paragraph * tmppar = row->par();
348 while (row && row->par() == tmppar) {
349 tmprow = row->next();
356 // insert the specified paragraph behind the specified row
357 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
360 insertRow(row, par, 0); /* insert a new row, starting
363 setCounter(bview->buffer(), par); // set the counters
365 // and now append the whole paragraph behind the new row
368 appendParagraph(bview, firstrow);
370 row->next()->height(0);
371 appendParagraph(bview, row->next());
376 Inset * LyXText::getInset() const
379 if (cursor.pos() == 0 && cursor.par()->bibkey) {
380 inset = cursor.par()->bibkey;
381 } else if (cursor.pos() < cursor.par()->size()
382 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
383 inset = cursor.par()->getInset(cursor.pos());
389 void LyXText::toggleInset(BufferView * bview)
391 Inset * inset = getInset();
392 if (!inset->editable())
394 //bview->owner()->message(inset->editMessage());
396 // do we want to keep this?? (JMarc)
397 if (inset->editable() != Inset::HIGHLY_EDITABLE)
398 setCursorParUndo(bview);
400 if (inset->isOpen()) {
406 inset->open(bview, !inset->isOpen());
411 /* used in setlayout */
412 // Asger is not sure we want to do this...
413 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
416 LyXLayout const & layout =
417 textclasslist.Style(buf->params.textclass, par->getLayout());
419 LyXFont layoutfont, tmpfont;
420 for (Paragraph::size_type pos = 0;
421 pos < par->size(); ++pos) {
422 if (pos < beginningOfMainBody(buf, par))
423 layoutfont = layout.labelfont;
425 layoutfont = layout.font;
427 tmpfont = par->getFontSettings(buf->params, pos);
428 tmpfont.reduce(layoutfont);
429 par->setFont(pos, tmpfont);
434 Paragraph * LyXText::setLayout(BufferView * bview,
435 LyXCursor & cur, LyXCursor & sstart_cur,
436 LyXCursor & send_cur,
437 LyXTextClass::size_type layout)
439 Paragraph * endpar = send_cur.par()->next();
440 Paragraph * undoendpar = endpar;
442 if (endpar && endpar->getDepth()) {
443 while (endpar && endpar->getDepth()) {
444 endpar = endpar->next();
448 endpar = endpar->next(); // because of parindents etc.
451 setUndo(bview, Undo::EDIT,
452 sstart_cur.par(), undoendpar);
454 // ok we have a selection. This is always between sstart_cur
455 // and sel_end cursor
458 LyXLayout const & lyxlayout =
459 textclasslist.Style(bview->buffer()->params.textclass, layout);
461 while (cur.par() != send_cur.par()) {
462 cur.par()->setLayout(layout);
463 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
464 Paragraph * fppar = cur.par();
465 fppar->params().spaceTop(lyxlayout.fill_top ?
466 VSpace(VSpace::VFILL)
467 : VSpace(VSpace::NONE));
468 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
469 VSpace(VSpace::VFILL)
470 : VSpace(VSpace::NONE));
471 if (lyxlayout.margintype == MARGIN_MANUAL)
472 cur.par()->setLabelWidthString(lyxlayout.labelstring());
473 if (lyxlayout.labeltype != LABEL_BIBLIO
475 delete fppar->bibkey;
478 cur.par(cur.par()->next());
480 cur.par()->setLayout(layout);
481 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
482 Paragraph * fppar = cur.par();
483 fppar->params().spaceTop(lyxlayout.fill_top ?
484 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
485 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
486 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
487 if (lyxlayout.margintype == MARGIN_MANUAL)
488 cur.par()->setLabelWidthString(lyxlayout.labelstring());
489 if (lyxlayout.labeltype != LABEL_BIBLIO
491 delete fppar->bibkey;
498 // set layout over selection and make a total rebreak of those paragraphs
499 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
501 LyXCursor tmpcursor = cursor; /* store the current cursor */
503 // if there is no selection just set the layout
504 // of the current paragraph */
505 if (!selection.set()) {
506 selection.start = cursor; // dummy selection
507 selection.end = cursor;
509 Paragraph * endpar = setLayout(bview, cursor, selection.start,
510 selection.end, layout);
511 redoParagraphs(bview, selection.start, endpar);
513 // we have to reset the selection, because the
514 // geometry could have changed
515 setCursor(bview, selection.start.par(),
516 selection.start.pos(), false);
517 selection.cursor = cursor;
518 setCursor(bview, selection.end.par(), selection.end.pos(), false);
519 updateCounters(bview, cursor.row());
520 clearSelection(bview);
522 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
526 // increment depth over selection and
527 // make a total rebreak of those paragraphs
528 void LyXText::incDepth(BufferView * bview)
530 // If there is no selection, just use the current paragraph
531 if (!selection.set()) {
532 selection.start = cursor; // dummy selection
533 selection.end = cursor;
536 // We end at the next paragraph with depth 0
537 Paragraph * endpar = selection.end.par()->next();
539 Paragraph * undoendpar = endpar;
541 if (endpar && endpar->getDepth()) {
542 while (endpar && endpar->getDepth()) {
543 endpar = endpar->next();
547 endpar = endpar->next(); // because of parindents etc.
550 setUndo(bview, Undo::EDIT,
551 selection.start.par(), undoendpar);
553 LyXCursor tmpcursor = cursor; // store the current cursor
555 // ok we have a selection. This is always between sel_start_cursor
556 // and sel_end cursor
557 cursor = selection.start;
559 bool anything_changed = false;
562 // NOTE: you can't change the depth of a bibliography entry
564 textclasslist.Style(bview->buffer()->params.textclass,
565 cursor.par()->getLayout()
566 ).labeltype != LABEL_BIBLIO) {
567 Paragraph * prev = cursor.par()->previous();
570 && (prev->getDepth() - cursor.par()->getDepth() > 0
571 || (prev->getDepth() == cursor.par()->getDepth()
572 && textclasslist.Style(bview->buffer()->params.textclass,
573 prev->getLayout()).isEnvironment()))) {
574 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
575 anything_changed = true;
578 if (cursor.par() == selection.end.par())
580 cursor.par(cursor.par()->next());
583 // if nothing changed set all depth to 0
584 if (!anything_changed) {
585 cursor = selection.start;
586 while (cursor.par() != selection.end.par()) {
587 cursor.par()->params().depth(0);
588 cursor.par(cursor.par()->next());
590 cursor.par()->params().depth(0);
593 redoParagraphs(bview, selection.start, endpar);
595 // we have to reset the selection, because the
596 // geometry could have changed
597 setCursor(bview, selection.start.par(), selection.start.pos());
598 selection.cursor = cursor;
599 setCursor(bview, selection.end.par(), selection.end.pos());
600 updateCounters(bview, cursor.row());
601 clearSelection(bview);
603 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
607 // decrement depth over selection and
608 // make a total rebreak of those paragraphs
609 void LyXText::decDepth(BufferView * bview)
611 // if there is no selection just set the layout
612 // of the current paragraph
613 if (!selection.set()) {
614 selection.start = cursor; // dummy selection
615 selection.end = cursor;
617 Paragraph * endpar = selection.end.par()->next();
618 Paragraph * undoendpar = endpar;
620 if (endpar && endpar->getDepth()) {
621 while (endpar && endpar->getDepth()) {
622 endpar = endpar->next();
626 endpar = endpar->next(); // because of parindents etc.
629 setUndo(bview, Undo::EDIT,
630 selection.start.par(), undoendpar);
632 LyXCursor tmpcursor = cursor; // store the current cursor
634 // ok we have a selection. This is always between sel_start_cursor
635 // and sel_end cursor
636 cursor = selection.start;
639 if (cursor.par()->params().depth()) {
640 cursor.par()->params()
641 .depth(cursor.par()->params().depth() - 1);
643 if (cursor.par() == selection.end.par()) {
646 cursor.par(cursor.par()->next());
649 redoParagraphs(bview, selection.start, endpar);
651 // we have to reset the selection, because the
652 // geometry could have changed
653 setCursor(bview, selection.start.par(),
654 selection.start.pos());
655 selection.cursor = cursor;
656 setCursor(bview, selection.end.par(), selection.end.pos());
657 updateCounters(bview, cursor.row());
658 clearSelection(bview);
660 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
664 // set font over selection and make a total rebreak of those paragraphs
665 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
667 // if there is no selection just set the current_font
668 if (!selection.set()) {
669 // Determine basis font
671 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
673 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
675 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
676 // Update current font
677 real_current_font.update(font,
678 bview->buffer()->params.language,
681 // Reduce to implicit settings
682 current_font = real_current_font;
683 current_font.reduce(layoutfont);
684 // And resolve it completely
685 real_current_font.realize(layoutfont);
689 LyXCursor tmpcursor = cursor; // store the current cursor
691 // ok we have a selection. This is always between sel_start_cursor
692 // and sel_end cursor
694 setUndo(bview, Undo::EDIT,
695 selection.start.par(),
696 selection.end.par()->next());
698 cursor = selection.start;
699 while (cursor.par() != selection.end.par() ||
700 (cursor.pos() < selection.end.pos())) {
701 if (cursor.pos() < cursor.par()->size()) {
702 // an open footnote should behave
704 setCharFont(bview, cursor.par(), cursor.pos(),
706 cursor.pos(cursor.pos() + 1);
709 cursor.par(cursor.par()->next());
714 redoParagraphs(bview, selection.start, selection.end.par()->next());
716 // we have to reset the selection, because the
717 // geometry could have changed
718 setCursor(bview, selection.start.par(), selection.start.pos());
719 selection.cursor = cursor;
720 setCursor(bview, selection.end.par(), selection.end.pos());
721 clearSelection(bview);
723 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
724 tmpcursor.boundary());
728 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
730 Row * tmprow = cur.row();
731 int y = cur.y() - tmprow->baseline();
733 setHeightOfRow(bview, tmprow);
735 while (tmprow->previous()
736 && tmprow->previous()->par() == tmprow->par()) {
737 tmprow = tmprow->previous();
738 y -= tmprow->height();
739 setHeightOfRow(bview, tmprow);
742 // we can set the refreshing parameters now
743 status(bview, LyXText::NEED_MORE_REFRESH);
745 refresh_row = tmprow;
746 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
750 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
752 Row * tmprow = cur.row();
754 int y = cur.y() - tmprow->baseline();
755 setHeightOfRow(bview, tmprow);
757 while (tmprow->previous()
758 && tmprow->previous()->par() == tmprow->par()) {
759 tmprow = tmprow->previous();
760 y -= tmprow->height();
763 // we can set the refreshing parameters now
764 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
766 refresh_row = tmprow;
768 status(bview, LyXText::NEED_MORE_REFRESH);
769 setCursor(bview, cur.par(), cur.pos());
773 // deletes and inserts again all paragaphs between the cursor
774 // and the specified par
775 // This function is needed after SetLayout and SetFont etc.
776 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
777 Paragraph const * endpar) const
780 Paragraph * tmppar = 0;
781 Paragraph * first_phys_par = 0;
783 Row * tmprow = cur.row();
785 int y = cur.y() - tmprow->baseline();
787 if (!tmprow->previous()) {
788 // a trick/hack for UNDO
789 // Can somebody please tell me _why_ this solves
791 first_phys_par = firstParagraph();
793 first_phys_par = tmprow->par();
794 while (tmprow->previous()
795 && tmprow->previous()->par() == first_phys_par) {
796 tmprow = tmprow->previous();
797 y -= tmprow->height();
801 // we can set the refreshing parameters now
802 status(bview, LyXText::NEED_MORE_REFRESH);
804 refresh_row = tmprow->previous(); /* the real refresh row will
805 be deleted, so I store
809 tmppar = tmprow->next()->par();
812 while (tmppar != endpar) {
813 removeRow(tmprow->next());
815 tmppar = tmprow->next()->par();
820 // remove the first one
821 tmprow2 = tmprow; /* this is because tmprow->previous()
823 tmprow = tmprow->previous();
826 tmppar = first_phys_par;
830 insertParagraph(bview, tmppar, tmprow);
834 while (tmprow->next()
835 && tmprow->next()->par() == tmppar) {
836 tmprow = tmprow->next();
838 tmppar = tmppar->next();
840 } while (tmppar && tmppar != endpar);
842 // this is because of layout changes
844 refresh_y -= refresh_row->height();
845 setHeightOfRow(bview, refresh_row);
847 refresh_row = firstrow;
849 setHeightOfRow(bview, refresh_row);
852 if (tmprow && tmprow->next())
853 setHeightOfRow(bview, tmprow->next());
857 bool LyXText::fullRebreak(BufferView * bview)
863 if (need_break_row) {
864 breakAgain(bview, need_break_row);
872 // important for the screen
875 /* the cursor set functions have a special mechanism. When they
876 * realize, that you left an empty paragraph, they will delete it.
877 * They also delete the corresponding row */
879 // need the selection cursor:
880 void LyXText::setSelection(BufferView * bview)
882 bool const lsel = selection.set();
884 if (!selection.set()) {
885 last_sel_cursor = selection.cursor;
886 selection.start = selection.cursor;
887 selection.end = selection.cursor;
892 // first the toggling area
893 if (cursor.y() < last_sel_cursor.y()
894 || (cursor.y() == last_sel_cursor.y()
895 && cursor.x() < last_sel_cursor.x())) {
896 toggle_end_cursor = last_sel_cursor;
897 toggle_cursor = cursor;
899 toggle_end_cursor = cursor;
900 toggle_cursor = last_sel_cursor;
903 last_sel_cursor = cursor;
905 // and now the whole selection
907 if (selection.cursor.par() == cursor.par())
908 if (selection.cursor.pos() < cursor.pos()) {
909 selection.end = cursor;
910 selection.start = selection.cursor;
912 selection.end = selection.cursor;
913 selection.start = cursor;
915 else if (selection.cursor.y() < cursor.y() ||
916 (selection.cursor.y() == cursor.y()
917 && selection.cursor.x() < cursor.x())) {
918 selection.end = cursor;
919 selection.start = selection.cursor;
922 selection.end = selection.cursor;
923 selection.start = cursor;
926 // a selection with no contents is not a selection
927 if (selection.start.par() == selection.end.par() &&
928 selection.start.pos() == selection.end.pos())
929 selection.set(false);
931 if (inset_owner && (selection.set() || lsel))
932 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
936 string const LyXText::selectionAsString(Buffer const * buffer) const
938 if (!selection.set()) return string();
941 // Special handling if the whole selection is within one paragraph
942 if (selection.start.par() == selection.end.par()) {
943 result += selection.start.par()->asString(buffer,
944 selection.start.pos(),
945 selection.end.pos());
949 // The selection spans more than one paragraph
951 // First paragraph in selection
952 result += selection.start.par()->asString(buffer,
953 selection.start.pos(),
954 selection.start.par()->size())
957 // The paragraphs in between (if any)
958 LyXCursor tmpcur(selection.start);
959 tmpcur.par(tmpcur.par()->next());
960 while (tmpcur.par() != selection.end.par()) {
961 result += tmpcur.par()->asString(buffer, 0,
962 tmpcur.par()->size()) +"\n\n";
963 tmpcur.par(tmpcur.par()->next());
966 // Last paragraph in selection
967 result += selection.end.par()->asString(buffer, 0,
968 selection.end.pos());
974 void LyXText::clearSelection(BufferView * /*bview*/) const
976 selection.set(false);
977 selection.mark(false);
978 selection.end = selection.start = selection.cursor = cursor;
982 void LyXText::cursorHome(BufferView * bview) const
984 setCursor(bview, cursor.par(), cursor.row()->pos());
988 void LyXText::cursorEnd(BufferView * bview) const
990 if (!cursor.row()->next()
991 || cursor.row()->next()->par() != cursor.row()->par()) {
992 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
994 if (cursor.par()->size() &&
995 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
996 || cursor.par()->isNewline(rowLast(cursor.row())))) {
997 setCursor(bview, cursor.par(), rowLast(cursor.row()));
999 setCursor(bview,cursor.par(),
1000 rowLast(cursor.row()) + 1);
1006 void LyXText::cursorTop(BufferView * bview) const
1008 while (cursor.par()->previous())
1009 cursor.par(cursor.par()->previous());
1010 setCursor(bview, cursor.par(), 0);
1014 void LyXText::cursorBottom(BufferView * bview) const
1016 while (cursor.par()->next())
1017 cursor.par(cursor.par()->next());
1018 setCursor(bview, cursor.par(), cursor.par()->size());
1022 void LyXText::toggleFree(BufferView * bview,
1023 LyXFont const & font, bool toggleall)
1025 // If the mask is completely neutral, tell user
1026 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1027 // Could only happen with user style
1028 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1032 // Try implicit word selection
1033 // If there is a change in the language the implicit word selection
1035 LyXCursor resetCursor = cursor;
1036 bool implicitSelection = (font.language() == ignore_language
1037 && font.number() == LyXFont::IGNORE)
1038 ? selectWordWhenUnderCursor(bview) : false;
1041 setFont(bview, font, toggleall);
1043 // Implicit selections are cleared afterwards
1044 //and cursor is set to the original position.
1045 if (implicitSelection) {
1046 clearSelection(bview);
1047 cursor = resetCursor;
1048 setCursor(bview, cursor.par(), cursor.pos());
1049 selection.cursor = cursor;
1052 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1056 Paragraph::size_type
1057 LyXText::beginningOfMainBody(Buffer const * buf,
1058 Paragraph const * par) const
1060 if (textclasslist.Style(buf->params.textclass,
1061 par->getLayout()).labeltype != LABEL_MANUAL)
1064 return par->beginningOfMainBody();
1068 /* the DTP switches for paragraphs. LyX will store them in the
1069 * first physicla paragraph. When a paragraph is broken, the top settings
1070 * rest, the bottom settings are given to the new one. So I can make shure,
1071 * they do not duplicate themself and you cannnot make dirty things with
1074 void LyXText::setParagraph(BufferView * bview,
1075 bool line_top, bool line_bottom,
1076 bool pagebreak_top, bool pagebreak_bottom,
1077 VSpace const & space_top,
1078 VSpace const & space_bottom,
1080 string labelwidthstring,
1083 LyXCursor tmpcursor = cursor;
1084 if (!selection.set()) {
1085 selection.start = cursor;
1086 selection.end = cursor;
1089 // make sure that the depth behind the selection are restored, too
1090 Paragraph * endpar = selection.end.par()->next();
1091 Paragraph * undoendpar = endpar;
1093 if (endpar && endpar->getDepth()) {
1094 while (endpar && endpar->getDepth()) {
1095 endpar = endpar->next();
1096 undoendpar = endpar;
1100 // because of parindents etc.
1101 endpar = endpar->next();
1104 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1107 Paragraph * tmppar = selection.end.par();
1108 while (tmppar != selection.start.par()->previous()) {
1109 setCursor(bview, tmppar, 0);
1110 status(bview, LyXText::NEED_MORE_REFRESH);
1111 refresh_row = cursor.row();
1112 refresh_y = cursor.y() - cursor.row()->baseline();
1113 cursor.par()->params().lineTop(line_top);
1114 cursor.par()->params().lineBottom(line_bottom);
1115 cursor.par()->params().pagebreakTop(pagebreak_top);
1116 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1117 cursor.par()->params().spaceTop(space_top);
1118 cursor.par()->params().spaceBottom(space_bottom);
1119 // does the layout allow the new alignment?
1120 if (align == LYX_ALIGN_LAYOUT)
1121 align = textclasslist
1122 .Style(bview->buffer()->params.textclass,
1123 cursor.par()->getLayout()).align;
1124 if (align & textclasslist
1125 .Style(bview->buffer()->params.textclass,
1126 cursor.par()->getLayout()).alignpossible) {
1127 if (align == textclasslist
1128 .Style(bview->buffer()->params.textclass,
1129 cursor.par()->getLayout()).align)
1130 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1132 cursor.par()->params().align(align);
1134 cursor.par()->setLabelWidthString(labelwidthstring);
1135 cursor.par()->params().noindent(noindent);
1136 tmppar = cursor.par()->previous();
1139 redoParagraphs(bview, selection.start, endpar);
1141 clearSelection(bview);
1142 setCursor(bview, selection.start.par(), selection.start.pos());
1143 selection.cursor = cursor;
1144 setCursor(bview, selection.end.par(), selection.end.pos());
1145 setSelection(bview);
1146 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1148 bview->updateInset(inset_owner, true);
1152 char loweralphaCounter(int n)
1154 if (n < 1 || n > 26)
1164 char alphaCounter(int n)
1166 if (n < 1 || n > 26)
1174 char hebrewCounter(int n)
1176 static const char hebrew[22] = {
1177 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1178 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1181 if (n < 1 || n > 22)
1189 string const romanCounter(int n)
1191 static char const * roman[20] = {
1192 "i", "ii", "iii", "iv", "v",
1193 "vi", "vii", "viii", "ix", "x",
1194 "xi", "xii", "xiii", "xiv", "xv",
1195 "xvi", "xvii", "xviii", "xix", "xx"
1197 if (n < 1 || n > 20)
1206 // set the counter of a paragraph. This includes the labels
1207 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1209 LyXLayout const & layout =
1210 textclasslist.Style(buf->params.textclass,
1213 LyXTextClass const & textclass =
1214 textclasslist.TextClass(buf->params.textclass);
1216 // copy the prev-counters to this one,
1217 // unless this is the first paragraph
1218 if (par->previous()) {
1219 for (int i = 0; i < 10; ++i) {
1220 par->setCounter(i, par->previous()->getFirstCounter(i));
1222 par->params().appendix(par->previous()->params().appendix());
1223 if (!par->params().appendix() && par->params().startOfAppendix()) {
1224 par->params().appendix(true);
1225 for (int i = 0; i < 10; ++i) {
1226 par->setCounter(i, 0);
1229 par->enumdepth = par->previous()->enumdepth;
1230 par->itemdepth = par->previous()->itemdepth;
1232 for (int i = 0; i < 10; ++i) {
1233 par->setCounter(i, 0);
1235 par->params().appendix(par->params().startOfAppendix());
1240 /* Maybe we have to increment the enumeration depth.
1241 * BUT, enumeration in a footnote is considered in isolation from its
1242 * surrounding paragraph so don't increment if this is the
1243 * first line of the footnote
1244 * AND, bibliographies can't have their depth changed ie. they
1245 * are always of depth 0
1248 && par->previous()->getDepth() < par->getDepth()
1249 && textclasslist.Style(buf->params.textclass,
1250 par->previous()->getLayout()
1251 ).labeltype == LABEL_COUNTER_ENUMI
1252 && par->enumdepth < 3
1253 && layout.labeltype != LABEL_BIBLIO) {
1257 // Maybe we have to decrement the enumeration depth, see note above
1259 && par->previous()->getDepth() > par->getDepth()
1260 && layout.labeltype != LABEL_BIBLIO) {
1261 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1262 par->setCounter(6 + par->enumdepth,
1263 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1264 /* reset the counters.
1265 * A depth change is like a breaking layout
1267 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1268 par->setCounter(i, 0);
1271 if (!par->params().labelString().empty()) {
1272 par->params().labelString(string());
1275 if (layout.margintype == MARGIN_MANUAL) {
1276 if (par->params().labelWidthString().empty()) {
1277 par->setLabelWidthString(layout.labelstring());
1280 par->setLabelWidthString(string());
1283 // is it a layout that has an automatic label?
1284 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1286 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1287 if (i >= 0 && i<= buf->params.secnumdepth) {
1288 par->incCounter(i); // increment the counter
1290 // Is there a label? Useful for Chapter layout
1291 if (!par->params().appendix()) {
1292 if (!layout.labelstring().empty())
1293 par->params().labelString(layout.labelstring());
1295 par->params().labelString(string());
1297 if (!layout.labelstring_appendix().empty())
1298 par->params().labelString(layout.labelstring_appendix());
1300 par->params().labelString(string());
1305 if (!par->params().appendix()) {
1306 switch (2 * LABEL_COUNTER_CHAPTER -
1307 textclass.maxcounter() + i) {
1308 case LABEL_COUNTER_CHAPTER:
1309 s << par->getCounter(i);
1311 case LABEL_COUNTER_SECTION:
1312 s << par->getCounter(i - 1) << '.'
1313 << par->getCounter(i);
1315 case LABEL_COUNTER_SUBSECTION:
1316 s << par->getCounter(i - 2) << '.'
1317 << par->getCounter(i - 1) << '.'
1318 << par->getCounter(i);
1320 case LABEL_COUNTER_SUBSUBSECTION:
1321 s << par->getCounter(i - 3) << '.'
1322 << par->getCounter(i - 2) << '.'
1323 << par->getCounter(i - 1) << '.'
1324 << par->getCounter(i);
1327 case LABEL_COUNTER_PARAGRAPH:
1328 s << par->getCounter(i - 4) << '.'
1329 << par->getCounter(i - 3) << '.'
1330 << par->getCounter(i - 2) << '.'
1331 << par->getCounter(i - 1) << '.'
1332 << par->getCounter(i);
1334 case LABEL_COUNTER_SUBPARAGRAPH:
1335 s << par->getCounter(i - 5) << '.'
1336 << par->getCounter(i - 4) << '.'
1337 << par->getCounter(i - 3) << '.'
1338 << par->getCounter(i - 2) << '.'
1339 << par->getCounter(i - 1) << '.'
1340 << par->getCounter(i);
1344 // Can this ever be reached? And in the
1345 // case it is, how can this be correct?
1347 s << par->getCounter(i) << '.';
1350 } else { // appendix
1351 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1352 case LABEL_COUNTER_CHAPTER:
1353 if (par->isRightToLeftPar(buf->params))
1354 s << hebrewCounter(par->getCounter(i));
1356 s << alphaCounter(par->getCounter(i));
1358 case LABEL_COUNTER_SECTION:
1359 if (par->isRightToLeftPar(buf->params))
1360 s << hebrewCounter(par->getCounter(i - 1));
1362 s << alphaCounter(par->getCounter(i - 1));
1365 << par->getCounter(i);
1368 case LABEL_COUNTER_SUBSECTION:
1369 if (par->isRightToLeftPar(buf->params))
1370 s << hebrewCounter(par->getCounter(i - 2));
1372 s << alphaCounter(par->getCounter(i - 2));
1375 << par->getCounter(i-1) << '.'
1376 << par->getCounter(i);
1379 case LABEL_COUNTER_SUBSUBSECTION:
1380 if (par->isRightToLeftPar(buf->params))
1381 s << hebrewCounter(par->getCounter(i-3));
1383 s << alphaCounter(par->getCounter(i-3));
1386 << par->getCounter(i-2) << '.'
1387 << par->getCounter(i-1) << '.'
1388 << par->getCounter(i);
1391 case LABEL_COUNTER_PARAGRAPH:
1392 if (par->isRightToLeftPar(buf->params))
1393 s << hebrewCounter(par->getCounter(i-4));
1395 s << alphaCounter(par->getCounter(i-4));
1398 << par->getCounter(i-3) << '.'
1399 << par->getCounter(i-2) << '.'
1400 << par->getCounter(i-1) << '.'
1401 << par->getCounter(i);
1404 case LABEL_COUNTER_SUBPARAGRAPH:
1405 if (par->isRightToLeftPar(buf->params))
1406 s << hebrewCounter(par->getCounter(i-5));
1408 s << alphaCounter(par->getCounter(i-5));
1411 << par->getCounter(i-4) << '.'
1412 << par->getCounter(i-3) << '.'
1413 << par->getCounter(i-2) << '.'
1414 << par->getCounter(i-1) << '.'
1415 << par->getCounter(i);
1419 // Can this ever be reached? And in the
1420 // case it is, how can this be correct?
1422 s << par->getCounter(i) << '.';
1428 par->params().labelString(par->params().labelString() +s.str().c_str());
1429 // We really want to remove the c_str as soon as
1432 for (i++; i < 10; ++i) {
1433 // reset the following counters
1434 par->setCounter(i, 0);
1436 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1437 for (i++; i < 10; ++i) {
1438 // reset the following counters
1439 par->setCounter(i, 0);
1441 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1442 par->incCounter(i + par->enumdepth);
1443 int number = par->getCounter(i + par->enumdepth);
1447 switch (par->enumdepth) {
1449 if (par->isRightToLeftPar(buf->params))
1451 << hebrewCounter(number)
1455 << loweralphaCounter(number)
1459 if (par->isRightToLeftPar(buf->params))
1460 s << '.' << romanCounter(number);
1462 s << romanCounter(number) << '.';
1465 if (par->isRightToLeftPar(buf->params))
1467 << alphaCounter(number);
1469 s << alphaCounter(number)
1473 if (par->isRightToLeftPar(buf->params))
1480 par->params().labelString(s.str().c_str());
1482 for (i += par->enumdepth + 1; i < 10; ++i) {
1483 // reset the following counters
1484 par->setCounter(i, 0);
1488 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1489 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1491 int number = par->getCounter(i);
1493 InsetCommandParams p( "bibitem" );
1494 par->bibkey = new InsetBibKey(p);
1496 par->bibkey->setCounter(number);
1497 par->params().labelString(layout.labelstring());
1499 // In biblio should't be following counters but...
1501 string s = layout.labelstring();
1503 // the caption hack:
1504 if (layout.labeltype == LABEL_SENSITIVE) {
1505 bool isOK (par->inInset() && par->inInset()->owner() &&
1506 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1509 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1511 = floatList.getType(tmp->type());
1512 // We should get the correct number here too.
1513 s = fl.name() + " #:";
1515 /* par->SetLayout(0);
1516 s = layout->labelstring; */
1517 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1518 ? " :úåòîùî øñç" : "Senseless: ";
1521 par->params().labelString(s);
1523 /* reset the enumeration counter. They are always resetted
1524 * when there is any other layout between */
1525 for (int i = 6 + par->enumdepth; i < 10; ++i)
1526 par->setCounter(i, 0);
1531 // Updates all counters BEHIND the row. Changed paragraphs
1532 // with a dynamic left margin will be rebroken.
1533 void LyXText::updateCounters(BufferView * bview, Row * row) const
1541 par = row->par()->next();
1545 while (row->par() != par)
1548 setCounter(bview->buffer(), par);
1550 // now check for the headline layouts. remember that they
1551 // have a dynamic left margin
1552 if ((textclasslist.Style(bview->buffer()->params.textclass,
1553 par->layout).margintype == MARGIN_DYNAMIC
1554 || textclasslist.Style(bview->buffer()->params.textclass,
1555 par->layout).labeltype == LABEL_SENSITIVE)) {
1557 // Rebreak the paragraph
1558 removeParagraph(row);
1559 appendParagraph(bview, row);
1566 void LyXText::insertInset(BufferView * bview, Inset * inset)
1568 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1570 setUndo(bview, Undo::INSERT,
1571 cursor.par(), cursor.par()->next());
1572 cursor.par()->insertInset(cursor.pos(), inset);
1573 // Just to rebreak and refresh correctly.
1574 // The character will not be inserted a second time
1575 insertChar(bview, Paragraph::META_INSET);
1577 // If we enter a highly editable inset the cursor should be to before
1578 // the inset. This couldn't happen before as Undo was not handled inside
1579 // inset now after the Undo LyX tries to call inset->Edit(...) again
1580 // and cannot do this as the cursor is behind the inset and GetInset
1581 // does not return the inset!
1582 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1583 cursorLeft(bview, true);
1589 void LyXText::copyEnvironmentType()
1591 copylayouttype = cursor.par()->getLayout();
1595 void LyXText::pasteEnvironmentType(BufferView * bview)
1597 setLayout(bview, copylayouttype);
1601 void LyXText::cutSelection(BufferView * bview, bool doclear)
1603 // Stuff what we got on the clipboard. Even if there is no selection.
1605 // There is a problem with having the stuffing here in that the
1606 // larger the selection the slower LyX will get. This can be
1607 // solved by running the line below only when the selection has
1608 // finished. The solution used currently just works, to make it
1609 // faster we need to be more clever and probably also have more
1610 // calls to stuffClipboard. (Lgb)
1611 bview->stuffClipboard(selectionAsString(bview->buffer()));
1613 // This doesn't make sense, if there is no selection
1614 if (!selection.set())
1617 // OK, we have a selection. This is always between selection.start
1618 // and selection.end
1620 // make sure that the depth behind the selection are restored, too
1621 Paragraph * endpar = selection.end.par()->next();
1622 Paragraph * undoendpar = endpar;
1624 if (endpar && endpar->getDepth()) {
1625 while (endpar && endpar->getDepth()) {
1626 endpar = endpar->next();
1627 undoendpar = endpar;
1629 } else if (endpar) {
1630 endpar = endpar->next(); // because of parindents etc.
1633 setUndo(bview, Undo::DELETE,
1634 selection.start.par(), undoendpar);
1636 // there are two cases: cut only within one paragraph or
1637 // more than one paragraph
1638 if (selection.start.par() == selection.end.par()) {
1639 // only within one paragraph
1640 endpar = selection.end.par();
1641 int pos = selection.end.pos();
1642 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1643 selection.start.pos(), pos,
1644 bview->buffer()->params.textclass, doclear);
1645 selection.end.pos(pos);
1647 endpar = selection.end.par();
1648 int pos = selection.end.pos();
1649 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1650 selection.start.pos(), pos,
1651 bview->buffer()->params.textclass, doclear);
1653 selection.end.par(endpar);
1654 selection.end.pos(pos);
1655 cursor.pos(selection.end.pos());
1657 endpar = endpar->next();
1659 // sometimes necessary
1661 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1663 redoParagraphs(bview, selection.start, endpar);
1665 // cutSelection can invalidate the cursor so we need to set
1667 cursor = selection.start;
1669 // need a valid cursor. (Lgb)
1670 clearSelection(bview);
1672 setCursor(bview, cursor.par(), cursor.pos());
1673 selection.cursor = cursor;
1674 updateCounters(bview, cursor.row());
1678 void LyXText::copySelection(BufferView * bview)
1680 // Stuff what we got on the clipboard. Even if there is no selection.
1682 // There is a problem with having the stuffing here in that the
1683 // larger the selection the slower LyX will get. This can be
1684 // solved by running the line below only when the selection has
1685 // finished. The solution used currently just works, to make it
1686 // faster we need to be more clever and probably also have more
1687 // calls to stuffClipboard. (Lgb)
1688 bview->stuffClipboard(selectionAsString(bview->buffer()));
1690 // this doesnt make sense, if there is no selection
1691 if (!selection.set())
1694 // ok we have a selection. This is always between selection.start
1695 // and sel_end cursor
1697 // copy behind a space if there is one
1698 while (selection.start.par()->size() > selection.start.pos()
1699 && selection.start.par()->isLineSeparator(selection.start.pos())
1700 && (selection.start.par() != selection.end.par()
1701 || selection.start.pos() < selection.end.pos()))
1702 selection.start.pos(selection.start.pos() + 1);
1704 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1705 selection.start.pos(), selection.end.pos(),
1706 bview->buffer()->params.textclass);
1710 void LyXText::pasteSelection(BufferView * bview)
1712 // this does not make sense, if there is nothing to paste
1713 if (!CutAndPaste::checkPastePossible(cursor.par()))
1716 setUndo(bview, Undo::INSERT,
1717 cursor.par(), cursor.par()->next());
1720 Paragraph * actpar = cursor.par();
1722 int pos = cursor.pos();
1723 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1724 bview->buffer()->params.textclass);
1726 redoParagraphs(bview, cursor, endpar);
1728 setCursor(bview, cursor.par(), cursor.pos());
1729 clearSelection(bview);
1731 selection.cursor = cursor;
1732 setCursor(bview, actpar, pos);
1733 setSelection(bview);
1734 updateCounters(bview, cursor.row());
1738 // returns a pointer to the very first Paragraph
1739 Paragraph * LyXText::firstParagraph() const
1741 return ownerParagraph();
1745 // sets the selection over the number of characters of string, no check!!
1746 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1751 selection.cursor = cursor;
1752 for (string::size_type i = 0; i < str.length(); ++i)
1754 setSelection(bview);
1758 // simple replacing. The font of the first selected character is used
1759 void LyXText::replaceSelectionWithString(BufferView * bview,
1762 setCursorParUndo(bview);
1765 if (!selection.set()) { // create a dummy selection
1766 selection.end = cursor;
1767 selection.start = cursor;
1770 // Get font setting before we cut
1771 Paragraph::size_type pos = selection.end.pos();
1772 LyXFont const font = selection.start.par()
1773 ->getFontSettings(bview->buffer()->params,
1774 selection.start.pos());
1776 // Insert the new string
1777 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1778 selection.end.par()->insertChar(pos, (*cit), font);
1782 // Cut the selection
1783 cutSelection(bview);
1789 // needed to insert the selection
1790 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1792 Paragraph * par = cursor.par();
1793 Paragraph::size_type pos = cursor.pos();
1794 Paragraph * endpar = cursor.par()->next();
1796 setCursorParUndo(bview);
1798 // only to be sure, should not be neccessary
1799 clearSelection(bview);
1801 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1803 redoParagraphs(bview, cursor, endpar);
1804 setCursor(bview, cursor.par(), cursor.pos());
1805 selection.cursor = cursor;
1806 setCursor(bview, par, pos);
1807 setSelection(bview);
1811 // turns double-CR to single CR, others where converted into one
1812 // blank. Then InsertStringAsLines is called
1813 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1815 string linestr(str);
1816 bool newline_inserted = false;
1817 for (string::size_type i = 0; i < linestr.length(); ++i) {
1818 if (linestr[i] == '\n') {
1819 if (newline_inserted) {
1820 // we know that \r will be ignored by
1821 // InsertStringA. Of course, it is a dirty
1822 // trick, but it works...
1823 linestr[i - 1] = '\r';
1827 newline_inserted = true;
1829 } else if (IsPrintable(linestr[i])) {
1830 newline_inserted = false;
1833 insertStringAsLines(bview, linestr);
1837 bool LyXText::gotoNextInset(BufferView * bview,
1838 std::vector<Inset::Code> const & codes,
1839 string const & contents) const
1841 LyXCursor res = cursor;
1844 if (res.pos() < res.par()->size() - 1) {
1845 res.pos(res.pos() + 1);
1847 res.par(res.par()->next());
1851 } while (res.par() &&
1852 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1853 && (inset = res.par()->getInset(res.pos())) != 0
1854 && find(codes.begin(), codes.end(), inset->lyxCode())
1856 && (contents.empty() ||
1857 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1861 setCursor(bview, res.par(), res.pos());
1868 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1869 Paragraph::size_type pos)
1871 LyXCursor tmpcursor;
1874 Paragraph::size_type z;
1875 Row * row = getRow(par, pos, y);
1877 // is there a break one row above
1878 if (row->previous() && row->previous()->par() == row->par()) {
1879 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1880 if (z >= row->pos()) {
1881 // set the dimensions of the row above
1882 y -= row->previous()->height();
1884 refresh_row = row->previous();
1885 status(bview, LyXText::NEED_MORE_REFRESH);
1887 breakAgain(bview, row->previous());
1889 // set the cursor again. Otherwise
1890 // dangling pointers are possible
1891 setCursor(bview, cursor.par(), cursor.pos(),
1892 false, cursor.boundary());
1893 selection.cursor = cursor;
1898 int const tmpheight = row->height();
1899 Paragraph::size_type const tmplast = rowLast(row);
1903 breakAgain(bview, row);
1904 if (row->height() == tmpheight && rowLast(row) == tmplast)
1905 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1907 status(bview, LyXText::NEED_MORE_REFRESH);
1909 // check the special right address boxes
1910 if (textclasslist.Style(bview->buffer()->params.textclass,
1911 par->getLayout()).margintype
1912 == MARGIN_RIGHT_ADDRESS_BOX) {
1919 redoDrawingOfParagraph(bview, tmpcursor);
1922 // set the cursor again. Otherwise dangling pointers are possible
1923 // also set the selection
1925 if (selection.set()) {
1927 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1928 false, selection.cursor.boundary());
1929 selection.cursor = cursor;
1930 setCursorIntern(bview, selection.start.par(),
1931 selection.start.pos(),
1932 false, selection.start.boundary());
1933 selection.start = cursor;
1934 setCursorIntern(bview, selection.end.par(),
1935 selection.end.pos(),
1936 false, selection.end.boundary());
1937 selection.end = cursor;
1938 setCursorIntern(bview, last_sel_cursor.par(),
1939 last_sel_cursor.pos(),
1940 false, last_sel_cursor.boundary());
1941 last_sel_cursor = cursor;
1944 setCursorIntern(bview, cursor.par(), cursor.pos(),
1945 false, cursor.boundary());
1949 // returns false if inset wasn't found
1950 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1952 // first check the current paragraph
1953 int pos = cursor.par()->getPositionOfInset(inset);
1955 checkParagraph(bview, cursor.par(), pos);
1959 // check every paragraph
1961 Paragraph * par = firstParagraph();
1963 pos = par->getPositionOfInset(inset);
1965 checkParagraph(bview, par, pos);
1975 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1976 Paragraph::size_type pos,
1977 bool setfont, bool boundary) const
1979 LyXCursor old_cursor = cursor;
1980 setCursorIntern(bview, par, pos, setfont, boundary);
1981 deleteEmptyParagraphMechanism(bview, old_cursor);
1985 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1986 Paragraph::size_type pos, bool boundary) const
1990 cur.boundary(boundary);
1992 // get the cursor y position in text
1994 Row * row = getRow(par, pos, y);
1995 // y is now the beginning of the cursor row
1996 y += row->baseline();
1997 // y is now the cursor baseline
2000 // now get the cursors x position
2002 float fill_separator;
2004 float fill_label_hfill;
2005 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2007 Paragraph::size_type cursor_vpos = 0;
2008 Paragraph::size_type last = rowLastPrintable(row);
2010 if (pos > last + 1) // This shouldn't happen.
2012 else if (pos < row->pos())
2015 if (last < row->pos())
2016 cursor_vpos = row->pos();
2017 else if (pos > last && !boundary)
2018 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2019 ? row->pos() : last + 1;
2020 else if (pos > row->pos() &&
2021 (pos > last || boundary))
2022 /// Place cursor after char at (logical) position pos - 1
2023 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2024 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2026 /// Place cursor before char at (logical) position pos
2027 cursor_vpos = (bidi_level(pos) % 2 == 0)
2028 ? log2vis(pos) : log2vis(pos) + 1;
2030 Paragraph::size_type main_body =
2031 beginningOfMainBody(bview->buffer(), row->par());
2032 if ((main_body > 0) &&
2033 ((main_body-1 > last) ||
2034 !row->par()->isLineSeparator(main_body-1)))
2037 for (Paragraph::size_type vpos = row->pos();
2038 vpos < cursor_vpos; ++vpos) {
2039 pos = vis2log(vpos);
2040 if (main_body > 0 && pos == main_body - 1) {
2041 x += fill_label_hfill +
2042 lyxfont::width(textclasslist.Style(
2043 bview->buffer()->params.textclass,
2044 row->par()->getLayout())
2046 getFont(bview->buffer(), row->par(), -2));
2047 if (row->par()->isLineSeparator(main_body-1))
2048 x -= singleWidth(bview, row->par(),main_body-1);
2050 if (hfillExpansion(bview->buffer(), row, pos)) {
2051 x += singleWidth(bview, row->par(), pos);
2052 if (pos >= main_body)
2055 x += fill_label_hfill;
2056 } else if (row->par()->isSeparator(pos)) {
2057 x += singleWidth(bview, row->par(), pos);
2058 if (pos >= main_body)
2059 x += fill_separator;
2061 x += singleWidth(bview, row->par(), pos);
2070 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2071 Paragraph::size_type pos,
2072 bool setfont, bool boundary) const
2074 InsetText * it = static_cast<InsetText *>(par->inInset());
2075 if (it && (it != inset_owner)) {
2076 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2079 setCursor(bview, cursor, par, pos, boundary);
2081 setCurrentFont(bview);
2086 void LyXText::setCurrentFont(BufferView * bview) const
2088 Paragraph::size_type pos = cursor.pos();
2089 if (cursor.boundary() && pos > 0)
2093 if (pos == cursor.par()->size())
2095 else // potentional bug... BUG (Lgb)
2096 if (cursor.par()->isSeparator(pos)) {
2097 if (pos > cursor.row()->pos() &&
2098 bidi_level(pos) % 2 ==
2099 bidi_level(pos - 1) % 2)
2101 else if (pos + 1 < cursor.par()->size())
2107 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2108 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2110 if (cursor.pos() == cursor.par()->size() &&
2111 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2112 !cursor.boundary()) {
2113 Language const * lang =
2114 cursor.par()->getParLanguage(bview->buffer()->params);
2115 current_font.setLanguage(lang);
2116 current_font.setNumber(LyXFont::OFF);
2117 real_current_font.setLanguage(lang);
2118 real_current_font.setNumber(LyXFont::OFF);
2123 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2125 LyXCursor old_cursor = cursor;
2127 // Get the row first.
2129 Row * row = getRowNearY(y);
2130 cursor.par(row->par());
2133 int column = getColumnNearX(bview, row, x, bound);
2134 cursor.pos(row->pos() + column);
2136 cursor.y(y + row->baseline());
2138 cursor.boundary(bound);
2139 setCurrentFont(bview);
2140 deleteEmptyParagraphMechanism(bview, old_cursor);
2144 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2147 // Get the row first.
2149 Row * row = getRowNearY(y);
2151 int column = getColumnNearX(bview, row, x, bound);
2153 cur.par(row->par());
2154 cur.pos(row->pos() + column);
2156 cur.y(y + row->baseline());
2158 cur.boundary(bound);
2162 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2164 if (cursor.pos() > 0) {
2165 bool boundary = cursor.boundary();
2166 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2167 if (!internal && !boundary &&
2168 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2169 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2170 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2171 Paragraph * par = cursor.par()->previous();
2172 setCursor(bview, par, par->size());
2177 void LyXText::cursorRight(BufferView * bview, bool internal) const
2179 if (!internal && cursor.boundary() &&
2180 !cursor.par()->isNewline(cursor.pos()))
2181 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2182 else if (cursor.pos() < cursor.par()->size()) {
2183 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2185 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2186 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2187 } else if (cursor.par()->next())
2188 setCursor(bview, cursor.par()->next(), 0);
2192 void LyXText::cursorUp(BufferView * bview) const
2194 setCursorFromCoordinates(bview, cursor.x_fix(),
2195 cursor.y() - cursor.row()->baseline() - 1);
2199 void LyXText::cursorDown(BufferView * bview) const
2201 setCursorFromCoordinates(bview, cursor.x_fix(),
2202 cursor.y() - cursor.row()->baseline()
2203 + cursor.row()->height() + 1);
2207 void LyXText::cursorUpParagraph(BufferView * bview) const
2209 if (cursor.pos() > 0) {
2210 setCursor(bview, cursor.par(), 0);
2212 else if (cursor.par()->previous()) {
2213 setCursor(bview, cursor.par()->previous(), 0);
2218 void LyXText::cursorDownParagraph(BufferView * bview) const
2220 if (cursor.par()->next()) {
2221 setCursor(bview, cursor.par()->next(), 0);
2223 setCursor(bview, cursor.par(), cursor.par()->size());
2228 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2229 LyXCursor const & old_cursor) const
2231 // Would be wrong to delete anything if we have a selection.
2232 if (selection.set()) return;
2234 // We allow all kinds of "mumbo-jumbo" when freespacing.
2235 if (textclasslist.Style(bview->buffer()->params.textclass,
2236 old_cursor.par()->getLayout()).free_spacing)
2239 bool deleted = false;
2241 /* Ok I'll put some comments here about what is missing.
2242 I have fixed BackSpace (and thus Delete) to not delete
2243 double-spaces automagically. I have also changed Cut,
2244 Copy and Paste to hopefully do some sensible things.
2245 There are still some small problems that can lead to
2246 double spaces stored in the document file or space at
2247 the beginning of paragraphs. This happens if you have
2248 the cursor betwenn to spaces and then save. Or if you
2249 cut and paste and the selection have a space at the
2250 beginning and then save right after the paste. I am
2251 sure none of these are very hard to fix, but I will
2252 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2253 that I can get some feedback. (Lgb)
2256 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2257 // delete the LineSeparator.
2260 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2261 // delete the LineSeparator.
2264 // If the pos around the old_cursor were spaces, delete one of them.
2265 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2266 // Only if the cursor has really moved
2268 if (old_cursor.pos() > 0
2269 && old_cursor.pos() < old_cursor.par()->size()
2270 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2271 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2272 old_cursor.par()->erase(old_cursor.pos() - 1);
2273 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2275 if (old_cursor.par() == cursor.par() &&
2276 cursor.pos() > old_cursor.pos()) {
2277 setCursorIntern(bview, cursor.par(),
2280 setCursorIntern(bview, cursor.par(),
2286 // Do not delete empty paragraphs with keepempty set.
2287 if ((textclasslist.Style(bview->buffer()->params.textclass,
2288 old_cursor.par()->getLayout())).keepempty)
2291 LyXCursor tmpcursor;
2293 if (old_cursor.par() != cursor.par()) {
2294 if ((old_cursor.par()->size() == 0
2295 || (old_cursor.par()->size() == 1
2296 && old_cursor.par()->isLineSeparator(0)))) {
2297 // ok, we will delete anything
2299 // make sure that you do not delete any environments
2300 status(bview, LyXText::NEED_MORE_REFRESH);
2303 if (old_cursor.row()->previous()) {
2304 refresh_row = old_cursor.row()->previous();
2305 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2307 cursor = old_cursor; // that undo can restore the right cursor position
2308 Paragraph * endpar = old_cursor.par()->next();
2309 if (endpar && endpar->getDepth()) {
2310 while (endpar && endpar->getDepth()) {
2311 endpar = endpar->next();
2314 setUndo(bview, Undo::DELETE,
2320 removeRow(old_cursor.row());
2321 if (ownerParagraph() == old_cursor.par()) {
2322 ownerParagraph(ownerParagraph()->next());
2325 delete old_cursor.par();
2327 /* Breakagain the next par. Needed
2328 * because of the parindent that
2329 * can occur or dissappear. The
2330 * next row can change its height,
2331 * if there is another layout before */
2332 if (refresh_row->next()) {
2333 breakAgain(bview, refresh_row->next());
2334 updateCounters(bview, refresh_row);
2336 setHeightOfRow(bview, refresh_row);
2338 refresh_row = old_cursor.row()->next();
2339 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2342 cursor = old_cursor; // that undo can restore the right cursor position
2343 Paragraph * endpar = old_cursor.par()->next();
2344 if (endpar && endpar->getDepth()) {
2345 while (endpar && endpar->getDepth()) {
2346 endpar = endpar->next();
2349 setUndo(bview, Undo::DELETE,
2355 removeRow(old_cursor.row());
2357 if (ownerParagraph() == old_cursor.par()) {
2358 ownerParagraph(ownerParagraph()->next());
2361 delete old_cursor.par();
2363 /* Breakagain the next par. Needed
2364 because of the parindent that can
2365 occur or dissappear.
2366 The next row can change its height,
2367 if there is another layout before
2370 breakAgain(bview, refresh_row);
2371 updateCounters(bview, refresh_row->previous());
2377 setCursorIntern(bview, cursor.par(), cursor.pos());
2379 if (selection.cursor.par() == old_cursor.par()
2380 && selection.cursor.pos() == selection.cursor.pos()) {
2381 // correct selection
2382 selection.cursor = cursor;
2386 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2387 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2389 setCursorIntern(bview, cursor.par(), cursor.pos());
2390 selection.cursor = cursor;
2397 void LyXText::toggleAppendix(BufferView * bview)
2399 Paragraph * par = cursor.par();
2400 bool start = !par->params().startOfAppendix();
2402 // ensure that we have only one start_of_appendix in this document
2403 Paragraph * tmp = firstParagraph();
2404 for (; tmp; tmp = tmp->next()) {
2405 tmp->params().startOfAppendix(false);
2408 par->params().startOfAppendix(start);
2410 // we can set the refreshing parameters now
2411 status(bview, LyXText::NEED_MORE_REFRESH);
2413 refresh_row = 0; // not needed for full update
2414 updateCounters(bview, 0);
2415 setCursor(bview, cursor.par(), cursor.pos());
2419 Paragraph * LyXText::ownerParagraph() const
2422 return inset_owner->paragraph();
2424 return bv_owner->buffer()->paragraph;
2428 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2431 inset_owner->paragraph(p);
2433 bv_owner->buffer()->paragraph = p;
2438 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2440 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2441 if (op && op->inInset()) {
2442 static_cast<InsetText *>(op->inInset())->paragraph(p);
2445 inset_owner->paragraph(p);
2447 bv_owner->buffer()->paragraph = p;
2454 LyXText::text_status LyXText::status() const
2460 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2463 if ((status_ != NEED_MORE_REFRESH)
2464 || (status_ == NEED_MORE_REFRESH)
2465 && (st != NEED_VERY_LITTLE_REFRESH)) {
2467 if (inset_owner && st != UNCHANGED) {
2468 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2472 #warning Please tell what the intention is here. (Lgb)
2473 // The above does not make any sense, I changed it to what is here,
2474 // but it still does not make much sense. (Lgb)
2475 #warning Sure have a look now! (Jug)
2476 // well as much as I know && binds more then || so the above and the
2477 // below are identical (this for your known use of parentesis!)
2478 // Now some explanation:
2479 // We should only go up with refreshing code so this means that if
2480 // we have a MORE refresh we should never set it to LITTLE if we still
2481 // didn't handle it (and then it will be UNCHANGED. Now as long as
2482 // we stay inside one LyXText this may work but we need to tell the
2483 // outermost LyXText that it should REALLY draw us if there is some
2484 // change in a Inset::LyXText. So you see that when we are inside a
2485 // inset's LyXText we give the LITTLE to the outermost LyXText to
2486 // tell'em that it should redraw the actual row (where the inset
2487 // resides! Capito?!
2489 if ((status_ != NEED_MORE_REFRESH)
2490 || (status_ == NEED_MORE_REFRESH
2491 && st != NEED_VERY_LITTLE_REFRESH))
2494 if (inset_owner && st != UNCHANGED) {
2495 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);