1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
28 #include "CutAndPaste.h"
34 #include "FloatList.h"
36 #include "ParagraphParameters.h"
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first_y(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
65 LyXText::LyXText(InsetText * inset)
66 : number_of_rows(0), height(0), width(0), first_y(0),
67 bv_owner(0), inset_owner(inset), the_locking_inset(0),
68 need_break_row(0), refresh_y(0), refresh_row(0),
69 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
73 void LyXText::init(BufferView * bview, bool reinit)
76 // Delete all rows, this does not touch the paragraphs!
77 Row * tmprow = firstrow;
79 tmprow = firstrow->next();
88 copylayouttype.erase();
89 number_of_rows = first_y = refresh_y = 0;
90 status_ = LyXText::UNCHANGED;
94 Paragraph * par = ownerParagraph();
95 current_font = getFont(bview->buffer(), par, 0);
98 insertParagraph(bview, par, lastrow);
101 setCursorIntern(bview, firstrow->par(), 0);
102 selection.cursor = cursor;
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 #ifndef INHERIT_LANGUAGE
132 tmpfont.realize(textclasslist[buf->params.textclass][
133 par->layout()].font);
135 tmpfont.realize(textclasslist.
136 Style(buf->params.textclass,
138 buf->params.language);
140 par_depth = par->getDepth();
144 #ifndef INHERIT_LANGUAGE
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
147 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
148 buf->params.language);
157 // Gets the fully instantiated font at a given position in a paragraph
158 // Basically the same routine as Paragraph::getFont() in paragraph.C.
159 // The difference is that this one is used for displaying, and thus we
160 // are allowed to make cosmetic improvements. For instance make footnotes
162 // If position is -1, we get the layout font of the paragraph.
163 // If position is -2, we get the font of the manual label of the paragraph.
164 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
167 lyx::Assert(pos >= 0);
169 LyXLayout const & layout =
170 textclasslist[buf->params.textclass][par->layout()];
172 Paragraph::depth_type par_depth = par->getDepth();
173 // We specialize the 95% common case:
175 if (layout.labeltype == LABEL_MANUAL
176 && pos < beginningOfMainBody(buf, par)) {
178 LyXFont f = par->getFontSettings(buf->params, pos);
180 par->inInset()->getDrawFont(f);
181 #ifndef INHERIT_LANGUAGE
182 return f.realize(layout.reslabelfont);
184 return f.realize(layout.reslabelfont, buf->params.language);
187 LyXFont f = par->getFontSettings(buf->params, pos);
189 par->inInset()->getDrawFont(f);
190 #ifndef INHERIT_LANGUAGE
191 return f.realize(layout.resfont);
193 return f.realize(layout.resfont, buf->params.language);
198 // The uncommon case need not be optimized as much
202 if (pos < beginningOfMainBody(buf, par)) {
204 layoutfont = layout.labelfont;
207 layoutfont = layout.font;
210 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
211 #ifndef INHERIT_LANGUAGE
212 tmpfont.realize(layoutfont);
214 tmpfont.realize(layoutfont, buf->params.language);
217 par->inInset()->getDrawFont(tmpfont);
219 return realizeFont(tmpfont, buf, par);
223 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
225 LyXLayout const & layout =
226 textclasslist[buf->params.textclass][par->layout()];
228 Paragraph::depth_type par_depth = par->getDepth();
231 return layout.resfont;
234 return realizeFont(layout.font, buf, par);
238 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
240 LyXLayout const & layout =
241 textclasslist[buf->params.textclass][par->layout()];
243 Paragraph::depth_type par_depth = par->getDepth();
246 return layout.reslabelfont;
249 return realizeFont(layout.labelfont, buf, par);
253 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
254 pos_type pos, LyXFont const & fnt,
257 Buffer const * buf = bv->buffer();
258 LyXFont font = getFont(buf, par, pos);
259 font.update(fnt, buf->params.language, toggleall);
260 // Let the insets convert their font
261 if (par->isInset(pos)) {
262 Inset * inset = par->getInset(pos);
263 if (isEditableInset(inset)) {
264 UpdatableInset * uinset =
265 static_cast<UpdatableInset *>(inset);
266 uinset->setFont(bv, fnt, toggleall, true);
270 // Plug thru to version below:
271 setCharFont(buf, par, pos, font);
275 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
276 pos_type pos, LyXFont const & fnt)
280 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
281 LyXLayout const & layout = tclass[par->layout()];
283 // Get concrete layout font to reduce against
286 if (pos < beginningOfMainBody(buf, par))
287 layoutfont = layout.labelfont;
289 layoutfont = layout.font;
291 // Realize against environment font information
292 if (par->getDepth()) {
293 Paragraph * tp = par;
294 while (!layoutfont.resolved() && tp && tp->getDepth()) {
295 tp = tp->outerHook();
297 #ifndef INHERIT_LANGUAGE
298 layoutfont.realize(tclass[tp->layout()].font);
300 layoutfont.realize(textclasslist.
301 Style(buf->params.textclass,
303 buf->params.language);
308 #ifndef INHERIT_LANGUAGE
309 layoutfont.realize(tclass.defaultfont());
311 layoutfont.realize(tclass.defaultfont(), buf->params.language);
314 // Now, reduce font against full layout font
315 font.reduce(layoutfont);
317 par->setFont(pos, font);
321 // inserts a new row behind the specified row, increments
322 // the touched counters
323 void LyXText::insertRow(Row * row, Paragraph * par,
326 Row * tmprow = new Row;
329 tmprow->next(firstrow);
332 tmprow->previous(row);
333 tmprow->next(row->next());
338 tmprow->next()->previous(tmprow);
340 if (tmprow->previous())
341 tmprow->previous()->next(tmprow);
353 // removes the row and reset the touched counters
354 void LyXText::removeRow(Row * row) const
356 Row * row_prev = row->previous();
358 row->next()->previous(row_prev);
360 firstrow = row->next();
361 // lyx::Assert(firstrow);
363 row_prev->next(row->next());
365 if (row == lastrow) {
366 lyx::Assert(!row->next());
369 if (refresh_row == row) {
370 refresh_row = row_prev ? row_prev : row->next();
371 // what about refresh_y, refresh_height
374 height -= row->height(); // the text becomes smaller
377 --number_of_rows; // one row less
381 // remove all following rows of the paragraph of the specified row.
382 void LyXText::removeParagraph(Row * row) const
384 Paragraph * tmppar = row->par();
388 while (row && row->par() == tmppar) {
389 tmprow = row->next();
396 // insert the specified paragraph behind the specified row
397 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
400 insertRow(row, par, 0); /* insert a new row, starting
403 setCounter(bview->buffer(), par); // set the counters
405 // and now append the whole paragraph behind the new row
408 appendParagraph(bview, firstrow);
410 row->next()->height(0);
411 appendParagraph(bview, row->next());
416 Inset * LyXText::getInset() const
419 if (cursor.pos() == 0 && cursor.par()->bibkey) {
420 inset = cursor.par()->bibkey;
421 } else if (cursor.pos() < cursor.par()->size()
422 && cursor.par()->isInset(cursor.pos())) {
423 inset = cursor.par()->getInset(cursor.pos());
429 void LyXText::toggleInset(BufferView * bview)
431 Inset * inset = getInset();
432 // is there an editable inset at cursor position?
433 if (!isEditableInset(inset)) {
434 // No, try to see if we are inside a collapsable inset
435 if (inset_owner && inset_owner->owner()
436 && inset_owner->owner()->isOpen()) {
437 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
438 inset_owner->owner()->close(bview);
442 //bview->owner()->message(inset->editMessage());
444 // do we want to keep this?? (JMarc)
445 if (!isHighlyEditableInset(inset))
446 setCursorParUndo(bview);
448 if (inset->isOpen()) {
454 inset->open(bview, !inset->isOpen());
459 /* used in setlayout */
460 // Asger is not sure we want to do this...
461 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
464 LyXLayout const & layout =
465 textclasslist[buf->params.textclass][par->layout()];
468 for (pos_type pos = 0; pos < par->size(); ++pos) {
469 if (pos < beginningOfMainBody(buf, par))
470 layoutfont = layout.labelfont;
472 layoutfont = layout.font;
474 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
475 tmpfont.reduce(layoutfont);
476 par->setFont(pos, tmpfont);
481 Paragraph * LyXText::setLayout(BufferView * bview,
482 LyXCursor & cur, LyXCursor & sstart_cur,
483 LyXCursor & send_cur,
484 string const & layout)
486 Paragraph * endpar = send_cur.par()->next();
487 Paragraph * undoendpar = endpar;
489 if (endpar && endpar->getDepth()) {
490 while (endpar && endpar->getDepth()) {
491 endpar = endpar->next();
495 endpar = endpar->next(); // because of parindents etc.
498 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
500 // ok we have a selection. This is always between sstart_cur
501 // and sel_end cursor
504 LyXLayout const & lyxlayout =
505 textclasslist[bview->buffer()->params.textclass][layout];
508 cur.par()->applyLayout(layout);
509 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
510 Paragraph * fppar = cur.par();
511 fppar->params().spaceTop(lyxlayout.fill_top ?
512 VSpace(VSpace::VFILL)
513 : VSpace(VSpace::NONE));
514 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
515 VSpace(VSpace::VFILL)
516 : VSpace(VSpace::NONE));
517 if (lyxlayout.margintype == MARGIN_MANUAL)
518 cur.par()->setLabelWidthString(lyxlayout.labelstring());
519 if (lyxlayout.labeltype != LABEL_BIBLIO
521 delete fppar->bibkey;
524 if (cur.par() != send_cur.par())
525 cur.par(cur.par()->next());
526 } while (cur.par() != send_cur.par());
532 // set layout over selection and make a total rebreak of those paragraphs
533 void LyXText::setLayout(BufferView * bview, string const & layout)
535 LyXCursor tmpcursor = cursor; /* store the current cursor */
537 // if there is no selection just set the layout
538 // of the current paragraph */
539 if (!selection.set()) {
540 selection.start = cursor; // dummy selection
541 selection.end = cursor;
543 Paragraph * endpar = setLayout(bview, cursor, selection.start,
544 selection.end, layout);
545 redoParagraphs(bview, selection.start, endpar);
547 // we have to reset the selection, because the
548 // geometry could have changed
549 setCursor(bview, selection.start.par(),
550 selection.start.pos(), false);
551 selection.cursor = cursor;
552 setCursor(bview, selection.end.par(), selection.end.pos(), false);
553 updateCounters(bview, cursor.row());
556 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
560 // increment depth over selection and
561 // make a total rebreak of those paragraphs
562 void LyXText::incDepth(BufferView * bview)
564 // If there is no selection, just use the current paragraph
565 if (!selection.set()) {
566 selection.start = cursor; // dummy selection
567 selection.end = cursor;
570 // We end at the next paragraph with depth 0
571 Paragraph * endpar = selection.end.par()->next();
573 Paragraph * undoendpar = endpar;
575 if (endpar && endpar->getDepth()) {
576 while (endpar && endpar->getDepth()) {
577 endpar = endpar->next();
581 endpar = endpar->next(); // because of parindents etc.
584 setUndo(bview, Undo::EDIT,
585 selection.start.par(), undoendpar);
587 LyXCursor tmpcursor = cursor; // store the current cursor
589 // ok we have a selection. This is always between sel_start_cursor
590 // and sel_end cursor
591 cursor = selection.start;
593 bool anything_changed = false;
595 LyXTextClass const & tclass =
596 textclasslist[bview->buffer()->params.textclass];
599 // NOTE: you can't change the depth of a bibliography entry
600 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
601 Paragraph * prev = cursor.par()->previous();
604 if (cursor.par()->getDepth()
605 < prev->getMaxDepthAfter(bview->buffer())){
606 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
607 anything_changed = true;
611 if (cursor.par() == selection.end.par())
613 cursor.par(cursor.par()->next());
616 // if nothing changed set all depth to 0
617 if (!anything_changed) {
618 cursor = selection.start;
619 while (cursor.par() != selection.end.par()) {
620 cursor.par()->params().depth(0);
621 cursor.par(cursor.par()->next());
623 cursor.par()->params().depth(0);
626 redoParagraphs(bview, selection.start, endpar);
628 // we have to reset the selection, because the
629 // geometry could have changed
630 setCursor(bview, selection.start.par(), selection.start.pos());
631 selection.cursor = cursor;
632 setCursor(bview, selection.end.par(), selection.end.pos());
633 updateCounters(bview, cursor.row());
636 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
640 // decrement depth over selection and
641 // make a total rebreak of those paragraphs
642 void LyXText::decDepth(BufferView * bview)
644 // if there is no selection just set the layout
645 // of the current paragraph
646 if (!selection.set()) {
647 selection.start = cursor; // dummy selection
648 selection.end = cursor;
650 Paragraph * endpar = selection.end.par()->next();
651 Paragraph * undoendpar = endpar;
653 if (endpar && endpar->getDepth()) {
654 while (endpar && endpar->getDepth()) {
655 endpar = endpar->next();
659 endpar = endpar->next(); // because of parindents etc.
662 setUndo(bview, Undo::EDIT,
663 selection.start.par(), undoendpar);
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
669 cursor = selection.start;
672 if (cursor.par()->params().depth()) {
673 cursor.par()->params()
674 .depth(cursor.par()->params().depth() - 1);
676 if (cursor.par() == selection.end.par()) {
679 cursor.par(cursor.par()->next());
682 redoParagraphs(bview, selection.start, endpar);
684 // we have to reset the selection, because the
685 // geometry could have changed
686 setCursor(bview, selection.start.par(),
687 selection.start.pos());
688 selection.cursor = cursor;
689 setCursor(bview, selection.end.par(), selection.end.pos());
690 updateCounters(bview, cursor.row());
693 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
697 // set font over selection and make a total rebreak of those paragraphs
698 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
700 // if there is no selection just set the current_font
701 if (!selection.set()) {
702 // Determine basis font
704 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
706 layoutfont = getLabelFont(bview->buffer(),
709 layoutfont = getLayoutFont(bview->buffer(),
712 // Update current font
713 real_current_font.update(font,
714 bview->buffer()->params.language,
717 // Reduce to implicit settings
718 current_font = real_current_font;
719 current_font.reduce(layoutfont);
720 // And resolve it completely
721 #ifndef INHERIT_LANGUAGE
722 real_current_font.realize(layoutfont);
724 real_current_font.realize(layoutfont,
725 bview->buffer()->params.language);
730 LyXCursor tmpcursor = cursor; // store the current cursor
732 // ok we have a selection. This is always between sel_start_cursor
733 // and sel_end cursor
735 setUndo(bview, Undo::EDIT,
736 selection.start.par(), selection.end.par()->next());
738 cursor = selection.start;
739 while (cursor.par() != selection.end.par() ||
740 (cursor.pos() < selection.end.pos()))
742 if (cursor.pos() < cursor.par()->size()) {
743 // an open footnote should behave
745 setCharFont(bview, cursor.par(), cursor.pos(),
747 cursor.pos(cursor.pos() + 1);
750 cursor.par(cursor.par()->next());
755 redoParagraphs(bview, selection.start, selection.end.par()->next());
757 // we have to reset the selection, because the
758 // geometry could have changed, but we keep
759 // it for user convenience
760 setCursor(bview, selection.start.par(), selection.start.pos());
761 selection.cursor = cursor;
762 setCursor(bview, selection.end.par(), selection.end.pos());
764 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
765 tmpcursor.boundary());
769 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
771 Row * tmprow = cur.row();
772 int y = cur.y() - tmprow->baseline();
774 setHeightOfRow(bview, tmprow);
776 while (tmprow->previous()
777 && tmprow->previous()->par() == tmprow->par()) {
778 tmprow = tmprow->previous();
779 y -= tmprow->height();
780 setHeightOfRow(bview, tmprow);
783 // we can set the refreshing parameters now
784 status(bview, LyXText::NEED_MORE_REFRESH);
786 refresh_row = tmprow;
787 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
791 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
793 Row * tmprow = cur.row();
795 int y = cur.y() - tmprow->baseline();
796 setHeightOfRow(bview, tmprow);
798 while (tmprow->previous()
799 && tmprow->previous()->par() == tmprow->par()) {
800 tmprow = tmprow->previous();
801 y -= tmprow->height();
804 // we can set the refreshing parameters now
805 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
807 refresh_row = tmprow;
809 status(bview, LyXText::NEED_MORE_REFRESH);
810 setCursor(bview, cur.par(), cur.pos());
814 // deletes and inserts again all paragaphs between the cursor
815 // and the specified par
816 // This function is needed after SetLayout and SetFont etc.
817 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
818 Paragraph const * endpar) const
821 Paragraph * tmppar = 0;
822 Paragraph * first_phys_par = 0;
824 Row * tmprow = cur.row();
826 int y = cur.y() - tmprow->baseline();
828 if (!tmprow->previous()) {
829 // a trick/hack for UNDO
830 // This is needed because in an UNDO/REDO we could have changed
831 // the ownerParagrah() so the paragraph inside the row is NOT
832 // my really first par anymore. Got it Lars ;) (Jug 20011206)
833 first_phys_par = ownerParagraph();
835 first_phys_par = tmprow->par();
836 while (tmprow->previous()
837 && tmprow->previous()->par() == first_phys_par)
839 tmprow = tmprow->previous();
840 y -= tmprow->height();
844 // we can set the refreshing parameters now
845 status(bview, LyXText::NEED_MORE_REFRESH);
847 refresh_row = tmprow->previous(); /* the real refresh row will
848 be deleted, so I store
852 tmppar = tmprow->next()->par();
855 while (tmprow->next() && tmppar != endpar) {
856 removeRow(tmprow->next());
857 if (tmprow->next()) {
858 tmppar = tmprow->next()->par();
864 // remove the first one
865 tmprow2 = tmprow; /* this is because tmprow->previous()
867 tmprow = tmprow->previous();
870 tmppar = first_phys_par;
874 insertParagraph(bview, tmppar, tmprow);
878 while (tmprow->next()
879 && tmprow->next()->par() == tmppar) {
880 tmprow = tmprow->next();
882 tmppar = tmppar->next();
884 } while (tmppar && tmppar != endpar);
886 // this is because of layout changes
888 refresh_y -= refresh_row->height();
889 setHeightOfRow(bview, refresh_row);
891 refresh_row = firstrow;
893 setHeightOfRow(bview, refresh_row);
896 if (tmprow && tmprow->next())
897 setHeightOfRow(bview, tmprow->next());
901 bool LyXText::fullRebreak(BufferView * bview)
907 if (need_break_row) {
908 breakAgain(bview, need_break_row);
916 // important for the screen
919 /* the cursor set functions have a special mechanism. When they
920 * realize, that you left an empty paragraph, they will delete it.
921 * They also delete the corresponding row */
923 // need the selection cursor:
924 void LyXText::setSelection(BufferView * bview)
926 bool const lsel = selection.set();
928 if (!selection.set()) {
929 last_sel_cursor = selection.cursor;
930 selection.start = selection.cursor;
931 selection.end = selection.cursor;
936 // first the toggling area
937 if (cursor.y() < last_sel_cursor.y()
938 || (cursor.y() == last_sel_cursor.y()
939 && cursor.x() < last_sel_cursor.x())) {
940 toggle_end_cursor = last_sel_cursor;
941 toggle_cursor = cursor;
943 toggle_end_cursor = cursor;
944 toggle_cursor = last_sel_cursor;
947 last_sel_cursor = cursor;
949 // and now the whole selection
951 if (selection.cursor.par() == cursor.par())
952 if (selection.cursor.pos() < cursor.pos()) {
953 selection.end = cursor;
954 selection.start = selection.cursor;
956 selection.end = selection.cursor;
957 selection.start = cursor;
959 else if (selection.cursor.y() < cursor.y() ||
960 (selection.cursor.y() == cursor.y()
961 && selection.cursor.x() < cursor.x())) {
962 selection.end = cursor;
963 selection.start = selection.cursor;
966 selection.end = selection.cursor;
967 selection.start = cursor;
970 // a selection with no contents is not a selection
971 if (selection.start.par() == selection.end.par() &&
972 selection.start.pos() == selection.end.pos())
973 selection.set(false);
975 if (inset_owner && (selection.set() || lsel))
976 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
980 string const LyXText::selectionAsString(Buffer const * buffer,
983 if (!selection.set()) return string();
986 // Special handling if the whole selection is within one paragraph
987 if (selection.start.par() == selection.end.par()) {
988 result += selection.start.par()->asString(buffer,
989 selection.start.pos(),
995 // The selection spans more than one paragraph
997 // First paragraph in selection
998 result += selection.start.par()->asString(buffer,
999 selection.start.pos(),
1000 selection.start.par()->size(),
1004 // The paragraphs in between (if any)
1005 LyXCursor tmpcur(selection.start);
1006 tmpcur.par(tmpcur.par()->next());
1007 while (tmpcur.par() != selection.end.par()) {
1008 result += tmpcur.par()->asString(buffer, 0,
1009 tmpcur.par()->size(),
1011 tmpcur.par(tmpcur.par()->next());
1014 // Last paragraph in selection
1015 result += selection.end.par()->asString(buffer, 0,
1016 selection.end.pos(), label);
1022 void LyXText::clearSelection() const
1024 selection.set(false);
1025 selection.mark(false);
1026 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1030 void LyXText::cursorHome(BufferView * bview) const
1032 setCursor(bview, cursor.par(), cursor.row()->pos());
1036 void LyXText::cursorEnd(BufferView * bview) const
1038 if (!cursor.row()->next()
1039 || cursor.row()->next()->par() != cursor.row()->par()) {
1040 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1042 if (cursor.par()->size() &&
1043 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1044 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1045 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1047 setCursor(bview,cursor.par(),
1048 rowLast(cursor.row()) + 1);
1054 void LyXText::cursorTop(BufferView * bview) const
1056 while (cursor.par()->previous())
1057 cursor.par(cursor.par()->previous());
1058 setCursor(bview, cursor.par(), 0);
1062 void LyXText::cursorBottom(BufferView * bview) const
1064 while (cursor.par()->next())
1065 cursor.par(cursor.par()->next());
1066 setCursor(bview, cursor.par(), cursor.par()->size());
1070 void LyXText::toggleFree(BufferView * bview,
1071 LyXFont const & font, bool toggleall)
1073 // If the mask is completely neutral, tell user
1074 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1075 // Could only happen with user style
1076 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1080 // Try implicit word selection
1081 // If there is a change in the language the implicit word selection
1083 LyXCursor resetCursor = cursor;
1084 bool implicitSelection = (font.language() == ignore_language
1085 && font.number() == LyXFont::IGNORE)
1086 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1089 setFont(bview, font, toggleall);
1091 // Implicit selections are cleared afterwards
1092 //and cursor is set to the original position.
1093 if (implicitSelection) {
1095 cursor = resetCursor;
1096 setCursor(bview, cursor.par(), cursor.pos());
1097 selection.cursor = cursor;
1100 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1104 string LyXText::getStringToIndex(BufferView * bview)
1108 // Try implicit word selection
1109 // If there is a change in the language the implicit word selection
1111 LyXCursor resetCursor = cursor;
1112 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1114 if (!selection.set()) {
1115 bview->owner()->message(_("Nothing to index!"));
1118 if (selection.start.par() != selection.end.par()) {
1119 bview->owner()->message(_("Cannot index more than one paragraph!"));
1123 idxstring = selectionAsString(bview->buffer(), false);
1125 // Implicit selections are cleared afterwards
1126 //and cursor is set to the original position.
1127 if (implicitSelection) {
1129 cursor = resetCursor;
1130 setCursor(bview, cursor.par(), cursor.pos());
1131 selection.cursor = cursor;
1137 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1138 Paragraph const * par) const
1140 if (textclasslist[buf->params.textclass][
1141 par->layout()].labeltype != LABEL_MANUAL)
1144 return par->beginningOfMainBody();
1148 /* the DTP switches for paragraphs. LyX will store them in the
1149 * first physicla paragraph. When a paragraph is broken, the top settings
1150 * rest, the bottom settings are given to the new one. So I can make shure,
1151 * they do not duplicate themself and you cannnot make dirty things with
1154 void LyXText::setParagraph(BufferView * bview,
1155 bool line_top, bool line_bottom,
1156 bool pagebreak_top, bool pagebreak_bottom,
1157 VSpace const & space_top,
1158 VSpace const & space_bottom,
1159 Spacing const & spacing,
1161 string labelwidthstring,
1164 LyXCursor tmpcursor = cursor;
1165 if (!selection.set()) {
1166 selection.start = cursor;
1167 selection.end = cursor;
1170 // make sure that the depth behind the selection are restored, too
1171 Paragraph * endpar = selection.end.par()->next();
1172 Paragraph * undoendpar = endpar;
1174 if (endpar && endpar->getDepth()) {
1175 while (endpar && endpar->getDepth()) {
1176 endpar = endpar->next();
1177 undoendpar = endpar;
1181 // because of parindents etc.
1182 endpar = endpar->next();
1185 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1188 Paragraph * tmppar = selection.end.par();
1189 LyXTextClass const & tclass =
1190 textclasslist[bview->buffer()->params.textclass];
1192 while (tmppar != selection.start.par()->previous()) {
1193 setCursor(bview, tmppar, 0);
1194 status(bview, LyXText::NEED_MORE_REFRESH);
1195 refresh_row = cursor.row();
1196 refresh_y = cursor.y() - cursor.row()->baseline();
1197 cursor.par()->params().lineTop(line_top);
1198 cursor.par()->params().lineBottom(line_bottom);
1199 cursor.par()->params().pagebreakTop(pagebreak_top);
1200 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1201 cursor.par()->params().spaceTop(space_top);
1202 cursor.par()->params().spaceBottom(space_bottom);
1203 cursor.par()->params().spacing(spacing);
1204 // does the layout allow the new alignment?
1205 LyXLayout const & layout = tclass[cursor.par()->layout()];
1207 if (align == LYX_ALIGN_LAYOUT)
1208 align = layout.align;
1209 if (align & layout.alignpossible) {
1210 if (align == layout.align)
1211 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1213 cursor.par()->params().align(align);
1215 cursor.par()->setLabelWidthString(labelwidthstring);
1216 cursor.par()->params().noindent(noindent);
1217 tmppar = cursor.par()->previous();
1220 redoParagraphs(bview, selection.start, endpar);
1223 setCursor(bview, selection.start.par(), selection.start.pos());
1224 selection.cursor = cursor;
1225 setCursor(bview, selection.end.par(), selection.end.pos());
1226 setSelection(bview);
1227 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1229 bview->updateInset(inset_owner, true);
1233 char loweralphaCounter(int n)
1235 if (n < 1 || n > 26)
1245 char alphaCounter(int n)
1247 if (n < 1 || n > 26)
1255 char hebrewCounter(int n)
1257 static const char hebrew[22] = {
1258 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1259 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1260 '÷', 'ø', 'ù', 'ú'
1262 if (n < 1 || n > 22)
1270 string const romanCounter(int n)
1272 static char const * roman[20] = {
1273 "i", "ii", "iii", "iv", "v",
1274 "vi", "vii", "viii", "ix", "x",
1275 "xi", "xii", "xiii", "xiv", "xv",
1276 "xvi", "xvii", "xviii", "xix", "xx"
1278 if (n < 1 || n > 20)
1287 // set the counter of a paragraph. This includes the labels
1288 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1290 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1291 LyXLayout const & layout = textclass[par->layout()];
1294 // copy the prev-counters to this one,
1295 // unless this is the first paragraph
1296 if (par->previous()) {
1297 for (int i = 0; i < 10; ++i) {
1298 par->setCounter(i, par->previous()->getFirstCounter(i));
1300 par->params().appendix(par->previous()->params().appendix());
1301 if (!par->params().appendix() && par->params().startOfAppendix()) {
1302 par->params().appendix(true);
1303 for (int i = 0; i < 10; ++i) {
1304 par->setCounter(i, 0);
1307 par->enumdepth = par->previous()->enumdepth;
1308 par->itemdepth = par->previous()->itemdepth;
1310 for (int i = 0; i < 10; ++i) {
1311 par->setCounter(i, 0);
1313 par->params().appendix(par->params().startOfAppendix());
1318 /* Maybe we have to increment the enumeration depth.
1319 * BUT, enumeration in a footnote is considered in isolation from its
1320 * surrounding paragraph so don't increment if this is the
1321 * first line of the footnote
1322 * AND, bibliographies can't have their depth changed ie. they
1323 * are always of depth 0
1326 && par->previous()->getDepth() < par->getDepth()
1327 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1328 && par->enumdepth < 3
1329 && layout.labeltype != LABEL_BIBLIO) {
1333 // Maybe we have to decrement the enumeration depth, see note above
1335 && par->previous()->getDepth() > par->getDepth()
1336 && layout.labeltype != LABEL_BIBLIO) {
1337 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1338 par->setCounter(6 + par->enumdepth,
1339 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1340 /* reset the counters.
1341 * A depth change is like a breaking layout
1343 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1344 par->setCounter(i, 0);
1347 if (!par->params().labelString().empty()) {
1348 par->params().labelString(string());
1351 if (layout.margintype == MARGIN_MANUAL) {
1352 if (par->params().labelWidthString().empty()) {
1353 par->setLabelWidthString(layout.labelstring());
1356 par->setLabelWidthString(string());
1359 // is it a layout that has an automatic label?
1360 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1362 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1363 if (i >= 0 && i<= buf->params.secnumdepth) {
1364 par->incCounter(i); // increment the counter
1366 // Is there a label? Useful for Chapter layout
1367 if (!par->params().appendix()) {
1368 if (!layout.labelstring().empty())
1369 par->params().labelString(layout.labelstring());
1371 par->params().labelString(string());
1373 if (!layout.labelstring_appendix().empty())
1374 par->params().labelString(layout.labelstring_appendix());
1376 par->params().labelString(string());
1381 if (!par->params().appendix()) {
1382 switch (2 * LABEL_COUNTER_CHAPTER -
1383 textclass.maxcounter() + i) {
1384 case LABEL_COUNTER_CHAPTER:
1385 s << par->getCounter(i);
1387 case LABEL_COUNTER_SECTION:
1388 s << par->getCounter(i - 1) << '.'
1389 << par->getCounter(i);
1391 case LABEL_COUNTER_SUBSECTION:
1392 s << par->getCounter(i - 2) << '.'
1393 << par->getCounter(i - 1) << '.'
1394 << par->getCounter(i);
1396 case LABEL_COUNTER_SUBSUBSECTION:
1397 s << par->getCounter(i - 3) << '.'
1398 << par->getCounter(i - 2) << '.'
1399 << par->getCounter(i - 1) << '.'
1400 << par->getCounter(i);
1403 case LABEL_COUNTER_PARAGRAPH:
1404 s << par->getCounter(i - 4) << '.'
1405 << par->getCounter(i - 3) << '.'
1406 << par->getCounter(i - 2) << '.'
1407 << par->getCounter(i - 1) << '.'
1408 << par->getCounter(i);
1410 case LABEL_COUNTER_SUBPARAGRAPH:
1411 s << par->getCounter(i - 5) << '.'
1412 << par->getCounter(i - 4) << '.'
1413 << par->getCounter(i - 3) << '.'
1414 << par->getCounter(i - 2) << '.'
1415 << par->getCounter(i - 1) << '.'
1416 << par->getCounter(i);
1420 // Can this ever be reached? And in the
1421 // case it is, how can this be correct?
1423 s << par->getCounter(i) << '.';
1426 } else { // appendix
1427 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1428 case LABEL_COUNTER_CHAPTER:
1429 if (par->isRightToLeftPar(buf->params))
1430 s << hebrewCounter(par->getCounter(i));
1432 s << alphaCounter(par->getCounter(i));
1434 case LABEL_COUNTER_SECTION:
1435 if (par->isRightToLeftPar(buf->params))
1436 s << hebrewCounter(par->getCounter(i - 1));
1438 s << alphaCounter(par->getCounter(i - 1));
1441 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBSECTION:
1445 if (par->isRightToLeftPar(buf->params))
1446 s << hebrewCounter(par->getCounter(i - 2));
1448 s << alphaCounter(par->getCounter(i - 2));
1451 << par->getCounter(i-1) << '.'
1452 << par->getCounter(i);
1455 case LABEL_COUNTER_SUBSUBSECTION:
1456 if (par->isRightToLeftPar(buf->params))
1457 s << hebrewCounter(par->getCounter(i-3));
1459 s << alphaCounter(par->getCounter(i-3));
1462 << par->getCounter(i-2) << '.'
1463 << par->getCounter(i-1) << '.'
1464 << par->getCounter(i);
1467 case LABEL_COUNTER_PARAGRAPH:
1468 if (par->isRightToLeftPar(buf->params))
1469 s << hebrewCounter(par->getCounter(i-4));
1471 s << alphaCounter(par->getCounter(i-4));
1474 << par->getCounter(i-3) << '.'
1475 << par->getCounter(i-2) << '.'
1476 << par->getCounter(i-1) << '.'
1477 << par->getCounter(i);
1480 case LABEL_COUNTER_SUBPARAGRAPH:
1481 if (par->isRightToLeftPar(buf->params))
1482 s << hebrewCounter(par->getCounter(i-5));
1484 s << alphaCounter(par->getCounter(i-5));
1487 << par->getCounter(i-4) << '.'
1488 << par->getCounter(i-3) << '.'
1489 << par->getCounter(i-2) << '.'
1490 << par->getCounter(i-1) << '.'
1491 << par->getCounter(i);
1495 // Can this ever be reached? And in the
1496 // case it is, how can this be correct?
1498 s << par->getCounter(i) << '.';
1504 par->params().labelString(par->params().labelString() +s.str().c_str());
1505 // We really want to remove the c_str as soon as
1508 for (i++; i < 10; ++i) {
1509 // reset the following counters
1510 par->setCounter(i, 0);
1512 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1513 for (i++; i < 10; ++i) {
1514 // reset the following counters
1515 par->setCounter(i, 0);
1517 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1518 par->incCounter(i + par->enumdepth);
1519 int number = par->getCounter(i + par->enumdepth);
1523 switch (par->enumdepth) {
1525 if (par->isRightToLeftPar(buf->params))
1527 << hebrewCounter(number)
1531 << loweralphaCounter(number)
1535 if (par->isRightToLeftPar(buf->params))
1536 s << '.' << romanCounter(number);
1538 s << romanCounter(number) << '.';
1541 if (par->isRightToLeftPar(buf->params))
1543 << alphaCounter(number);
1545 s << alphaCounter(number)
1549 if (par->isRightToLeftPar(buf->params))
1556 par->params().labelString(s.str().c_str());
1558 for (i += par->enumdepth + 1; i < 10; ++i) {
1559 // reset the following counters
1560 par->setCounter(i, 0);
1564 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1565 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1567 int number = par->getCounter(i);
1569 InsetCommandParams p("bibitem" );
1570 par->bibkey = new InsetBibKey(p);
1572 par->bibkey->setCounter(number);
1573 par->params().labelString(layout.labelstring());
1575 // In biblio should't be following counters but...
1577 string s = layout.labelstring();
1579 // the caption hack:
1580 if (layout.labeltype == LABEL_SENSITIVE) {
1581 bool isOK (par->inInset() && par->inInset()->owner() &&
1582 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1585 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1587 = floatList.getType(tmp->type());
1588 // We should get the correct number here too.
1589 s = fl.name() + " #:";
1591 /* par->SetLayout(0);
1592 s = layout->labelstring; */
1593 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1594 ? " :úåòîùî øñç" : "Senseless: ";
1597 par->params().labelString(s);
1599 /* reset the enumeration counter. They are always resetted
1600 * when there is any other layout between */
1601 for (int i = 6 + par->enumdepth; i < 10; ++i)
1602 par->setCounter(i, 0);
1607 // Updates all counters BEHIND the row. Changed paragraphs
1608 // with a dynamic left margin will be rebroken.
1609 void LyXText::updateCounters(BufferView * bview, Row * row) const
1617 par = row->par()->next();
1621 while (row->par() != par)
1624 setCounter(bview->buffer(), par);
1626 // now check for the headline layouts. remember that they
1627 // have a dynamic left margin
1628 LyXTextClass const & tclass =
1629 textclasslist[bview->buffer()->params.textclass];
1630 LyXLayout const & layout = tclass[par->layout()];
1632 if (layout.margintype == MARGIN_DYNAMIC
1633 || layout.labeltype == LABEL_SENSITIVE) {
1634 // Rebreak the paragraph
1635 removeParagraph(row);
1636 appendParagraph(bview, row);
1643 void LyXText::insertInset(BufferView * bview, Inset * inset)
1645 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1647 // I don't know if this is necessary here (Jug 20020102)
1648 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1649 cursor.par()->insertInset(cursor.pos(), inset);
1650 // Just to rebreak and refresh correctly.
1651 // The character will not be inserted a second time
1652 insertChar(bview, Paragraph::META_INSET);
1654 // If we enter a highly editable inset the cursor should be to before
1655 // the inset. This couldn't happen before as Undo was not handled inside
1656 // inset now after the Undo LyX tries to call inset->Edit(...) again
1657 // and cannot do this as the cursor is behind the inset and GetInset
1658 // does not return the inset!
1659 if (isHighlyEditableInset(inset)) {
1660 cursorLeft(bview, true);
1666 void LyXText::copyEnvironmentType()
1668 copylayouttype = cursor.par()->layout();
1672 void LyXText::pasteEnvironmentType(BufferView * bview)
1674 setLayout(bview, copylayouttype);
1678 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1680 // Stuff what we got on the clipboard. Even if there is no selection.
1682 // There is a problem with having the stuffing here in that the
1683 // larger the selection the slower LyX will get. This can be
1684 // solved by running the line below only when the selection has
1685 // finished. The solution used currently just works, to make it
1686 // faster we need to be more clever and probably also have more
1687 // calls to stuffClipboard. (Lgb)
1688 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1690 // This doesn't make sense, if there is no selection
1691 if (!selection.set())
1694 // OK, we have a selection. This is always between selection.start
1695 // and selection.end
1697 // make sure that the depth behind the selection are restored, too
1698 Paragraph * endpar = selection.end.par()->next();
1699 Paragraph * undoendpar = endpar;
1701 if (endpar && endpar->getDepth()) {
1702 while (endpar && endpar->getDepth()) {
1703 endpar = endpar->next();
1704 undoendpar = endpar;
1706 } else if (endpar) {
1707 endpar = endpar->next(); // because of parindents etc.
1710 setUndo(bview, Undo::DELETE,
1711 selection.start.par(), undoendpar);
1713 // there are two cases: cut only within one paragraph or
1714 // more than one paragraph
1715 if (selection.start.par() == selection.end.par()) {
1716 // only within one paragraph
1717 endpar = selection.end.par();
1718 int pos = selection.end.pos();
1719 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1720 selection.start.pos(), pos,
1721 bview->buffer()->params.textclass,
1723 selection.end.pos(pos);
1725 endpar = selection.end.par();
1726 int pos = selection.end.pos();
1727 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1728 selection.start.pos(), pos,
1729 bview->buffer()->params.textclass,
1732 selection.end.par(endpar);
1733 selection.end.pos(pos);
1734 cursor.pos(selection.end.pos());
1736 endpar = endpar->next();
1738 // sometimes necessary
1740 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1742 redoParagraphs(bview, selection.start, endpar);
1744 // cutSelection can invalidate the cursor so we need to set
1746 cursor = selection.start;
1748 // need a valid cursor. (Lgb)
1751 setCursor(bview, cursor.par(), cursor.pos());
1752 selection.cursor = cursor;
1753 updateCounters(bview, cursor.row());
1757 void LyXText::copySelection(BufferView * bview)
1759 // stuff the selection onto the X clipboard, from an explicit copy request
1760 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1762 // this doesnt make sense, if there is no selection
1763 if (!selection.set())
1766 // ok we have a selection. This is always between selection.start
1767 // and sel_end cursor
1769 // copy behind a space if there is one
1770 while (selection.start.par()->size() > selection.start.pos()
1771 && selection.start.par()->isLineSeparator(selection.start.pos())
1772 && (selection.start.par() != selection.end.par()
1773 || selection.start.pos() < selection.end.pos()))
1774 selection.start.pos(selection.start.pos() + 1);
1776 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1777 selection.start.pos(), selection.end.pos(),
1778 bview->buffer()->params.textclass);
1782 void LyXText::pasteSelection(BufferView * bview)
1784 // this does not make sense, if there is nothing to paste
1785 if (!CutAndPaste::checkPastePossible(cursor.par()))
1788 setUndo(bview, Undo::INSERT,
1789 cursor.par(), cursor.par()->next());
1792 Paragraph * actpar = cursor.par();
1793 int pos = cursor.pos();
1795 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1796 bview->buffer()->params.textclass);
1798 redoParagraphs(bview, cursor, endpar);
1800 setCursor(bview, cursor.par(), cursor.pos());
1803 selection.cursor = cursor;
1804 setCursor(bview, actpar, pos);
1805 setSelection(bview);
1806 updateCounters(bview, cursor.row());
1810 // sets the selection over the number of characters of string, no check!!
1811 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1816 selection.cursor = cursor;
1817 for (string::size_type i = 0; i < str.length(); ++i)
1819 setSelection(bview);
1823 // simple replacing. The font of the first selected character is used
1824 void LyXText::replaceSelectionWithString(BufferView * bview,
1827 setCursorParUndo(bview);
1830 if (!selection.set()) { // create a dummy selection
1831 selection.end = cursor;
1832 selection.start = cursor;
1835 // Get font setting before we cut
1836 pos_type pos = selection.end.pos();
1837 LyXFont const font = selection.start.par()
1838 ->getFontSettings(bview->buffer()->params,
1839 selection.start.pos());
1841 // Insert the new string
1842 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1843 selection.end.par()->insertChar(pos, (*cit), font);
1847 // Cut the selection
1848 cutSelection(bview, true, false);
1854 // needed to insert the selection
1855 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1857 Paragraph * par = cursor.par();
1858 pos_type pos = cursor.pos();
1859 Paragraph * endpar = cursor.par()->next();
1861 setCursorParUndo(bview);
1863 // only to be sure, should not be neccessary
1866 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1868 redoParagraphs(bview, cursor, endpar);
1869 setCursor(bview, cursor.par(), cursor.pos());
1870 selection.cursor = cursor;
1871 setCursor(bview, par, pos);
1872 setSelection(bview);
1876 // turns double-CR to single CR, others where converted into one
1877 // blank. Then InsertStringAsLines is called
1878 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1880 string linestr(str);
1881 bool newline_inserted = false;
1882 for (string::size_type i = 0; i < linestr.length(); ++i) {
1883 if (linestr[i] == '\n') {
1884 if (newline_inserted) {
1885 // we know that \r will be ignored by
1886 // InsertStringA. Of course, it is a dirty
1887 // trick, but it works...
1888 linestr[i - 1] = '\r';
1892 newline_inserted = true;
1894 } else if (IsPrintable(linestr[i])) {
1895 newline_inserted = false;
1898 insertStringAsLines(bview, linestr);
1902 bool LyXText::gotoNextInset(BufferView * bview,
1903 vector<Inset::Code> const & codes,
1904 string const & contents) const
1906 LyXCursor res = cursor;
1909 if (res.pos() < res.par()->size() - 1) {
1910 res.pos(res.pos() + 1);
1912 res.par(res.par()->next());
1916 } while (res.par() &&
1917 !(res.par()->isInset(res.pos())
1918 && (inset = res.par()->getInset(res.pos())) != 0
1919 && find(codes.begin(), codes.end(), inset->lyxCode())
1921 && (contents.empty() ||
1922 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1926 setCursor(bview, res.par(), res.pos(), false);
1933 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1936 LyXCursor tmpcursor;
1940 Row * row = getRow(par, pos, y);
1942 // is there a break one row above
1943 if (row->previous() && row->previous()->par() == row->par()) {
1944 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1945 if (z >= row->pos()) {
1946 // set the dimensions of the row above
1947 y -= row->previous()->height();
1949 refresh_row = row->previous();
1950 status(bview, LyXText::NEED_MORE_REFRESH);
1952 breakAgain(bview, row->previous());
1954 // set the cursor again. Otherwise
1955 // dangling pointers are possible
1956 setCursor(bview, cursor.par(), cursor.pos(),
1957 false, cursor.boundary());
1958 selection.cursor = cursor;
1963 int const tmpheight = row->height();
1964 pos_type const tmplast = rowLast(row);
1968 breakAgain(bview, row);
1969 if (row->height() == tmpheight && rowLast(row) == tmplast)
1970 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1972 status(bview, LyXText::NEED_MORE_REFRESH);
1974 // check the special right address boxes
1975 if (textclasslist[bview->buffer()->params.textclass][
1976 par->layout()].margintype
1977 == MARGIN_RIGHT_ADDRESS_BOX)
1985 redoDrawingOfParagraph(bview, tmpcursor);
1988 // set the cursor again. Otherwise dangling pointers are possible
1989 // also set the selection
1991 if (selection.set()) {
1993 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1994 false, selection.cursor.boundary());
1995 selection.cursor = cursor;
1996 setCursorIntern(bview, selection.start.par(),
1997 selection.start.pos(),
1998 false, selection.start.boundary());
1999 selection.start = cursor;
2000 setCursorIntern(bview, selection.end.par(),
2001 selection.end.pos(),
2002 false, selection.end.boundary());
2003 selection.end = cursor;
2004 setCursorIntern(bview, last_sel_cursor.par(),
2005 last_sel_cursor.pos(),
2006 false, last_sel_cursor.boundary());
2007 last_sel_cursor = cursor;
2010 setCursorIntern(bview, cursor.par(), cursor.pos(),
2011 false, cursor.boundary());
2015 // returns false if inset wasn't found
2016 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2018 // first check the current paragraph
2019 int pos = cursor.par()->getPositionOfInset(inset);
2021 checkParagraph(bview, cursor.par(), pos);
2025 // check every paragraph
2027 Paragraph * par = ownerParagraph();
2029 pos = par->getPositionOfInset(inset);
2031 checkParagraph(bview, par, pos);
2041 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2043 bool setfont, bool boundary) const
2045 LyXCursor old_cursor = cursor;
2046 setCursorIntern(bview, par, pos, setfont, boundary);
2047 return deleteEmptyParagraphMechanism(bview, old_cursor);
2051 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2052 pos_type pos, bool boundary) const
2059 cur.boundary(boundary);
2062 if (pos && par->getChar(pos) == Paragraph::META_INSET &&
2063 par->getInset(pos)) {
2064 Inset * ins = par->getInset(pos);
2065 if (ins->needFullRow() || ins->display()) {
2072 // get the cursor y position in text
2074 Row * row = getRow(par, pos, y);
2075 // y is now the beginning of the cursor row
2076 y += row->baseline();
2077 // y is now the cursor baseline
2080 // now get the cursors x position
2082 float fill_separator;
2084 float fill_label_hfill;
2085 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2087 pos_type cursor_vpos = 0;
2088 pos_type last = rowLastPrintable(row);
2090 if (pos > last + 1) {
2091 // This shouldn't happen.
2094 } else if (pos < row->pos()) {
2099 if (last < row->pos())
2100 cursor_vpos = row->pos();
2101 else if (pos > last && !boundary)
2102 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2103 ? row->pos() : last + 1;
2104 else if (pos > row->pos() &&
2105 (pos > last || boundary))
2106 /// Place cursor after char at (logical) position pos - 1
2107 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2108 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2110 /// Place cursor before char at (logical) position pos
2111 cursor_vpos = (bidi_level(pos) % 2 == 0)
2112 ? log2vis(pos) : log2vis(pos) + 1;
2114 pos_type main_body =
2115 beginningOfMainBody(bview->buffer(), row->par());
2116 if ((main_body > 0) &&
2117 ((main_body-1 > last) ||
2118 !row->par()->isLineSeparator(main_body-1)))
2121 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2122 pos = vis2log(vpos);
2123 if (main_body > 0 && pos == main_body - 1) {
2124 x += fill_label_hfill +
2125 lyxfont::width(textclasslist[
2126 bview->buffer()->params.textclass][
2127 row->par()->layout()]
2129 getLabelFont(bview->buffer(), row->par()));
2130 if (row->par()->isLineSeparator(main_body-1))
2131 x -= singleWidth(bview, row->par(),main_body-1);
2133 if (hfillExpansion(bview->buffer(), row, pos)) {
2134 x += singleWidth(bview, row->par(), pos);
2135 if (pos >= main_body)
2138 x += fill_label_hfill;
2139 } else if (row->par()->isSeparator(pos)) {
2140 x += singleWidth(bview, row->par(), pos);
2141 if (pos >= main_body)
2142 x += fill_separator;
2144 x += singleWidth(bview, row->par(), pos);
2153 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2154 pos_type pos, bool setfont, bool boundary) const
2156 InsetText * it = static_cast<InsetText *>(par->inInset());
2158 if (it != inset_owner) {
2159 lyxerr[Debug::INSETS] << "InsetText is " << it
2161 << "inset_owner is "
2162 << inset_owner << endl;
2163 #ifdef WITH_WARNINGS
2164 #warning I believe this code is wrong. (Lgb)
2165 #warning Jürgen, have a look at this. (Lgb)
2166 #warning Hmmm, I guess you are right but we
2167 #warning should verify when this is needed
2169 // Jürgen, would you like to have a look?
2170 // I guess we need to move the outer cursor
2171 // and open and lock the inset (bla bla bla)
2172 // stuff I don't know... so can you have a look?
2174 // I moved the lyxerr stuff in here so we can see if
2175 // this is actually really needed and where!
2177 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2182 setCursor(bview, cursor, par, pos, boundary);
2184 setCurrentFont(bview);
2188 void LyXText::setCurrentFont(BufferView * bview) const
2190 pos_type pos = cursor.pos();
2191 if (cursor.boundary() && pos > 0)
2195 if (pos == cursor.par()->size())
2197 else // potentional bug... BUG (Lgb)
2198 if (cursor.par()->isSeparator(pos)) {
2199 if (pos > cursor.row()->pos() &&
2200 bidi_level(pos) % 2 ==
2201 bidi_level(pos - 1) % 2)
2203 else if (pos + 1 < cursor.par()->size())
2209 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2210 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2212 if (cursor.pos() == cursor.par()->size() &&
2213 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2214 !cursor.boundary()) {
2215 Language const * lang =
2216 cursor.par()->getParLanguage(bview->buffer()->params);
2217 current_font.setLanguage(lang);
2218 current_font.setNumber(LyXFont::OFF);
2219 real_current_font.setLanguage(lang);
2220 real_current_font.setNumber(LyXFont::OFF);
2225 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2227 LyXCursor old_cursor = cursor;
2229 setCursorFromCoordinates(bview, cursor, x, y);
2230 setCurrentFont(bview);
2231 deleteEmptyParagraphMechanism(bview, old_cursor);
2235 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2238 // Get the row first.
2240 Row * row = getRowNearY(y);
2242 pos_type const column = getColumnNearX(bview, row, x, bound);
2243 cur.par(row->par());
2244 cur.pos(row->pos() + column);
2246 cur.y(y + row->baseline());
2248 cur.boundary(bound);
2252 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2254 if (cursor.pos() > 0) {
2255 bool boundary = cursor.boundary();
2256 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2257 if (!internal && !boundary &&
2258 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2259 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2260 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2261 Paragraph * par = cursor.par()->previous();
2262 setCursor(bview, par, par->size());
2267 void LyXText::cursorRight(BufferView * bview, bool internal) const
2269 if (!internal && cursor.boundary() &&
2270 !cursor.par()->isNewline(cursor.pos()))
2271 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2272 else if (cursor.pos() < cursor.par()->size()) {
2273 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2275 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2276 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2277 } else if (cursor.par()->next())
2278 setCursor(bview, cursor.par()->next(), 0);
2282 void LyXText::cursorUp(BufferView * bview) const
2284 setCursorFromCoordinates(bview, cursor.x_fix(),
2285 cursor.y() - cursor.row()->baseline() - 1);
2289 void LyXText::cursorDown(BufferView * bview) const
2291 setCursorFromCoordinates(bview, cursor.x_fix(),
2292 cursor.y() - cursor.row()->baseline()
2293 + cursor.row()->height() + 1);
2297 void LyXText::cursorUpParagraph(BufferView * bview) const
2299 if (cursor.pos() > 0) {
2300 setCursor(bview, cursor.par(), 0);
2302 else if (cursor.par()->previous()) {
2303 setCursor(bview, cursor.par()->previous(), 0);
2308 void LyXText::cursorDownParagraph(BufferView * bview) const
2310 if (cursor.par()->next()) {
2311 setCursor(bview, cursor.par()->next(), 0);
2313 setCursor(bview, cursor.par(), cursor.par()->size());
2317 // fix the cursor `cur' after a characters has been deleted at `where'
2318 // position. Called by deleteEmptyParagraphMechanism
2319 void LyXText::fixCursorAfterDelete(BufferView * bview,
2321 LyXCursor const & where) const
2323 // if cursor is not in the paragraph where the delete occured,
2325 if (cur.par() != where.par())
2328 // if cursor position is after the place where the delete occured,
2330 if (cur.pos() > where.pos())
2331 cur.pos(cur.pos()-1);
2333 // recompute row et al. for this cursor
2334 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2338 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2339 LyXCursor const & old_cursor) const
2341 // Would be wrong to delete anything if we have a selection.
2342 if (selection.set())
2345 // We allow all kinds of "mumbo-jumbo" when freespacing.
2346 if (textclasslist[bview->buffer()->params.textclass][
2347 old_cursor.par()->layout()].free_spacing
2348 || old_cursor.par()->isFreeSpacing())
2353 /* Ok I'll put some comments here about what is missing.
2354 I have fixed BackSpace (and thus Delete) to not delete
2355 double-spaces automagically. I have also changed Cut,
2356 Copy and Paste to hopefully do some sensible things.
2357 There are still some small problems that can lead to
2358 double spaces stored in the document file or space at
2359 the beginning of paragraphs. This happens if you have
2360 the cursor betwenn to spaces and then save. Or if you
2361 cut and paste and the selection have a space at the
2362 beginning and then save right after the paste. I am
2363 sure none of these are very hard to fix, but I will
2364 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2365 that I can get some feedback. (Lgb)
2368 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2369 // delete the LineSeparator.
2372 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2373 // delete the LineSeparator.
2376 // If the pos around the old_cursor were spaces, delete one of them.
2377 if (old_cursor.par() != cursor.par()
2378 || old_cursor.pos() != cursor.pos()) {
2379 // Only if the cursor has really moved
2381 if (old_cursor.pos() > 0
2382 && old_cursor.pos() < old_cursor.par()->size()
2383 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2384 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2385 old_cursor.par()->erase(old_cursor.pos() - 1);
2386 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2388 #ifdef WITH_WARNINGS
2389 #warning This will not work anymore when we have multiple views of the same buffer
2390 // In this case, we will have to correct also the cursors held by
2391 // other bufferviews. It will probably be easier to do that in a more
2392 // automated way in LyXCursor code. (JMarc 26/09/2001)
2394 // correct all cursors held by the LyXText
2395 fixCursorAfterDelete(bview, cursor, old_cursor);
2396 fixCursorAfterDelete(bview, selection.cursor,
2398 fixCursorAfterDelete(bview, selection.start,
2400 fixCursorAfterDelete(bview, selection.end, old_cursor);
2401 fixCursorAfterDelete(bview, last_sel_cursor,
2403 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2404 fixCursorAfterDelete(bview, toggle_end_cursor,
2410 // don't delete anything if this is the ONLY paragraph!
2411 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2414 // Do not delete empty paragraphs with keepempty set.
2416 [bview->buffer()->params.textclass]
2417 [old_cursor.par()->layout()].keepempty)
2420 // only do our magic if we changed paragraph
2421 if (old_cursor.par() == cursor.par())
2424 // record if we have deleted a paragraph
2425 // we can't possibly have deleted a paragraph before this point
2426 bool deleted = false;
2428 if ((old_cursor.par()->size() == 0
2429 || (old_cursor.par()->size() == 1
2430 && old_cursor.par()->isLineSeparator(0)))) {
2431 // ok, we will delete anything
2432 LyXCursor tmpcursor;
2434 // make sure that you do not delete any environments
2435 status(bview, LyXText::NEED_MORE_REFRESH);
2438 if (old_cursor.row()->previous()) {
2439 refresh_row = old_cursor.row()->previous();
2440 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2442 cursor = old_cursor; // that undo can restore the right cursor position
2443 Paragraph * endpar = old_cursor.par()->next();
2444 if (endpar && endpar->getDepth()) {
2445 while (endpar && endpar->getDepth()) {
2446 endpar = endpar->next();
2449 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2453 removeRow(old_cursor.row());
2454 if (ownerParagraph() == old_cursor.par()) {
2455 ownerParagraph(ownerParagraph()->next());
2458 delete old_cursor.par();
2460 /* Breakagain the next par. Needed because of
2461 * the parindent that can occur or dissappear.
2462 * The next row can change its height, if
2463 * there is another layout before */
2464 if (refresh_row->next()) {
2465 breakAgain(bview, refresh_row->next());
2466 updateCounters(bview, refresh_row);
2468 setHeightOfRow(bview, refresh_row);
2470 refresh_row = old_cursor.row()->next();
2471 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2474 cursor = old_cursor; // that undo can restore the right cursor position
2475 Paragraph * endpar = old_cursor.par()->next();
2476 if (endpar && endpar->getDepth()) {
2477 while (endpar && endpar->getDepth()) {
2478 endpar = endpar->next();
2481 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2485 removeRow(old_cursor.row());
2487 if (ownerParagraph() == old_cursor.par()) {
2488 ownerParagraph(ownerParagraph()->next());
2491 delete old_cursor.par();
2493 /* Breakagain the next par. Needed because of
2494 the parindent that can occur or dissappear.
2495 The next row can change its height, if
2496 there is another layout before */
2498 breakAgain(bview, refresh_row);
2499 updateCounters(bview, refresh_row->previous());
2504 setCursorIntern(bview, cursor.par(), cursor.pos());
2506 if (selection.cursor.par() == old_cursor.par()
2507 && selection.cursor.pos() == old_cursor.pos()) {
2508 // correct selection
2509 selection.cursor = cursor;
2513 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2514 redoParagraphs(bview, old_cursor,
2515 old_cursor.par()->next());
2517 setCursorIntern(bview, cursor.par(), cursor.pos());
2518 selection.cursor = cursor;
2525 void LyXText::toggleAppendix(BufferView * bview)
2527 Paragraph * par = cursor.par();
2528 bool start = !par->params().startOfAppendix();
2530 // ensure that we have only one start_of_appendix in this document
2531 Paragraph * tmp = ownerParagraph();
2532 for (; tmp; tmp = tmp->next()) {
2533 tmp->params().startOfAppendix(false);
2536 par->params().startOfAppendix(start);
2538 // we can set the refreshing parameters now
2539 status(bview, LyXText::NEED_MORE_REFRESH);
2541 refresh_row = 0; // not needed for full update
2542 updateCounters(bview, 0);
2543 setCursor(bview, cursor.par(), cursor.pos());
2547 Paragraph * LyXText::ownerParagraph() const
2550 return inset_owner->paragraph();
2552 return bv_owner->buffer()->paragraph;
2556 void LyXText::ownerParagraph(Paragraph * p) const
2559 inset_owner->paragraph(p);
2561 bv_owner->buffer()->paragraph = p;
2566 void LyXText::ownerParagraph(int id, Paragraph * p) const
2568 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2569 if (op && op->inInset()) {
2570 static_cast<InsetText *>(op->inInset())->paragraph(p);
2577 LyXText::text_status LyXText::status() const
2583 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2585 // well as much as I know && binds more then || so the above and the
2586 // below are identical (this for your known use of parentesis!)
2587 // Now some explanation:
2588 // We should only go up with refreshing code so this means that if
2589 // we have a MORE refresh we should never set it to LITTLE if we still
2590 // didn't handle it (and then it will be UNCHANGED. Now as long as
2591 // we stay inside one LyXText this may work but we need to tell the
2592 // outermost LyXText that it should REALLY draw us if there is some
2593 // change in a Inset::LyXText. So you see that when we are inside a
2594 // inset's LyXText we give the LITTLE to the outermost LyXText to
2595 // tell'em that it should redraw the actual row (where the inset
2596 // resides! Capito?!
2598 if ((status_ != NEED_MORE_REFRESH)
2599 || (status_ == NEED_MORE_REFRESH
2600 && st != NEED_VERY_LITTLE_REFRESH))
2603 if (inset_owner && st != UNCHANGED) {
2604 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2605 if (!bview->text->refresh_row) {
2606 bview->text->refresh_row = bview->text->cursor.row();
2607 bview->text->refresh_y = bview->text->cursor.y() -
2608 bview->text->cursor.row()->baseline();