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 bool isEnvironment =
1789 textclasslist.Style(bview->buffer()->params.textclass,
1790 cursor.par()->getLayout()).isEnvironment();
1792 textclasslist.Style(bview->buffer()->params.textclass,
1793 cursor.par()->getLayout()).free_spacing;
1795 textclasslist.Style(bview->buffer()->params.textclass,
1796 cursor.par()->getLayout()).keepempty;
1798 // only to be sure, should not be neccessary
1799 clearSelection(bview);
1801 // insert the string, don't insert doublespace
1802 bool space_inserted = true;
1803 for(string::const_iterator cit = str.begin();
1804 cit != str.end(); ++cit) {
1806 if (par->size() || keepempty) {
1807 par->breakParagraph(bview->buffer()->params,
1808 pos, isEnvironment);
1811 space_inserted = true;
1815 // do not insert consecutive spaces if !free_spacing
1816 } else if ((*cit == ' ' || *cit == '\t')
1817 && space_inserted && !free_spacing) {
1819 } else if (*cit == '\t') {
1820 if (!free_spacing) {
1821 // tabs are like spaces here
1822 par->insertChar(pos, ' ',
1825 space_inserted = true;
1827 const Paragraph::value_type nb = 8 - pos % 8;
1828 for (Paragraph::size_type a = 0;
1830 par->insertChar(pos, ' ',
1834 space_inserted = true;
1836 } else if (!IsPrintable(*cit)) {
1837 // Ignore unprintables
1840 // just insert the character
1841 par->insertChar(pos, *cit, current_font);
1843 space_inserted = (*cit == ' ');
1848 redoParagraphs(bview, cursor, endpar);
1849 setCursor(bview, cursor.par(), cursor.pos());
1850 selection.cursor = cursor;
1851 setCursor(bview, par, pos);
1852 setSelection(bview);
1856 // turns double-CR to single CR, others where converted into one
1857 // blank. Then InsertStringAsLines is called
1858 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1860 string linestr(str);
1861 bool newline_inserted = false;
1862 for (string::size_type i = 0; i < linestr.length(); ++i) {
1863 if (linestr[i] == '\n') {
1864 if (newline_inserted) {
1865 // we know that \r will be ignored by
1866 // InsertStringA. Of course, it is a dirty
1867 // trick, but it works...
1868 linestr[i - 1] = '\r';
1872 newline_inserted = true;
1874 } else if (IsPrintable(linestr[i])) {
1875 newline_inserted = false;
1878 insertStringAsLines(bview, linestr);
1882 bool LyXText::gotoNextInset(BufferView * bview,
1883 std::vector<Inset::Code> const & codes,
1884 string const & contents) const
1886 LyXCursor res = cursor;
1889 if (res.pos() < res.par()->size() - 1) {
1890 res.pos(res.pos() + 1);
1892 res.par(res.par()->next());
1896 } while (res.par() &&
1897 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1898 && (inset = res.par()->getInset(res.pos())) != 0
1899 && find(codes.begin(), codes.end(), inset->lyxCode())
1901 && (contents.empty() ||
1902 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1906 setCursor(bview, res.par(), res.pos());
1913 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1914 Paragraph::size_type pos)
1916 LyXCursor tmpcursor;
1919 Paragraph::size_type z;
1920 Row * row = getRow(par, pos, y);
1922 // is there a break one row above
1923 if (row->previous() && row->previous()->par() == row->par()) {
1924 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1925 if (z >= row->pos()) {
1926 // set the dimensions of the row above
1927 y -= row->previous()->height();
1929 refresh_row = row->previous();
1930 status(bview, LyXText::NEED_MORE_REFRESH);
1932 breakAgain(bview, row->previous());
1934 // set the cursor again. Otherwise
1935 // dangling pointers are possible
1936 setCursor(bview, cursor.par(), cursor.pos(),
1937 false, cursor.boundary());
1938 selection.cursor = cursor;
1943 int const tmpheight = row->height();
1944 Paragraph::size_type const tmplast = rowLast(row);
1948 breakAgain(bview, row);
1949 if (row->height() == tmpheight && rowLast(row) == tmplast)
1950 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1952 status(bview, LyXText::NEED_MORE_REFRESH);
1954 // check the special right address boxes
1955 if (textclasslist.Style(bview->buffer()->params.textclass,
1956 par->getLayout()).margintype
1957 == MARGIN_RIGHT_ADDRESS_BOX) {
1964 redoDrawingOfParagraph(bview, tmpcursor);
1967 // set the cursor again. Otherwise dangling pointers are possible
1968 // also set the selection
1970 if (selection.set()) {
1972 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1973 false, selection.cursor.boundary());
1974 selection.cursor = cursor;
1975 setCursorIntern(bview, selection.start.par(),
1976 selection.start.pos(),
1977 false, selection.start.boundary());
1978 selection.start = cursor;
1979 setCursorIntern(bview, selection.end.par(),
1980 selection.end.pos(),
1981 false, selection.end.boundary());
1982 selection.end = cursor;
1983 setCursorIntern(bview, last_sel_cursor.par(),
1984 last_sel_cursor.pos(),
1985 false, last_sel_cursor.boundary());
1986 last_sel_cursor = cursor;
1989 setCursorIntern(bview, cursor.par(), cursor.pos(),
1990 false, cursor.boundary());
1994 // returns false if inset wasn't found
1995 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1997 // first check the current paragraph
1998 int pos = cursor.par()->getPositionOfInset(inset);
2000 checkParagraph(bview, cursor.par(), pos);
2004 // check every paragraph
2006 Paragraph * par = firstParagraph();
2008 pos = par->getPositionOfInset(inset);
2010 checkParagraph(bview, par, pos);
2020 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2021 Paragraph::size_type pos,
2022 bool setfont, bool boundary) const
2024 LyXCursor old_cursor = cursor;
2025 setCursorIntern(bview, par, pos, setfont, boundary);
2026 deleteEmptyParagraphMechanism(bview, old_cursor);
2030 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2031 Paragraph::size_type pos, bool boundary) const
2035 cur.boundary(boundary);
2037 // get the cursor y position in text
2039 Row * row = getRow(par, pos, y);
2040 // y is now the beginning of the cursor row
2041 y += row->baseline();
2042 // y is now the cursor baseline
2045 // now get the cursors x position
2047 float fill_separator;
2049 float fill_label_hfill;
2050 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2052 Paragraph::size_type cursor_vpos = 0;
2053 Paragraph::size_type last = rowLastPrintable(row);
2055 if (pos > last + 1) // This shouldn't happen.
2057 else if (pos < row->pos())
2060 if (last < row->pos())
2061 cursor_vpos = row->pos();
2062 else if (pos > last && !boundary)
2063 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2064 ? row->pos() : last + 1;
2065 else if (pos > row->pos() &&
2066 (pos > last || boundary))
2067 /// Place cursor after char at (logical) position pos - 1
2068 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2069 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2071 /// Place cursor before char at (logical) position pos
2072 cursor_vpos = (bidi_level(pos) % 2 == 0)
2073 ? log2vis(pos) : log2vis(pos) + 1;
2075 Paragraph::size_type main_body =
2076 beginningOfMainBody(bview->buffer(), row->par());
2077 if ((main_body > 0) &&
2078 ((main_body-1 > last) ||
2079 !row->par()->isLineSeparator(main_body-1)))
2082 for (Paragraph::size_type vpos = row->pos();
2083 vpos < cursor_vpos; ++vpos) {
2084 pos = vis2log(vpos);
2085 if (main_body > 0 && pos == main_body - 1) {
2086 x += fill_label_hfill +
2087 lyxfont::width(textclasslist.Style(
2088 bview->buffer()->params.textclass,
2089 row->par()->getLayout())
2091 getFont(bview->buffer(), row->par(), -2));
2092 if (row->par()->isLineSeparator(main_body-1))
2093 x -= singleWidth(bview, row->par(),main_body-1);
2095 if (hfillExpansion(bview->buffer(), row, pos)) {
2096 x += singleWidth(bview, row->par(), pos);
2097 if (pos >= main_body)
2100 x += fill_label_hfill;
2101 } else if (row->par()->isSeparator(pos)) {
2102 x += singleWidth(bview, row->par(), pos);
2103 if (pos >= main_body)
2104 x += fill_separator;
2106 x += singleWidth(bview, row->par(), pos);
2115 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2116 Paragraph::size_type pos,
2117 bool setfont, bool boundary) const
2119 InsetText * it = static_cast<InsetText *>(par->inInset());
2120 if (it && (it != inset_owner)) {
2121 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2124 setCursor(bview, cursor, par, pos, boundary);
2126 setCurrentFont(bview);
2131 void LyXText::setCurrentFont(BufferView * bview) const
2133 Paragraph::size_type pos = cursor.pos();
2134 if (cursor.boundary() && pos > 0)
2138 if (pos == cursor.par()->size())
2140 else // potentional bug... BUG (Lgb)
2141 if (cursor.par()->isSeparator(pos)) {
2142 if (pos > cursor.row()->pos() &&
2143 bidi_level(pos) % 2 ==
2144 bidi_level(pos - 1) % 2)
2146 else if (pos + 1 < cursor.par()->size())
2152 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2153 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2155 if (cursor.pos() == cursor.par()->size() &&
2156 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2157 !cursor.boundary()) {
2158 Language const * lang =
2159 cursor.par()->getParLanguage(bview->buffer()->params);
2160 current_font.setLanguage(lang);
2161 current_font.setNumber(LyXFont::OFF);
2162 real_current_font.setLanguage(lang);
2163 real_current_font.setNumber(LyXFont::OFF);
2168 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2170 LyXCursor old_cursor = cursor;
2172 // Get the row first.
2174 Row * row = getRowNearY(y);
2175 cursor.par(row->par());
2178 int column = getColumnNearX(bview, row, x, bound);
2179 cursor.pos(row->pos() + column);
2181 cursor.y(y + row->baseline());
2183 cursor.boundary(bound);
2184 setCurrentFont(bview);
2185 deleteEmptyParagraphMechanism(bview, old_cursor);
2189 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2192 // Get the row first.
2194 Row * row = getRowNearY(y);
2196 int column = getColumnNearX(bview, row, x, bound);
2198 cur.par(row->par());
2199 cur.pos(row->pos() + column);
2201 cur.y(y + row->baseline());
2203 cur.boundary(bound);
2207 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2209 if (cursor.pos() > 0) {
2210 bool boundary = cursor.boundary();
2211 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2212 if (!internal && !boundary &&
2213 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2214 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2215 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2216 Paragraph * par = cursor.par()->previous();
2217 setCursor(bview, par, par->size());
2222 void LyXText::cursorRight(BufferView * bview, bool internal) const
2224 if (!internal && cursor.boundary() &&
2225 !cursor.par()->isNewline(cursor.pos()))
2226 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2227 else if (cursor.pos() < cursor.par()->size()) {
2228 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2230 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2231 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2232 } else if (cursor.par()->next())
2233 setCursor(bview, cursor.par()->next(), 0);
2237 void LyXText::cursorUp(BufferView * bview) const
2239 setCursorFromCoordinates(bview, cursor.x_fix(),
2240 cursor.y() - cursor.row()->baseline() - 1);
2244 void LyXText::cursorDown(BufferView * bview) const
2246 setCursorFromCoordinates(bview, cursor.x_fix(),
2247 cursor.y() - cursor.row()->baseline()
2248 + cursor.row()->height() + 1);
2252 void LyXText::cursorUpParagraph(BufferView * bview) const
2254 if (cursor.pos() > 0) {
2255 setCursor(bview, cursor.par(), 0);
2257 else if (cursor.par()->previous()) {
2258 setCursor(bview, cursor.par()->previous(), 0);
2263 void LyXText::cursorDownParagraph(BufferView * bview) const
2265 if (cursor.par()->next()) {
2266 setCursor(bview, cursor.par()->next(), 0);
2268 setCursor(bview, cursor.par(), cursor.par()->size());
2273 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2274 LyXCursor const & old_cursor) const
2276 // Would be wrong to delete anything if we have a selection.
2277 if (selection.set()) return;
2279 // We allow all kinds of "mumbo-jumbo" when freespacing.
2280 if (textclasslist.Style(bview->buffer()->params.textclass,
2281 old_cursor.par()->getLayout()).free_spacing)
2284 bool deleted = false;
2286 /* Ok I'll put some comments here about what is missing.
2287 I have fixed BackSpace (and thus Delete) to not delete
2288 double-spaces automagically. I have also changed Cut,
2289 Copy and Paste to hopefully do some sensible things.
2290 There are still some small problems that can lead to
2291 double spaces stored in the document file or space at
2292 the beginning of paragraphs. This happens if you have
2293 the cursor betwenn to spaces and then save. Or if you
2294 cut and paste and the selection have a space at the
2295 beginning and then save right after the paste. I am
2296 sure none of these are very hard to fix, but I will
2297 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2298 that I can get some feedback. (Lgb)
2301 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2302 // delete the LineSeparator.
2305 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2306 // delete the LineSeparator.
2309 // If the pos around the old_cursor were spaces, delete one of them.
2310 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2311 // Only if the cursor has really moved
2313 if (old_cursor.pos() > 0
2314 && old_cursor.pos() < old_cursor.par()->size()
2315 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2316 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2317 old_cursor.par()->erase(old_cursor.pos() - 1);
2318 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2320 if (old_cursor.par() == cursor.par() &&
2321 cursor.pos() > old_cursor.pos()) {
2322 setCursorIntern(bview, cursor.par(),
2325 setCursorIntern(bview, cursor.par(),
2331 // Do not delete empty paragraphs with keepempty set.
2332 if ((textclasslist.Style(bview->buffer()->params.textclass,
2333 old_cursor.par()->getLayout())).keepempty)
2336 LyXCursor tmpcursor;
2338 if (old_cursor.par() != cursor.par()) {
2339 if ((old_cursor.par()->size() == 0
2340 || (old_cursor.par()->size() == 1
2341 && old_cursor.par()->isLineSeparator(0)))) {
2342 // ok, we will delete anything
2344 // make sure that you do not delete any environments
2345 status(bview, LyXText::NEED_MORE_REFRESH);
2348 if (old_cursor.row()->previous()) {
2349 refresh_row = old_cursor.row()->previous();
2350 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2352 cursor = old_cursor; // that undo can restore the right cursor position
2353 Paragraph * endpar = old_cursor.par()->next();
2354 if (endpar && endpar->getDepth()) {
2355 while (endpar && endpar->getDepth()) {
2356 endpar = endpar->next();
2359 setUndo(bview, Undo::DELETE,
2365 removeRow(old_cursor.row());
2366 if (ownerParagraph() == old_cursor.par()) {
2367 ownerParagraph(ownerParagraph()->next());
2370 delete old_cursor.par();
2372 /* Breakagain the next par. Needed
2373 * because of the parindent that
2374 * can occur or dissappear. The
2375 * next row can change its height,
2376 * if there is another layout before */
2377 if (refresh_row->next()) {
2378 breakAgain(bview, refresh_row->next());
2379 updateCounters(bview, refresh_row);
2381 setHeightOfRow(bview, refresh_row);
2383 refresh_row = old_cursor.row()->next();
2384 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2387 cursor = old_cursor; // that undo can restore the right cursor position
2388 Paragraph * endpar = old_cursor.par()->next();
2389 if (endpar && endpar->getDepth()) {
2390 while (endpar && endpar->getDepth()) {
2391 endpar = endpar->next();
2394 setUndo(bview, Undo::DELETE,
2400 removeRow(old_cursor.row());
2402 if (ownerParagraph() == old_cursor.par()) {
2403 ownerParagraph(ownerParagraph()->next());
2406 delete old_cursor.par();
2408 /* Breakagain the next par. Needed
2409 because of the parindent that can
2410 occur or dissappear.
2411 The next row can change its height,
2412 if there is another layout before
2415 breakAgain(bview, refresh_row);
2416 updateCounters(bview, refresh_row->previous());
2422 setCursorIntern(bview, cursor.par(), cursor.pos());
2424 if (selection.cursor.par() == old_cursor.par()
2425 && selection.cursor.pos() == selection.cursor.pos()) {
2426 // correct selection
2427 selection.cursor = cursor;
2431 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2432 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2434 setCursorIntern(bview, cursor.par(), cursor.pos());
2435 selection.cursor = cursor;
2442 void LyXText::toggleAppendix(BufferView * bview)
2444 Paragraph * par = cursor.par();
2445 bool start = !par->params().startOfAppendix();
2447 // ensure that we have only one start_of_appendix in this document
2448 Paragraph * tmp = firstParagraph();
2449 for (; tmp; tmp = tmp->next()) {
2450 tmp->params().startOfAppendix(false);
2453 par->params().startOfAppendix(start);
2455 // we can set the refreshing parameters now
2456 status(bview, LyXText::NEED_MORE_REFRESH);
2458 refresh_row = 0; // not needed for full update
2459 updateCounters(bview, 0);
2460 setCursor(bview, cursor.par(), cursor.pos());
2464 Paragraph * LyXText::ownerParagraph() const
2467 return inset_owner->paragraph();
2469 return bv_owner->buffer()->paragraph;
2473 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2476 inset_owner->paragraph(p);
2478 bv_owner->buffer()->paragraph = p;
2483 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2485 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2486 if (op && op->inInset()) {
2487 static_cast<InsetText *>(op->inInset())->paragraph(p);
2490 inset_owner->paragraph(p);
2492 bv_owner->buffer()->paragraph = p;
2499 LyXText::text_status LyXText::status() const
2505 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2508 if ((status_ != NEED_MORE_REFRESH)
2509 || (status_ == NEED_MORE_REFRESH)
2510 && (st != NEED_VERY_LITTLE_REFRESH)) {
2512 if (inset_owner && st != UNCHANGED) {
2513 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2517 #warning Please tell what the intention is here. (Lgb)
2518 // The above does not make any sense, I changed it to what is here,
2519 // but it still does not make much sense. (Lgb)
2520 #warning Sure have a look now! (Jug)
2521 // well as much as I know && binds more then || so the above and the
2522 // below are identical (this for your known use of parentesis!)
2523 // Now some explanation:
2524 // We should only go up with refreshing code so this means that if
2525 // we have a MORE refresh we should never set it to LITTLE if we still
2526 // didn't handle it (and then it will be UNCHANGED. Now as long as
2527 // we stay inside one LyXText this may work but we need to tell the
2528 // outermost LyXText that it should REALLY draw us if there is some
2529 // change in a Inset::LyXText. So you see that when we are inside a
2530 // inset's LyXText we give the LITTLE to the outermost LyXText to
2531 // tell'em that it should redraw the actual row (where the inset
2532 // resides! Capito?!
2534 if ((status_ != NEED_MORE_REFRESH)
2535 || (status_ == NEED_MORE_REFRESH
2536 && st != NEED_VERY_LITTLE_REFRESH))
2539 if (inset_owner && st != UNCHANGED) {
2540 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);