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());
375 Inset * LyXText::getInset() const
378 if (cursor.pos() == 0 && cursor.par()->bibkey) {
379 inset = cursor.par()->bibkey;
380 } else if (cursor.pos() < cursor.par()->size()
381 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
382 inset = cursor.par()->getInset(cursor.pos());
387 void LyXText::toggleInset(BufferView * bview)
389 Inset * inset = getInset();
390 if (!inset->editable())
392 //bview->owner()->message(inset->editMessage());
394 // do we want to keep this?? (JMarc)
395 if (inset->editable() != Inset::HIGHLY_EDITABLE)
396 setCursorParUndo(bview);
397 inset->open(bview, !inset->isOpen());
401 /* used in setlayout */
402 // Asger is not sure we want to do this...
403 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
406 LyXLayout const & layout =
407 textclasslist.Style(buf->params.textclass, par->getLayout());
409 LyXFont layoutfont, tmpfont;
410 for (Paragraph::size_type pos = 0;
411 pos < par->size(); ++pos) {
412 if (pos < beginningOfMainBody(buf, par))
413 layoutfont = layout.labelfont;
415 layoutfont = layout.font;
417 tmpfont = par->getFontSettings(buf->params, pos);
418 tmpfont.reduce(layoutfont);
419 par->setFont(pos, tmpfont);
424 Paragraph * LyXText::setLayout(BufferView * bview,
425 LyXCursor & cur, LyXCursor & sstart_cur,
426 LyXCursor & send_cur,
427 LyXTextClass::size_type layout)
429 Paragraph * endpar = send_cur.par()->next();
430 Paragraph * undoendpar = endpar;
432 if (endpar && endpar->getDepth()) {
433 while (endpar && endpar->getDepth()) {
434 endpar = endpar->next();
438 endpar = endpar->next(); // because of parindents etc.
441 setUndo(bview, Undo::EDIT,
442 sstart_cur.par(), undoendpar);
444 // ok we have a selection. This is always between sstart_cur
445 // and sel_end cursor
448 LyXLayout const & lyxlayout =
449 textclasslist.Style(bview->buffer()->params.textclass, layout);
451 while (cur.par() != send_cur.par()) {
452 cur.par()->setLayout(layout);
453 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
454 Paragraph * fppar = cur.par();
455 fppar->params().spaceTop(lyxlayout.fill_top ?
456 VSpace(VSpace::VFILL)
457 : VSpace(VSpace::NONE));
458 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
459 VSpace(VSpace::VFILL)
460 : VSpace(VSpace::NONE));
461 if (lyxlayout.margintype == MARGIN_MANUAL)
462 cur.par()->setLabelWidthString(lyxlayout.labelstring());
463 if (lyxlayout.labeltype != LABEL_BIBLIO
465 delete fppar->bibkey;
468 cur.par(cur.par()->next());
470 cur.par()->setLayout(layout);
471 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
472 Paragraph * fppar = cur.par();
473 fppar->params().spaceTop(lyxlayout.fill_top ?
474 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
475 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
476 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
477 if (lyxlayout.margintype == MARGIN_MANUAL)
478 cur.par()->setLabelWidthString(lyxlayout.labelstring());
479 if (lyxlayout.labeltype != LABEL_BIBLIO
481 delete fppar->bibkey;
488 // set layout over selection and make a total rebreak of those paragraphs
489 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
491 LyXCursor tmpcursor = cursor; /* store the current cursor */
493 // if there is no selection just set the layout
494 // of the current paragraph */
495 if (!selection.set()) {
496 selection.start = cursor; // dummy selection
497 selection.end = cursor;
499 Paragraph * endpar = setLayout(bview, cursor, selection.start,
500 selection.end, layout);
501 redoParagraphs(bview, selection.start, endpar);
503 // we have to reset the selection, because the
504 // geometry could have changed
505 setCursor(bview, selection.start.par(),
506 selection.start.pos(), false);
507 selection.cursor = cursor;
508 setCursor(bview, selection.end.par(), selection.end.pos(), false);
509 updateCounters(bview, cursor.row());
510 clearSelection(bview);
512 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
516 // increment depth over selection and
517 // make a total rebreak of those paragraphs
518 void LyXText::incDepth(BufferView * bview)
520 // If there is no selection, just use the current paragraph
521 if (!selection.set()) {
522 selection.start = cursor; // dummy selection
523 selection.end = cursor;
526 // We end at the next paragraph with depth 0
527 Paragraph * endpar = selection.end.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 selection.start.par(), undoendpar);
543 LyXCursor tmpcursor = cursor; // store the current cursor
545 // ok we have a selection. This is always between sel_start_cursor
546 // and sel_end cursor
547 cursor = selection.start;
549 bool anything_changed = false;
552 // NOTE: you can't change the depth of a bibliography entry
554 textclasslist.Style(bview->buffer()->params.textclass,
555 cursor.par()->getLayout()
556 ).labeltype != LABEL_BIBLIO) {
557 Paragraph * prev = cursor.par()->previous();
560 && (prev->getDepth() - cursor.par()->getDepth() > 0
561 || (prev->getDepth() == cursor.par()->getDepth()
562 && textclasslist.Style(bview->buffer()->params.textclass,
563 prev->getLayout()).isEnvironment()))) {
564 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
565 anything_changed = true;
568 if (cursor.par() == selection.end.par())
570 cursor.par(cursor.par()->next());
573 // if nothing changed set all depth to 0
574 if (!anything_changed) {
575 cursor = selection.start;
576 while (cursor.par() != selection.end.par()) {
577 cursor.par()->params().depth(0);
578 cursor.par(cursor.par()->next());
580 cursor.par()->params().depth(0);
583 redoParagraphs(bview, selection.start, endpar);
585 // we have to reset the selection, because the
586 // geometry could have changed
587 setCursor(bview, selection.start.par(), selection.start.pos());
588 selection.cursor = cursor;
589 setCursor(bview, selection.end.par(), selection.end.pos());
590 updateCounters(bview, cursor.row());
591 clearSelection(bview);
593 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
597 // decrement depth over selection and
598 // make a total rebreak of those paragraphs
599 void LyXText::decDepth(BufferView * bview)
601 // if there is no selection just set the layout
602 // of the current paragraph
603 if (!selection.set()) {
604 selection.start = cursor; // dummy selection
605 selection.end = cursor;
607 Paragraph * endpar = selection.end.par()->next();
608 Paragraph * undoendpar = endpar;
610 if (endpar && endpar->getDepth()) {
611 while (endpar && endpar->getDepth()) {
612 endpar = endpar->next();
616 endpar = endpar->next(); // because of parindents etc.
619 setUndo(bview, Undo::EDIT,
620 selection.start.par(), undoendpar);
622 LyXCursor tmpcursor = cursor; // store the current cursor
624 // ok we have a selection. This is always between sel_start_cursor
625 // and sel_end cursor
626 cursor = selection.start;
629 if (cursor.par()->params().depth()) {
630 cursor.par()->params()
631 .depth(cursor.par()->params().depth() - 1);
633 if (cursor.par() == selection.end.par()) {
636 cursor.par(cursor.par()->next());
639 redoParagraphs(bview, selection.start, endpar);
641 // we have to reset the selection, because the
642 // geometry could have changed
643 setCursor(bview, selection.start.par(),
644 selection.start.pos());
645 selection.cursor = cursor;
646 setCursor(bview, selection.end.par(), selection.end.pos());
647 updateCounters(bview, cursor.row());
648 clearSelection(bview);
650 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
654 // set font over selection and make a total rebreak of those paragraphs
655 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
657 // if there is no selection just set the current_font
658 if (!selection.set()) {
659 // Determine basis font
661 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
663 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
665 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
666 // Update current font
667 real_current_font.update(font,
668 bview->buffer()->params.language,
671 // Reduce to implicit settings
672 current_font = real_current_font;
673 current_font.reduce(layoutfont);
674 // And resolve it completely
675 real_current_font.realize(layoutfont);
679 LyXCursor tmpcursor = cursor; // store the current cursor
681 // ok we have a selection. This is always between sel_start_cursor
682 // and sel_end cursor
684 setUndo(bview, Undo::EDIT,
685 selection.start.par(),
686 selection.end.par()->next());
688 cursor = selection.start;
689 while (cursor.par() != selection.end.par() ||
690 (cursor.pos() < selection.end.pos())) {
691 if (cursor.pos() < cursor.par()->size()) {
692 // an open footnote should behave
694 setCharFont(bview, cursor.par(), cursor.pos(),
696 cursor.pos(cursor.pos() + 1);
699 cursor.par(cursor.par()->next());
704 redoParagraphs(bview, selection.start, selection.end.par()->next());
706 // we have to reset the selection, because the
707 // geometry could have changed
708 setCursor(bview, selection.start.par(), selection.start.pos());
709 selection.cursor = cursor;
710 setCursor(bview, selection.end.par(), selection.end.pos());
711 clearSelection(bview);
713 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
714 tmpcursor.boundary());
718 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
720 Row * tmprow = cur.row();
721 int y = cur.y() - tmprow->baseline();
723 setHeightOfRow(bview, tmprow);
725 while (tmprow->previous()
726 && tmprow->previous()->par() == tmprow->par()) {
727 tmprow = tmprow->previous();
728 y -= tmprow->height();
729 setHeightOfRow(bview, tmprow);
732 // we can set the refreshing parameters now
733 status(bview, LyXText::NEED_MORE_REFRESH);
735 refresh_row = tmprow;
736 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
740 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
742 Row * tmprow = cur.row();
744 int y = cur.y() - tmprow->baseline();
745 setHeightOfRow(bview, tmprow);
747 while (tmprow->previous()
748 && tmprow->previous()->par() == tmprow->par()) {
749 tmprow = tmprow->previous();
750 y -= tmprow->height();
753 // we can set the refreshing parameters now
754 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
756 refresh_row = tmprow;
758 status(bview, LyXText::NEED_MORE_REFRESH);
759 setCursor(bview, cur.par(), cur.pos());
763 // deletes and inserts again all paragaphs between the cursor
764 // and the specified par
765 // This function is needed after SetLayout and SetFont etc.
766 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
767 Paragraph const * endpar) const
770 Paragraph * tmppar = 0;
771 Paragraph * first_phys_par = 0;
773 Row * tmprow = cur.row();
775 int y = cur.y() - tmprow->baseline();
777 if (!tmprow->previous()) {
778 // a trick/hack for UNDO
779 // Can somebody please tell me _why_ this solves
781 first_phys_par = firstParagraph();
783 first_phys_par = tmprow->par();
784 while (tmprow->previous()
785 && tmprow->previous()->par() == first_phys_par) {
786 tmprow = tmprow->previous();
787 y -= tmprow->height();
791 // we can set the refreshing parameters now
792 status(bview, LyXText::NEED_MORE_REFRESH);
794 refresh_row = tmprow->previous(); /* the real refresh row will
795 be deleted, so I store
799 tmppar = tmprow->next()->par();
802 while (tmppar != endpar) {
803 removeRow(tmprow->next());
805 tmppar = tmprow->next()->par();
810 // remove the first one
811 tmprow2 = tmprow; /* this is because tmprow->previous()
813 tmprow = tmprow->previous();
816 tmppar = first_phys_par;
820 insertParagraph(bview, tmppar, tmprow);
824 while (tmprow->next()
825 && tmprow->next()->par() == tmppar) {
826 tmprow = tmprow->next();
828 tmppar = tmppar->next();
830 } while (tmppar && tmppar != endpar);
832 // this is because of layout changes
834 refresh_y -= refresh_row->height();
835 setHeightOfRow(bview, refresh_row);
837 refresh_row = firstrow;
839 setHeightOfRow(bview, refresh_row);
842 if (tmprow && tmprow->next())
843 setHeightOfRow(bview, tmprow->next());
847 bool LyXText::fullRebreak(BufferView * bview)
853 if (need_break_row) {
854 breakAgain(bview, need_break_row);
862 // important for the screen
865 /* the cursor set functions have a special mechanism. When they
866 * realize, that you left an empty paragraph, they will delete it.
867 * They also delete the corresponding row */
869 // need the selection cursor:
870 void LyXText::setSelection(BufferView * bview)
872 bool const lsel = selection.set();
874 if (!selection.set()) {
875 last_sel_cursor = selection.cursor;
876 selection.start = selection.cursor;
877 selection.end = selection.cursor;
882 // first the toggling area
883 if (cursor.y() < last_sel_cursor.y()
884 || (cursor.y() == last_sel_cursor.y()
885 && cursor.x() < last_sel_cursor.x())) {
886 toggle_end_cursor = last_sel_cursor;
887 toggle_cursor = cursor;
889 toggle_end_cursor = cursor;
890 toggle_cursor = last_sel_cursor;
893 last_sel_cursor = cursor;
895 // and now the whole selection
897 if (selection.cursor.par() == cursor.par())
898 if (selection.cursor.pos() < cursor.pos()) {
899 selection.end = cursor;
900 selection.start = selection.cursor;
902 selection.end = selection.cursor;
903 selection.start = cursor;
905 else if (selection.cursor.y() < cursor.y() ||
906 (selection.cursor.y() == cursor.y()
907 && selection.cursor.x() < cursor.x())) {
908 selection.end = cursor;
909 selection.start = selection.cursor;
912 selection.end = selection.cursor;
913 selection.start = cursor;
916 // a selection with no contents is not a selection
917 if (selection.start.par() == selection.end.par() &&
918 selection.start.pos() == selection.end.pos())
919 selection.set(false);
921 if (inset_owner && (selection.set() || lsel))
922 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
926 string const LyXText::selectionAsString(Buffer const * buffer) const
928 if (!selection.set()) return string();
931 // Special handling if the whole selection is within one paragraph
932 if (selection.start.par() == selection.end.par()) {
933 result += selection.start.par()->asString(buffer,
934 selection.start.pos(),
935 selection.end.pos());
939 // The selection spans more than one paragraph
941 // First paragraph in selection
942 result += selection.start.par()->asString(buffer,
943 selection.start.pos(),
944 selection.start.par()->size())
947 // The paragraphs in between (if any)
948 LyXCursor tmpcur(selection.start);
949 tmpcur.par(tmpcur.par()->next());
950 while (tmpcur.par() != selection.end.par()) {
951 result += tmpcur.par()->asString(buffer, 0,
952 tmpcur.par()->size()) +"\n\n";
953 tmpcur.par(tmpcur.par()->next());
956 // Last paragraph in selection
957 result += selection.end.par()->asString(buffer, 0,
958 selection.end.pos());
964 void LyXText::clearSelection(BufferView * /*bview*/) const
966 selection.set(false);
967 selection.mark(false);
968 selection.end = selection.start = selection.cursor = cursor;
972 void LyXText::cursorHome(BufferView * bview) const
974 setCursor(bview, cursor.par(), cursor.row()->pos());
978 void LyXText::cursorEnd(BufferView * bview) const
980 if (!cursor.row()->next()
981 || cursor.row()->next()->par() != cursor.row()->par()) {
982 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
984 if (cursor.par()->size() &&
985 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
986 || cursor.par()->isNewline(rowLast(cursor.row())))) {
987 setCursor(bview, cursor.par(), rowLast(cursor.row()));
989 setCursor(bview,cursor.par(),
990 rowLast(cursor.row()) + 1);
996 void LyXText::cursorTop(BufferView * bview) const
998 while (cursor.par()->previous())
999 cursor.par(cursor.par()->previous());
1000 setCursor(bview, cursor.par(), 0);
1004 void LyXText::cursorBottom(BufferView * bview) const
1006 while (cursor.par()->next())
1007 cursor.par(cursor.par()->next());
1008 setCursor(bview, cursor.par(), cursor.par()->size());
1012 void LyXText::toggleFree(BufferView * bview,
1013 LyXFont const & font, bool toggleall)
1015 // If the mask is completely neutral, tell user
1016 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1017 // Could only happen with user style
1018 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1022 // Try implicit word selection
1023 // If there is a change in the language the implicit word selection
1025 LyXCursor resetCursor = cursor;
1026 bool implicitSelection = (font.language() == ignore_language
1027 && font.number() == LyXFont::IGNORE)
1028 ? selectWordWhenUnderCursor(bview) : false;
1031 setFont(bview, font, toggleall);
1033 // Implicit selections are cleared afterwards
1034 //and cursor is set to the original position.
1035 if (implicitSelection) {
1036 clearSelection(bview);
1037 cursor = resetCursor;
1038 setCursor(bview, cursor.par(), cursor.pos());
1039 selection.cursor = cursor;
1042 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1046 Paragraph::size_type
1047 LyXText::beginningOfMainBody(Buffer const * buf,
1048 Paragraph const * par) const
1050 if (textclasslist.Style(buf->params.textclass,
1051 par->getLayout()).labeltype != LABEL_MANUAL)
1054 return par->beginningOfMainBody();
1058 /* the DTP switches for paragraphs. LyX will store them in the
1059 * first physicla paragraph. When a paragraph is broken, the top settings
1060 * rest, the bottom settings are given to the new one. So I can make shure,
1061 * they do not duplicate themself and you cannnot make dirty things with
1064 void LyXText::setParagraph(BufferView * bview,
1065 bool line_top, bool line_bottom,
1066 bool pagebreak_top, bool pagebreak_bottom,
1067 VSpace const & space_top,
1068 VSpace const & space_bottom,
1070 string labelwidthstring,
1073 LyXCursor tmpcursor = cursor;
1074 if (!selection.set()) {
1075 selection.start = cursor;
1076 selection.end = cursor;
1079 // make sure that the depth behind the selection are restored, too
1080 Paragraph * endpar = selection.end.par()->next();
1081 Paragraph * undoendpar = endpar;
1083 if (endpar && endpar->getDepth()) {
1084 while (endpar && endpar->getDepth()) {
1085 endpar = endpar->next();
1086 undoendpar = endpar;
1090 // because of parindents etc.
1091 endpar = endpar->next();
1094 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1097 Paragraph * tmppar = selection.end.par();
1098 while (tmppar != selection.start.par()->previous()) {
1099 setCursor(bview, tmppar, 0);
1100 status(bview, LyXText::NEED_MORE_REFRESH);
1101 refresh_row = cursor.row();
1102 refresh_y = cursor.y() - cursor.row()->baseline();
1103 cursor.par()->params().lineTop(line_top);
1104 cursor.par()->params().lineBottom(line_bottom);
1105 cursor.par()->params().pagebreakTop(pagebreak_top);
1106 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1107 cursor.par()->params().spaceTop(space_top);
1108 cursor.par()->params().spaceBottom(space_bottom);
1109 // does the layout allow the new alignment?
1110 if (align == LYX_ALIGN_LAYOUT)
1111 align = textclasslist
1112 .Style(bview->buffer()->params.textclass,
1113 cursor.par()->getLayout()).align;
1114 if (align & textclasslist
1115 .Style(bview->buffer()->params.textclass,
1116 cursor.par()->getLayout()).alignpossible) {
1117 if (align == textclasslist
1118 .Style(bview->buffer()->params.textclass,
1119 cursor.par()->getLayout()).align)
1120 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1122 cursor.par()->params().align(align);
1124 cursor.par()->setLabelWidthString(labelwidthstring);
1125 cursor.par()->params().noindent(noindent);
1126 tmppar = cursor.par()->previous();
1129 redoParagraphs(bview, selection.start, endpar);
1131 clearSelection(bview);
1132 setCursor(bview, selection.start.par(), selection.start.pos());
1133 selection.cursor = cursor;
1134 setCursor(bview, selection.end.par(), selection.end.pos());
1135 setSelection(bview);
1136 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1138 bview->updateInset(inset_owner, true);
1142 char loweralphaCounter(int n)
1144 if (n < 1 || n > 26)
1154 char alphaCounter(int n)
1156 if (n < 1 || n > 26)
1164 char hebrewCounter(int n)
1166 static const char hebrew[22] = {
1167 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1168 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1171 if (n < 1 || n > 22)
1179 string const romanCounter(int n)
1181 static char const * roman[20] = {
1182 "i", "ii", "iii", "iv", "v",
1183 "vi", "vii", "viii", "ix", "x",
1184 "xi", "xii", "xiii", "xiv", "xv",
1185 "xvi", "xvii", "xviii", "xix", "xx"
1187 if (n < 1 || n > 20)
1196 // set the counter of a paragraph. This includes the labels
1197 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1199 LyXLayout const & layout =
1200 textclasslist.Style(buf->params.textclass,
1203 LyXTextClass const & textclass =
1204 textclasslist.TextClass(buf->params.textclass);
1206 // copy the prev-counters to this one,
1207 // unless this is the first paragraph
1208 if (par->previous()) {
1209 for (int i = 0; i < 10; ++i) {
1210 par->setCounter(i, par->previous()->getFirstCounter(i));
1212 par->params().appendix(par->previous()->params().appendix());
1213 if (!par->params().appendix() && par->params().startOfAppendix()) {
1214 par->params().appendix(true);
1215 for (int i = 0; i < 10; ++i) {
1216 par->setCounter(i, 0);
1219 par->enumdepth = par->previous()->enumdepth;
1220 par->itemdepth = par->previous()->itemdepth;
1222 for (int i = 0; i < 10; ++i) {
1223 par->setCounter(i, 0);
1225 par->params().appendix(par->params().startOfAppendix());
1230 /* Maybe we have to increment the enumeration depth.
1231 * BUT, enumeration in a footnote is considered in isolation from its
1232 * surrounding paragraph so don't increment if this is the
1233 * first line of the footnote
1234 * AND, bibliographies can't have their depth changed ie. they
1235 * are always of depth 0
1238 && par->previous()->getDepth() < par->getDepth()
1239 && textclasslist.Style(buf->params.textclass,
1240 par->previous()->getLayout()
1241 ).labeltype == LABEL_COUNTER_ENUMI
1242 && par->enumdepth < 3
1243 && layout.labeltype != LABEL_BIBLIO) {
1247 // Maybe we have to decrement the enumeration depth, see note above
1249 && par->previous()->getDepth() > par->getDepth()
1250 && layout.labeltype != LABEL_BIBLIO) {
1251 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1252 par->setCounter(6 + par->enumdepth,
1253 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1254 /* reset the counters.
1255 * A depth change is like a breaking layout
1257 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1258 par->setCounter(i, 0);
1261 if (!par->params().labelString().empty()) {
1262 par->params().labelString(string());
1265 if (layout.margintype == MARGIN_MANUAL) {
1266 if (par->params().labelWidthString().empty()) {
1267 par->setLabelWidthString(layout.labelstring());
1270 par->setLabelWidthString(string());
1273 // is it a layout that has an automatic label?
1274 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1276 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1277 if (i >= 0 && i<= buf->params.secnumdepth) {
1278 par->incCounter(i); // increment the counter
1280 // Is there a label? Useful for Chapter layout
1281 if (!par->params().appendix()) {
1282 if (!layout.labelstring().empty())
1283 par->params().labelString(layout.labelstring());
1285 par->params().labelString(string());
1287 if (!layout.labelstring_appendix().empty())
1288 par->params().labelString(layout.labelstring_appendix());
1290 par->params().labelString(string());
1295 if (!par->params().appendix()) {
1296 switch (2 * LABEL_COUNTER_CHAPTER -
1297 textclass.maxcounter() + i) {
1298 case LABEL_COUNTER_CHAPTER:
1299 s << par->getCounter(i);
1301 case LABEL_COUNTER_SECTION:
1302 s << par->getCounter(i - 1) << '.'
1303 << par->getCounter(i);
1305 case LABEL_COUNTER_SUBSECTION:
1306 s << par->getCounter(i - 2) << '.'
1307 << par->getCounter(i - 1) << '.'
1308 << par->getCounter(i);
1310 case LABEL_COUNTER_SUBSUBSECTION:
1311 s << par->getCounter(i - 3) << '.'
1312 << par->getCounter(i - 2) << '.'
1313 << par->getCounter(i - 1) << '.'
1314 << par->getCounter(i);
1317 case LABEL_COUNTER_PARAGRAPH:
1318 s << par->getCounter(i - 4) << '.'
1319 << par->getCounter(i - 3) << '.'
1320 << par->getCounter(i - 2) << '.'
1321 << par->getCounter(i - 1) << '.'
1322 << par->getCounter(i);
1324 case LABEL_COUNTER_SUBPARAGRAPH:
1325 s << par->getCounter(i - 5) << '.'
1326 << par->getCounter(i - 4) << '.'
1327 << par->getCounter(i - 3) << '.'
1328 << par->getCounter(i - 2) << '.'
1329 << par->getCounter(i - 1) << '.'
1330 << par->getCounter(i);
1334 // Can this ever be reached? And in the
1335 // case it is, how can this be correct?
1337 s << par->getCounter(i) << '.';
1340 } else { // appendix
1341 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1342 case LABEL_COUNTER_CHAPTER:
1343 if (par->isRightToLeftPar(buf->params))
1344 s << hebrewCounter(par->getCounter(i));
1346 s << alphaCounter(par->getCounter(i));
1348 case LABEL_COUNTER_SECTION:
1349 if (par->isRightToLeftPar(buf->params))
1350 s << hebrewCounter(par->getCounter(i - 1));
1352 s << alphaCounter(par->getCounter(i - 1));
1355 << par->getCounter(i);
1358 case LABEL_COUNTER_SUBSECTION:
1359 if (par->isRightToLeftPar(buf->params))
1360 s << hebrewCounter(par->getCounter(i - 2));
1362 s << alphaCounter(par->getCounter(i - 2));
1365 << par->getCounter(i-1) << '.'
1366 << par->getCounter(i);
1369 case LABEL_COUNTER_SUBSUBSECTION:
1370 if (par->isRightToLeftPar(buf->params))
1371 s << hebrewCounter(par->getCounter(i-3));
1373 s << alphaCounter(par->getCounter(i-3));
1376 << par->getCounter(i-2) << '.'
1377 << par->getCounter(i-1) << '.'
1378 << par->getCounter(i);
1381 case LABEL_COUNTER_PARAGRAPH:
1382 if (par->isRightToLeftPar(buf->params))
1383 s << hebrewCounter(par->getCounter(i-4));
1385 s << alphaCounter(par->getCounter(i-4));
1388 << par->getCounter(i-3) << '.'
1389 << par->getCounter(i-2) << '.'
1390 << par->getCounter(i-1) << '.'
1391 << par->getCounter(i);
1394 case LABEL_COUNTER_SUBPARAGRAPH:
1395 if (par->isRightToLeftPar(buf->params))
1396 s << hebrewCounter(par->getCounter(i-5));
1398 s << alphaCounter(par->getCounter(i-5));
1401 << par->getCounter(i-4) << '.'
1402 << par->getCounter(i-3) << '.'
1403 << par->getCounter(i-2) << '.'
1404 << par->getCounter(i-1) << '.'
1405 << par->getCounter(i);
1409 // Can this ever be reached? And in the
1410 // case it is, how can this be correct?
1412 s << par->getCounter(i) << '.';
1418 par->params().labelString(par->params().labelString() +s.str().c_str());
1419 // We really want to remove the c_str as soon as
1422 for (i++; i < 10; ++i) {
1423 // reset the following counters
1424 par->setCounter(i, 0);
1426 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1427 for (i++; i < 10; ++i) {
1428 // reset the following counters
1429 par->setCounter(i, 0);
1431 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1432 par->incCounter(i + par->enumdepth);
1433 int number = par->getCounter(i + par->enumdepth);
1437 switch (par->enumdepth) {
1439 if (par->isRightToLeftPar(buf->params))
1441 << hebrewCounter(number)
1445 << loweralphaCounter(number)
1449 if (par->isRightToLeftPar(buf->params))
1450 s << '.' << romanCounter(number);
1452 s << romanCounter(number) << '.';
1455 if (par->isRightToLeftPar(buf->params))
1457 << alphaCounter(number);
1459 s << alphaCounter(number)
1463 if (par->isRightToLeftPar(buf->params))
1470 par->params().labelString(s.str().c_str());
1472 for (i += par->enumdepth + 1; i < 10; ++i) {
1473 // reset the following counters
1474 par->setCounter(i, 0);
1478 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1479 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1481 int number = par->getCounter(i);
1483 InsetCommandParams p( "bibitem" );
1484 par->bibkey = new InsetBibKey(p);
1486 par->bibkey->setCounter(number);
1487 par->params().labelString(layout.labelstring());
1489 // In biblio should't be following counters but...
1491 string s = layout.labelstring();
1493 // the caption hack:
1494 if (layout.labeltype == LABEL_SENSITIVE) {
1495 bool isOK (par->inInset() && par->inInset()->owner() &&
1496 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1499 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1501 = floatList.getType(tmp->type());
1502 // We should get the correct number here too.
1503 s = fl.name() + " #:";
1505 /* par->SetLayout(0);
1506 s = layout->labelstring; */
1507 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1508 ? " :úåòîùî øñç" : "Senseless: ";
1511 par->params().labelString(s);
1513 /* reset the enumeration counter. They are always resetted
1514 * when there is any other layout between */
1515 for (int i = 6 + par->enumdepth; i < 10; ++i)
1516 par->setCounter(i, 0);
1521 // Updates all counters BEHIND the row. Changed paragraphs
1522 // with a dynamic left margin will be rebroken.
1523 void LyXText::updateCounters(BufferView * bview, Row * row) const
1531 par = row->par()->next();
1535 while (row->par() != par)
1538 setCounter(bview->buffer(), par);
1540 // now check for the headline layouts. remember that they
1541 // have a dynamic left margin
1542 if ((textclasslist.Style(bview->buffer()->params.textclass,
1543 par->layout).margintype == MARGIN_DYNAMIC
1544 || textclasslist.Style(bview->buffer()->params.textclass,
1545 par->layout).labeltype == LABEL_SENSITIVE)) {
1547 // Rebreak the paragraph
1548 removeParagraph(row);
1549 appendParagraph(bview, row);
1556 void LyXText::insertInset(BufferView * bview, Inset * inset)
1558 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1560 setUndo(bview, Undo::INSERT,
1561 cursor.par(), cursor.par()->next());
1562 cursor.par()->insertInset(cursor.pos(), inset);
1563 // Just to rebreak and refresh correctly.
1564 // The character will not be inserted a second time
1565 insertChar(bview, Paragraph::META_INSET);
1567 // If we enter a highly editable inset the cursor should be to before
1568 // the inset. This couldn't happen before as Undo was not handled inside
1569 // inset now after the Undo LyX tries to call inset->Edit(...) again
1570 // and cannot do this as the cursor is behind the inset and GetInset
1571 // does not return the inset!
1572 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1573 cursorLeft(bview, true);
1579 void LyXText::copyEnvironmentType()
1581 copylayouttype = cursor.par()->getLayout();
1585 void LyXText::pasteEnvironmentType(BufferView * bview)
1587 setLayout(bview, copylayouttype);
1591 void LyXText::cutSelection(BufferView * bview, bool doclear)
1593 // Stuff what we got on the clipboard. Even if there is no selection.
1595 // There is a problem with having the stuffing here in that the
1596 // larger the selection the slower LyX will get. This can be
1597 // solved by running the line below only when the selection has
1598 // finished. The solution used currently just works, to make it
1599 // faster we need to be more clever and probably also have more
1600 // calls to stuffClipboard. (Lgb)
1601 bview->stuffClipboard(selectionAsString(bview->buffer()));
1603 // This doesn't make sense, if there is no selection
1604 if (!selection.set())
1607 // OK, we have a selection. This is always between selection.start
1608 // and selection.end
1610 // make sure that the depth behind the selection are restored, too
1611 Paragraph * endpar = selection.end.par()->next();
1612 Paragraph * undoendpar = endpar;
1614 if (endpar && endpar->getDepth()) {
1615 while (endpar && endpar->getDepth()) {
1616 endpar = endpar->next();
1617 undoendpar = endpar;
1619 } else if (endpar) {
1620 endpar = endpar->next(); // because of parindents etc.
1623 setUndo(bview, Undo::DELETE,
1624 selection.start.par(), undoendpar);
1626 // there are two cases: cut only within one paragraph or
1627 // more than one paragraph
1628 if (selection.start.par() == selection.end.par()) {
1629 // only within one paragraph
1630 endpar = selection.end.par();
1631 int pos = selection.end.pos();
1632 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1633 selection.start.pos(), pos,
1634 bview->buffer()->params.textclass, doclear);
1635 selection.end.pos(pos);
1637 endpar = selection.end.par();
1638 int pos = selection.end.pos();
1639 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1640 selection.start.pos(), pos,
1641 bview->buffer()->params.textclass, doclear);
1643 selection.end.par(endpar);
1644 selection.end.pos(pos);
1645 cursor.pos(selection.end.pos());
1647 endpar = endpar->next();
1649 // sometimes necessary
1651 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1653 redoParagraphs(bview, selection.start, endpar);
1655 // cutSelection can invalidate the cursor so we need to set
1657 cursor = selection.start;
1659 // need a valid cursor. (Lgb)
1660 clearSelection(bview);
1662 setCursor(bview, cursor.par(), cursor.pos());
1663 selection.cursor = cursor;
1664 updateCounters(bview, cursor.row());
1668 void LyXText::copySelection(BufferView * bview)
1670 // Stuff what we got on the clipboard. Even if there is no selection.
1672 // There is a problem with having the stuffing here in that the
1673 // larger the selection the slower LyX will get. This can be
1674 // solved by running the line below only when the selection has
1675 // finished. The solution used currently just works, to make it
1676 // faster we need to be more clever and probably also have more
1677 // calls to stuffClipboard. (Lgb)
1678 bview->stuffClipboard(selectionAsString(bview->buffer()));
1680 // this doesnt make sense, if there is no selection
1681 if (!selection.set())
1684 // ok we have a selection. This is always between selection.start
1685 // and sel_end cursor
1687 // copy behind a space if there is one
1688 while (selection.start.par()->size() > selection.start.pos()
1689 && selection.start.par()->isLineSeparator(selection.start.pos())
1690 && (selection.start.par() != selection.end.par()
1691 || selection.start.pos() < selection.end.pos()))
1692 selection.start.pos(selection.start.pos() + 1);
1694 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1695 selection.start.pos(), selection.end.pos(),
1696 bview->buffer()->params.textclass);
1700 void LyXText::pasteSelection(BufferView * bview)
1702 // this does not make sense, if there is nothing to paste
1703 if (!CutAndPaste::checkPastePossible(cursor.par()))
1706 setUndo(bview, Undo::INSERT,
1707 cursor.par(), cursor.par()->next());
1710 Paragraph * actpar = cursor.par();
1712 int pos = cursor.pos();
1713 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1714 bview->buffer()->params.textclass);
1716 redoParagraphs(bview, cursor, endpar);
1718 setCursor(bview, cursor.par(), cursor.pos());
1719 clearSelection(bview);
1721 selection.cursor = cursor;
1722 setCursor(bview, actpar, pos);
1723 setSelection(bview);
1724 updateCounters(bview, cursor.row());
1728 // returns a pointer to the very first Paragraph
1729 Paragraph * LyXText::firstParagraph() const
1731 return ownerParagraph();
1735 // sets the selection over the number of characters of string, no check!!
1736 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1741 selection.cursor = cursor;
1742 for (string::size_type i = 0; i < str.length(); ++i)
1744 setSelection(bview);
1748 // simple replacing. The font of the first selected character is used
1749 void LyXText::replaceSelectionWithString(BufferView * bview,
1752 setCursorParUndo(bview);
1755 if (!selection.set()) { // create a dummy selection
1756 selection.end = cursor;
1757 selection.start = cursor;
1760 // Get font setting before we cut
1761 Paragraph::size_type pos = selection.end.pos();
1762 LyXFont const font = selection.start.par()
1763 ->getFontSettings(bview->buffer()->params,
1764 selection.start.pos());
1766 // Insert the new string
1767 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1768 selection.end.par()->insertChar(pos, (*cit), font);
1772 // Cut the selection
1773 cutSelection(bview);
1779 // needed to insert the selection
1780 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1782 Paragraph * par = cursor.par();
1783 Paragraph::size_type pos = cursor.pos();
1784 Paragraph * endpar = cursor.par()->next();
1786 setCursorParUndo(bview);
1788 // only to be sure, should not be neccessary
1789 clearSelection(bview);
1791 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1793 redoParagraphs(bview, cursor, endpar);
1794 setCursor(bview, cursor.par(), cursor.pos());
1795 selection.cursor = cursor;
1796 setCursor(bview, par, pos);
1797 setSelection(bview);
1801 // turns double-CR to single CR, others where converted into one
1802 // blank. Then InsertStringAsLines is called
1803 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1805 string linestr(str);
1806 bool newline_inserted = false;
1807 for (string::size_type i = 0; i < linestr.length(); ++i) {
1808 if (linestr[i] == '\n') {
1809 if (newline_inserted) {
1810 // we know that \r will be ignored by
1811 // InsertStringA. Of course, it is a dirty
1812 // trick, but it works...
1813 linestr[i - 1] = '\r';
1817 newline_inserted = true;
1819 } else if (IsPrintable(linestr[i])) {
1820 newline_inserted = false;
1823 insertStringAsLines(bview, linestr);
1827 bool LyXText::gotoNextInset(BufferView * bview,
1828 std::vector<Inset::Code> const & codes,
1829 string const & contents) const
1831 LyXCursor res = cursor;
1834 if (res.pos() < res.par()->size() - 1) {
1835 res.pos(res.pos() + 1);
1837 res.par(res.par()->next());
1841 } while (res.par() &&
1842 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1843 && (inset = res.par()->getInset(res.pos())) != 0
1844 && find(codes.begin(), codes.end(), inset->lyxCode())
1846 && (contents.empty() ||
1847 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1851 setCursor(bview, res.par(), res.pos());
1858 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1859 Paragraph::size_type pos)
1861 LyXCursor tmpcursor;
1864 Paragraph::size_type z;
1865 Row * row = getRow(par, pos, y);
1867 // is there a break one row above
1868 if (row->previous() && row->previous()->par() == row->par()) {
1869 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1870 if (z >= row->pos()) {
1871 // set the dimensions of the row above
1872 y -= row->previous()->height();
1874 refresh_row = row->previous();
1875 status(bview, LyXText::NEED_MORE_REFRESH);
1877 breakAgain(bview, row->previous());
1879 // set the cursor again. Otherwise
1880 // dangling pointers are possible
1881 setCursor(bview, cursor.par(), cursor.pos(),
1882 false, cursor.boundary());
1883 selection.cursor = cursor;
1888 int const tmpheight = row->height();
1889 Paragraph::size_type const tmplast = rowLast(row);
1893 breakAgain(bview, row);
1894 if (row->height() == tmpheight && rowLast(row) == tmplast)
1895 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1897 status(bview, LyXText::NEED_MORE_REFRESH);
1899 // check the special right address boxes
1900 if (textclasslist.Style(bview->buffer()->params.textclass,
1901 par->getLayout()).margintype
1902 == MARGIN_RIGHT_ADDRESS_BOX) {
1909 redoDrawingOfParagraph(bview, tmpcursor);
1912 // set the cursor again. Otherwise dangling pointers are possible
1913 // also set the selection
1915 if (selection.set()) {
1917 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1918 false, selection.cursor.boundary());
1919 selection.cursor = cursor;
1920 setCursorIntern(bview, selection.start.par(),
1921 selection.start.pos(),
1922 false, selection.start.boundary());
1923 selection.start = cursor;
1924 setCursorIntern(bview, selection.end.par(),
1925 selection.end.pos(),
1926 false, selection.end.boundary());
1927 selection.end = cursor;
1928 setCursorIntern(bview, last_sel_cursor.par(),
1929 last_sel_cursor.pos(),
1930 false, last_sel_cursor.boundary());
1931 last_sel_cursor = cursor;
1934 setCursorIntern(bview, cursor.par(), cursor.pos(),
1935 false, cursor.boundary());
1939 // returns false if inset wasn't found
1940 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1942 // first check the current paragraph
1943 int pos = cursor.par()->getPositionOfInset(inset);
1945 checkParagraph(bview, cursor.par(), pos);
1949 // check every paragraph
1951 Paragraph * par = firstParagraph();
1953 pos = par->getPositionOfInset(inset);
1955 checkParagraph(bview, par, pos);
1965 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1966 Paragraph::size_type pos,
1967 bool setfont, bool boundary) const
1969 LyXCursor old_cursor = cursor;
1970 setCursorIntern(bview, par, pos, setfont, boundary);
1971 deleteEmptyParagraphMechanism(bview, old_cursor);
1975 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1976 Paragraph::size_type pos, bool boundary) const
1980 cur.boundary(boundary);
1982 // get the cursor y position in text
1984 Row * row = getRow(par, pos, y);
1985 // y is now the beginning of the cursor row
1986 y += row->baseline();
1987 // y is now the cursor baseline
1990 // now get the cursors x position
1992 float fill_separator;
1994 float fill_label_hfill;
1995 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1997 Paragraph::size_type cursor_vpos = 0;
1998 Paragraph::size_type last = rowLastPrintable(row);
2000 if (pos > last + 1) // This shouldn't happen.
2002 else if (pos < row->pos())
2005 if (last < row->pos())
2006 cursor_vpos = row->pos();
2007 else if (pos > last && !boundary)
2008 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2009 ? row->pos() : last + 1;
2010 else if (pos > row->pos() &&
2011 (pos > last || boundary))
2012 /// Place cursor after char at (logical) position pos - 1
2013 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2014 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2016 /// Place cursor before char at (logical) position pos
2017 cursor_vpos = (bidi_level(pos) % 2 == 0)
2018 ? log2vis(pos) : log2vis(pos) + 1;
2020 Paragraph::size_type main_body =
2021 beginningOfMainBody(bview->buffer(), row->par());
2022 if ((main_body > 0) &&
2023 ((main_body-1 > last) ||
2024 !row->par()->isLineSeparator(main_body-1)))
2027 for (Paragraph::size_type vpos = row->pos();
2028 vpos < cursor_vpos; ++vpos) {
2029 pos = vis2log(vpos);
2030 if (main_body > 0 && pos == main_body - 1) {
2031 x += fill_label_hfill +
2032 lyxfont::width(textclasslist.Style(
2033 bview->buffer()->params.textclass,
2034 row->par()->getLayout())
2036 getFont(bview->buffer(), row->par(), -2));
2037 if (row->par()->isLineSeparator(main_body-1))
2038 x -= singleWidth(bview, row->par(),main_body-1);
2040 if (hfillExpansion(bview->buffer(), row, pos)) {
2041 x += singleWidth(bview, row->par(), pos);
2042 if (pos >= main_body)
2045 x += fill_label_hfill;
2046 } else if (row->par()->isSeparator(pos)) {
2047 x += singleWidth(bview, row->par(), pos);
2048 if (pos >= main_body)
2049 x += fill_separator;
2051 x += singleWidth(bview, row->par(), pos);
2060 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2061 Paragraph::size_type pos,
2062 bool setfont, bool boundary) const
2064 InsetText * it = static_cast<InsetText *>(par->inInset());
2065 if (it && (it != inset_owner)) {
2066 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2069 setCursor(bview, cursor, par, pos, boundary);
2071 setCurrentFont(bview);
2076 void LyXText::setCurrentFont(BufferView * bview) const
2078 Paragraph::size_type pos = cursor.pos();
2079 if (cursor.boundary() && pos > 0)
2083 if (pos == cursor.par()->size())
2085 else // potentional bug... BUG (Lgb)
2086 if (cursor.par()->isSeparator(pos)) {
2087 if (pos > cursor.row()->pos() &&
2088 bidi_level(pos) % 2 ==
2089 bidi_level(pos - 1) % 2)
2091 else if (pos + 1 < cursor.par()->size())
2097 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2098 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2100 if (cursor.pos() == cursor.par()->size() &&
2101 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2102 !cursor.boundary()) {
2103 Language const * lang =
2104 cursor.par()->getParLanguage(bview->buffer()->params);
2105 current_font.setLanguage(lang);
2106 current_font.setNumber(LyXFont::OFF);
2107 real_current_font.setLanguage(lang);
2108 real_current_font.setNumber(LyXFont::OFF);
2113 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2115 LyXCursor old_cursor = cursor;
2117 // Get the row first.
2119 Row * row = getRowNearY(y);
2120 cursor.par(row->par());
2123 int column = getColumnNearX(bview, row, x, bound);
2124 cursor.pos(row->pos() + column);
2126 cursor.y(y + row->baseline());
2128 cursor.boundary(bound);
2129 setCurrentFont(bview);
2130 deleteEmptyParagraphMechanism(bview, old_cursor);
2134 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2137 // Get the row first.
2139 Row * row = getRowNearY(y);
2141 int column = getColumnNearX(bview, row, x, bound);
2143 cur.par(row->par());
2144 cur.pos(row->pos() + column);
2146 cur.y(y + row->baseline());
2148 cur.boundary(bound);
2152 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2154 if (cursor.pos() > 0) {
2155 bool boundary = cursor.boundary();
2156 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2157 if (!internal && !boundary &&
2158 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2159 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2160 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2161 Paragraph * par = cursor.par()->previous();
2162 setCursor(bview, par, par->size());
2167 void LyXText::cursorRight(BufferView * bview, bool internal) const
2169 if (!internal && cursor.boundary() &&
2170 !cursor.par()->isNewline(cursor.pos()))
2171 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2172 else if (cursor.pos() < cursor.par()->size()) {
2173 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2175 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2176 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2177 } else if (cursor.par()->next())
2178 setCursor(bview, cursor.par()->next(), 0);
2182 void LyXText::cursorUp(BufferView * bview) const
2184 setCursorFromCoordinates(bview, cursor.x_fix(),
2185 cursor.y() - cursor.row()->baseline() - 1);
2189 void LyXText::cursorDown(BufferView * bview) const
2191 setCursorFromCoordinates(bview, cursor.x_fix(),
2192 cursor.y() - cursor.row()->baseline()
2193 + cursor.row()->height() + 1);
2197 void LyXText::cursorUpParagraph(BufferView * bview) const
2199 if (cursor.pos() > 0) {
2200 setCursor(bview, cursor.par(), 0);
2202 else if (cursor.par()->previous()) {
2203 setCursor(bview, cursor.par()->previous(), 0);
2208 void LyXText::cursorDownParagraph(BufferView * bview) const
2210 if (cursor.par()->next()) {
2211 setCursor(bview, cursor.par()->next(), 0);
2213 setCursor(bview, cursor.par(), cursor.par()->size());
2218 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2219 LyXCursor const & old_cursor) const
2221 // Would be wrong to delete anything if we have a selection.
2222 if (selection.set()) return;
2224 // We allow all kinds of "mumbo-jumbo" when freespacing.
2225 if (textclasslist.Style(bview->buffer()->params.textclass,
2226 old_cursor.par()->getLayout()).free_spacing)
2229 bool deleted = false;
2231 /* Ok I'll put some comments here about what is missing.
2232 I have fixed BackSpace (and thus Delete) to not delete
2233 double-spaces automagically. I have also changed Cut,
2234 Copy and Paste to hopefully do some sensible things.
2235 There are still some small problems that can lead to
2236 double spaces stored in the document file or space at
2237 the beginning of paragraphs. This happens if you have
2238 the cursor betwenn to spaces and then save. Or if you
2239 cut and paste and the selection have a space at the
2240 beginning and then save right after the paste. I am
2241 sure none of these are very hard to fix, but I will
2242 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2243 that I can get some feedback. (Lgb)
2246 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2247 // delete the LineSeparator.
2250 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2251 // delete the LineSeparator.
2254 // If the pos around the old_cursor were spaces, delete one of them.
2255 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2256 // Only if the cursor has really moved
2258 if (old_cursor.pos() > 0
2259 && old_cursor.pos() < old_cursor.par()->size()
2260 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2261 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2262 old_cursor.par()->erase(old_cursor.pos() - 1);
2263 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2265 if (old_cursor.par() == cursor.par() &&
2266 cursor.pos() > old_cursor.pos()) {
2267 setCursorIntern(bview, cursor.par(),
2270 setCursorIntern(bview, cursor.par(),
2276 // Do not delete empty paragraphs with keepempty set.
2277 if ((textclasslist.Style(bview->buffer()->params.textclass,
2278 old_cursor.par()->getLayout())).keepempty)
2281 LyXCursor tmpcursor;
2283 if (old_cursor.par() != cursor.par()) {
2284 if ((old_cursor.par()->size() == 0
2285 || (old_cursor.par()->size() == 1
2286 && old_cursor.par()->isLineSeparator(0)))) {
2287 // ok, we will delete anything
2289 // make sure that you do not delete any environments
2290 status(bview, LyXText::NEED_MORE_REFRESH);
2293 if (old_cursor.row()->previous()) {
2294 refresh_row = old_cursor.row()->previous();
2295 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2297 cursor = old_cursor; // that undo can restore the right cursor position
2298 Paragraph * endpar = old_cursor.par()->next();
2299 if (endpar && endpar->getDepth()) {
2300 while (endpar && endpar->getDepth()) {
2301 endpar = endpar->next();
2304 setUndo(bview, Undo::DELETE,
2310 removeRow(old_cursor.row());
2311 if (ownerParagraph() == old_cursor.par()) {
2312 ownerParagraph(ownerParagraph()->next());
2315 delete old_cursor.par();
2317 /* Breakagain the next par. Needed
2318 * because of the parindent that
2319 * can occur or dissappear. The
2320 * next row can change its height,
2321 * if there is another layout before */
2322 if (refresh_row->next()) {
2323 breakAgain(bview, refresh_row->next());
2324 updateCounters(bview, refresh_row);
2326 setHeightOfRow(bview, refresh_row);
2328 refresh_row = old_cursor.row()->next();
2329 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2332 cursor = old_cursor; // that undo can restore the right cursor position
2333 Paragraph * endpar = old_cursor.par()->next();
2334 if (endpar && endpar->getDepth()) {
2335 while (endpar && endpar->getDepth()) {
2336 endpar = endpar->next();
2339 setUndo(bview, Undo::DELETE,
2345 removeRow(old_cursor.row());
2347 if (ownerParagraph() == old_cursor.par()) {
2348 ownerParagraph(ownerParagraph()->next());
2351 delete old_cursor.par();
2353 /* Breakagain the next par. Needed
2354 because of the parindent that can
2355 occur or dissappear.
2356 The next row can change its height,
2357 if there is another layout before
2360 breakAgain(bview, refresh_row);
2361 updateCounters(bview, refresh_row->previous());
2367 setCursorIntern(bview, cursor.par(), cursor.pos());
2369 if (selection.cursor.par() == old_cursor.par()
2370 && selection.cursor.pos() == selection.cursor.pos()) {
2371 // correct selection
2372 selection.cursor = cursor;
2376 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2377 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2379 setCursorIntern(bview, cursor.par(), cursor.pos());
2380 selection.cursor = cursor;
2387 void LyXText::toggleAppendix(BufferView * bview)
2389 Paragraph * par = cursor.par();
2390 bool start = !par->params().startOfAppendix();
2392 // ensure that we have only one start_of_appendix in this document
2393 Paragraph * tmp = firstParagraph();
2394 for (; tmp; tmp = tmp->next()) {
2395 tmp->params().startOfAppendix(false);
2398 par->params().startOfAppendix(start);
2400 // we can set the refreshing parameters now
2401 status(bview, LyXText::NEED_MORE_REFRESH);
2403 refresh_row = 0; // not needed for full update
2404 updateCounters(bview, 0);
2405 setCursor(bview, cursor.par(), cursor.pos());
2409 Paragraph * LyXText::ownerParagraph() const
2412 return inset_owner->paragraph();
2414 return bv_owner->buffer()->paragraph;
2418 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2421 inset_owner->paragraph(p);
2423 bv_owner->buffer()->paragraph = p;
2428 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2430 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2431 if (op && op->inInset()) {
2432 static_cast<InsetText *>(op->inInset())->paragraph(p);
2435 inset_owner->paragraph(p);
2437 bv_owner->buffer()->paragraph = p;
2444 LyXText::text_status LyXText::status() const
2450 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2453 if ((status_ != NEED_MORE_REFRESH)
2454 || (status_ == NEED_MORE_REFRESH)
2455 && (st != NEED_VERY_LITTLE_REFRESH)) {
2457 if (inset_owner && st != UNCHANGED) {
2458 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2462 #warning Please tell what the intention is here. (Lgb)
2463 // The above does not make any sense, I changed it to what is here,
2464 // but it still does not make much sense. (Lgb)
2465 #warning Sure have a look now! (Jug)
2466 // well as much as I know && binds more then || so the above and the
2467 // below are identical (this for your known use of parentesis!)
2468 // Now some explanation:
2469 // We should only go up with refreshing code so this means that if
2470 // we have a MORE refresh we should never set it to LITTLE if we still
2471 // didn't handle it (and then it will be UNCHANGED. Now as long as
2472 // we stay inside one LyXText this may work but we need to tell the
2473 // outermost LyXText that it should REALLY draw us if there is some
2474 // change in a Inset::LyXText. So you see that when we are inside a
2475 // inset's LyXText we give the LITTLE to the outermost LyXText to
2476 // tell'em that it should redraw the actual row (where the inset
2477 // resides! Capito?!
2479 if ((status_ != NEED_MORE_REFRESH)
2480 || (status_ == NEED_MORE_REFRESH
2481 && st != NEED_VERY_LITTLE_REFRESH))
2484 if (inset_owner && st != UNCHANGED) {
2485 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);