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 "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
42 #include "FloatList.h"
44 #include "ParagraphParameters.h"
45 #include "support/LAssert.h"
55 LyXText::LyXText(BufferView * bv)
56 : number_of_rows(0), height(0), width(0), first(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
64 LyXText::LyXText(InsetText * inset)
65 : number_of_rows(0), height(0), width(0), first(0),
66 bv_owner(0), inset_owner(inset), the_locking_inset(0),
67 need_break_row(0), refresh_y(0), refresh_row(0),
68 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
72 void LyXText::init(BufferView * bview, bool reinit)
75 // Delete all rows, this does not touch the paragraphs!
76 Row * tmprow = firstrow;
78 tmprow = firstrow->next();
82 lastrow = refresh_row = need_break_row = 0;
83 width = height = copylayouttype = 0;
84 number_of_rows = first = refresh_y = 0;
85 status_ = LyXText::UNCHANGED;
89 Paragraph * par = ownerParagraph();
90 current_font = getFont(bview->buffer(), par, 0);
92 insertParagraph(bview, par, lastrow);
95 setCursorIntern(bview, firstrow->par(), 0);
96 selection.cursor = cursor;
102 // Delete all rows, this does not touch the paragraphs!
103 Row * tmprow = firstrow;
105 tmprow = firstrow->next();
114 LyXFont const realizeFont(LyXFont const & font,
118 LyXFont tmpfont(font);
119 Paragraph::depth_type par_depth = par->getDepth();
121 // Resolve against environment font information
122 while (par && par_depth && !tmpfont.resolved()) {
123 par = par->outerHook();
125 #ifndef INHERIT_LANGUAGE
126 tmpfont.realize(textclasslist.
127 Style(buf->params.textclass,
128 par->getLayout()).font);
130 tmpfont.realize(textclasslist.
131 Style(buf->params.textclass,
132 par->getLayout()).font,
133 buf->params.language);
135 par_depth = par->getDepth();
139 #ifndef INHERIT_LANGUAGE
140 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
143 buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout const & layout =
165 textclasslist.Style(buf->params.textclass, par->getLayout());
167 Paragraph::depth_type par_depth = par->getDepth();
168 // We specialize the 95% common case:
170 if (layout.labeltype == LABEL_MANUAL
171 && pos < beginningOfMainBody(buf, par)) {
173 LyXFont f = par->getFontSettings(buf->params,
175 #ifndef INHERIT_LANGUAGE
176 return f.realize(layout.reslabelfont);
178 return f.realize(layout.reslabelfont, buf->params.language);
181 LyXFont f = par->getFontSettings(buf->params, pos);
182 #ifndef INHERIT_LANGUAGE
183 return f.realize(layout.resfont);
185 return f.realize(layout.resfont, buf->params.language);
190 // The uncommon case need not be optimized as much
194 if (pos < beginningOfMainBody(buf, par)) {
196 layoutfont = layout.labelfont;
199 layoutfont = layout.font;
202 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
203 #ifndef INHERIT_LANGUAGE
204 tmpfont.realize(layoutfont);
206 tmpfont.realize(layoutfont, buf->params.language);
209 return realizeFont(tmpfont, buf, par);
213 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
215 LyXLayout const & layout =
216 textclasslist.Style(buf->params.textclass, par->getLayout());
218 Paragraph::depth_type par_depth = par->getDepth();
221 return layout.resfont;
224 return realizeFont(layout.font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout const & layout =
231 textclasslist.Style(buf->params.textclass, par->getLayout());
233 Paragraph::depth_type par_depth = par->getDepth();
236 return layout.reslabelfont;
239 return realizeFont(layout.labelfont, buf, par);
243 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
244 pos_type pos, LyXFont const & fnt,
247 Buffer const * buf = bv->buffer();
248 LyXFont font = getFont(buf, par, pos);
249 font.update(fnt, buf->params.language, toggleall);
250 // Let the insets convert their font
251 if (par->isInset(pos)) {
252 Inset * inset = par->getInset(pos);
253 if (isEditableInset(inset)) {
254 UpdatableInset * uinset =
255 static_cast<UpdatableInset *>(inset);
256 uinset->setFont(bv, fnt, toggleall, true);
260 LyXLayout const & layout =
261 textclasslist.Style(buf->params.textclass,
264 // Get concrete layout font to reduce against
267 if (pos < beginningOfMainBody(buf, par))
268 layoutfont = layout.labelfont;
270 layoutfont = layout.font;
272 // Realize against environment font information
273 if (par->getDepth()){
274 Paragraph * tp = par;
275 while (!layoutfont.resolved() && tp && tp->getDepth()) {
276 tp = tp->outerHook();
278 #ifndef INHERIT_LANGUAGE
279 layoutfont.realize(textclasslist.
280 Style(buf->params.textclass,
281 tp->getLayout()).font);
283 layoutfont.realize(textclasslist.
284 Style(buf->params.textclass,
285 tp->getLayout()).font,
286 buf->params.language);
291 #ifndef INHERIT_LANGUAGE
292 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
295 buf->params.language);
298 // Now, reduce font against full layout font
299 font.reduce(layoutfont);
301 par->setFont(pos, font);
305 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
306 pos_type pos, LyXFont const & fnt)
310 LyXLayout const & layout =
311 textclasslist.Style(buf->params.textclass,
314 // Get concrete layout font to reduce against
317 if (pos < beginningOfMainBody(buf, par))
318 layoutfont = layout.labelfont;
320 layoutfont = layout.font;
322 // Realize against environment font information
323 if (par->getDepth()){
324 Paragraph * tp = par;
325 while (!layoutfont.resolved() && tp && tp->getDepth()) {
326 tp = tp->outerHook();
328 #ifndef INHERIT_LANGUAGE
329 layoutfont.realize(textclasslist.
330 Style(buf->params.textclass,
331 tp->getLayout()).font);
333 layoutfont.realize(textclasslist.
334 Style(buf->params.textclass,
335 tp->getLayout()).font,
336 buf->params.language);
341 #ifndef INHERIT_LANGUAGE
342 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
345 buf->params.language);
348 // Now, reduce font against full layout font
349 font.reduce(layoutfont);
351 par->setFont(pos, font);
355 // inserts a new row behind the specified row, increments
356 // the touched counters
357 void LyXText::insertRow(Row * row, Paragraph * par,
360 Row * tmprow = new Row;
363 tmprow->next(firstrow);
366 tmprow->previous(row);
367 tmprow->next(row->next());
372 tmprow->next()->previous(tmprow);
374 if (tmprow->previous())
375 tmprow->previous()->next(tmprow);
387 // removes the row and reset the touched counters
388 void LyXText::removeRow(Row * row) const
391 row->next()->previous(row->previous());
392 if (!row->previous()) {
393 firstrow = row->next();
394 // lyx::Assert(firstrow);
396 row->previous()->next(row->next());
399 lastrow = row->previous();
401 height -= row->height(); // the text becomes smaller
404 --number_of_rows; // one row less
408 // remove all following rows of the paragraph of the specified row.
409 void LyXText::removeParagraph(Row * row) const
411 Paragraph * tmppar = row->par();
415 while (row && row->par() == tmppar) {
416 tmprow = row->next();
423 // insert the specified paragraph behind the specified row
424 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
427 insertRow(row, par, 0); /* insert a new row, starting
430 setCounter(bview->buffer(), par); // set the counters
432 // and now append the whole paragraph behind the new row
435 appendParagraph(bview, firstrow);
437 row->next()->height(0);
438 appendParagraph(bview, row->next());
443 Inset * LyXText::getInset() const
446 if (cursor.pos() == 0 && cursor.par()->bibkey) {
447 inset = cursor.par()->bibkey;
448 } else if (cursor.pos() < cursor.par()->size()
449 && cursor.par()->isInset(cursor.pos())) {
450 inset = cursor.par()->getInset(cursor.pos());
456 void LyXText::toggleInset(BufferView * bview)
458 Inset * inset = getInset();
459 if (!isEditableInset(inset))
461 //bview->owner()->message(inset->editMessage());
463 // do we want to keep this?? (JMarc)
464 if (!isHighlyEditableInset(inset))
465 setCursorParUndo(bview);
467 if (inset->isOpen()) {
473 inset->open(bview, !inset->isOpen());
478 /* used in setlayout */
479 // Asger is not sure we want to do this...
480 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
483 LyXLayout const & layout =
484 textclasslist.Style(buf->params.textclass, par->getLayout());
487 for (pos_type pos = 0; pos < par->size(); ++pos) {
488 if (pos < beginningOfMainBody(buf, par))
489 layoutfont = layout.labelfont;
491 layoutfont = layout.font;
493 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
494 tmpfont.reduce(layoutfont);
495 par->setFont(pos, tmpfont);
500 Paragraph * LyXText::setLayout(BufferView * bview,
501 LyXCursor & cur, LyXCursor & sstart_cur,
502 LyXCursor & send_cur,
503 lyx::layout_type layout)
505 Paragraph * endpar = send_cur.par()->next();
506 Paragraph * undoendpar = endpar;
508 if (endpar && endpar->getDepth()) {
509 while (endpar && endpar->getDepth()) {
510 endpar = endpar->next();
514 endpar = endpar->next(); // because of parindents etc.
517 setUndo(bview, Undo::EDIT,
518 sstart_cur.par(), undoendpar);
520 // ok we have a selection. This is always between sstart_cur
521 // and sel_end cursor
524 LyXLayout const & lyxlayout =
525 textclasslist.Style(bview->buffer()->params.textclass, layout);
527 while (cur.par() != send_cur.par()) {
528 cur.par()->setLayout(layout);
529 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
530 Paragraph * fppar = cur.par();
531 fppar->params().spaceTop(lyxlayout.fill_top ?
532 VSpace(VSpace::VFILL)
533 : VSpace(VSpace::NONE));
534 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
535 VSpace(VSpace::VFILL)
536 : VSpace(VSpace::NONE));
537 if (lyxlayout.margintype == MARGIN_MANUAL)
538 cur.par()->setLabelWidthString(lyxlayout.labelstring());
539 if (lyxlayout.labeltype != LABEL_BIBLIO
541 delete fppar->bibkey;
544 cur.par(cur.par()->next());
546 cur.par()->setLayout(layout);
547 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
548 Paragraph * fppar = cur.par();
549 fppar->params().spaceTop(lyxlayout.fill_top ?
550 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
551 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
552 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
553 if (lyxlayout.margintype == MARGIN_MANUAL)
554 cur.par()->setLabelWidthString(lyxlayout.labelstring());
555 if (lyxlayout.labeltype != LABEL_BIBLIO
557 delete fppar->bibkey;
564 // set layout over selection and make a total rebreak of those paragraphs
565 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
567 LyXCursor tmpcursor = cursor; /* store the current cursor */
569 // if there is no selection just set the layout
570 // of the current paragraph */
571 if (!selection.set()) {
572 selection.start = cursor; // dummy selection
573 selection.end = cursor;
575 Paragraph * endpar = setLayout(bview, cursor, selection.start,
576 selection.end, layout);
577 redoParagraphs(bview, selection.start, endpar);
579 // we have to reset the selection, because the
580 // geometry could have changed
581 setCursor(bview, selection.start.par(),
582 selection.start.pos(), false);
583 selection.cursor = cursor;
584 setCursor(bview, selection.end.par(), selection.end.pos(), false);
585 updateCounters(bview, cursor.row());
588 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
592 // increment depth over selection and
593 // make a total rebreak of those paragraphs
594 void LyXText::incDepth(BufferView * bview)
596 // If there is no selection, just use the current paragraph
597 if (!selection.set()) {
598 selection.start = cursor; // dummy selection
599 selection.end = cursor;
602 // We end at the next paragraph with depth 0
603 Paragraph * endpar = selection.end.par()->next();
605 Paragraph * undoendpar = endpar;
607 if (endpar && endpar->getDepth()) {
608 while (endpar && endpar->getDepth()) {
609 endpar = endpar->next();
613 endpar = endpar->next(); // because of parindents etc.
616 setUndo(bview, Undo::EDIT,
617 selection.start.par(), undoendpar);
619 LyXCursor tmpcursor = cursor; // store the current cursor
621 // ok we have a selection. This is always between sel_start_cursor
622 // and sel_end cursor
623 cursor = selection.start;
625 bool anything_changed = false;
628 // NOTE: you can't change the depth of a bibliography entry
630 textclasslist.Style(bview->buffer()->params.textclass,
631 cursor.par()->getLayout()
632 ).labeltype != LABEL_BIBLIO) {
633 Paragraph * prev = cursor.par()->previous();
636 && (prev->getDepth() - cursor.par()->getDepth() > 0
637 || (prev->getDepth() == cursor.par()->getDepth()
638 && textclasslist.Style(bview->buffer()->params.textclass,
639 prev->getLayout()).isEnvironment()))) {
640 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
641 anything_changed = true;
644 if (cursor.par() == selection.end.par())
646 cursor.par(cursor.par()->next());
649 // if nothing changed set all depth to 0
650 if (!anything_changed) {
651 cursor = selection.start;
652 while (cursor.par() != selection.end.par()) {
653 cursor.par()->params().depth(0);
654 cursor.par(cursor.par()->next());
656 cursor.par()->params().depth(0);
659 redoParagraphs(bview, selection.start, endpar);
661 // we have to reset the selection, because the
662 // geometry could have changed
663 setCursor(bview, selection.start.par(), selection.start.pos());
664 selection.cursor = cursor;
665 setCursor(bview, selection.end.par(), selection.end.pos());
666 updateCounters(bview, cursor.row());
669 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
673 // decrement depth over selection and
674 // make a total rebreak of those paragraphs
675 void LyXText::decDepth(BufferView * bview)
677 // if there is no selection just set the layout
678 // of the current paragraph
679 if (!selection.set()) {
680 selection.start = cursor; // dummy selection
681 selection.end = cursor;
683 Paragraph * endpar = selection.end.par()->next();
684 Paragraph * undoendpar = endpar;
686 if (endpar && endpar->getDepth()) {
687 while (endpar && endpar->getDepth()) {
688 endpar = endpar->next();
692 endpar = endpar->next(); // because of parindents etc.
695 setUndo(bview, Undo::EDIT,
696 selection.start.par(), undoendpar);
698 LyXCursor tmpcursor = cursor; // store the current cursor
700 // ok we have a selection. This is always between sel_start_cursor
701 // and sel_end cursor
702 cursor = selection.start;
705 if (cursor.par()->params().depth()) {
706 cursor.par()->params()
707 .depth(cursor.par()->params().depth() - 1);
709 if (cursor.par() == selection.end.par()) {
712 cursor.par(cursor.par()->next());
715 redoParagraphs(bview, selection.start, endpar);
717 // we have to reset the selection, because the
718 // geometry could have changed
719 setCursor(bview, selection.start.par(),
720 selection.start.pos());
721 selection.cursor = cursor;
722 setCursor(bview, selection.end.par(), selection.end.pos());
723 updateCounters(bview, cursor.row());
726 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
730 // set font over selection and make a total rebreak of those paragraphs
731 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
733 // if there is no selection just set the current_font
734 if (!selection.set()) {
735 // Determine basis font
737 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
739 layoutfont = getLabelFont(bview->buffer(),
742 layoutfont = getLayoutFont(bview->buffer(),
745 // Update current font
746 real_current_font.update(font,
747 bview->buffer()->params.language,
750 // Reduce to implicit settings
751 current_font = real_current_font;
752 current_font.reduce(layoutfont);
753 // And resolve it completely
754 #ifndef INHERIT_LANGUAGE
755 real_current_font.realize(layoutfont);
757 real_current_font.realize(layoutfont,
758 bview->buffer()->params.language);
763 LyXCursor tmpcursor = cursor; // store the current cursor
765 // ok we have a selection. This is always between sel_start_cursor
766 // and sel_end cursor
768 setUndo(bview, Undo::EDIT,
769 selection.start.par(), selection.end.par()->next());
771 cursor = selection.start;
772 while (cursor.par() != selection.end.par() ||
773 (cursor.pos() < selection.end.pos()))
775 if (cursor.pos() < cursor.par()->size()) {
776 // an open footnote should behave
778 setCharFont(bview, cursor.par(), cursor.pos(),
780 cursor.pos(cursor.pos() + 1);
783 cursor.par(cursor.par()->next());
788 redoParagraphs(bview, selection.start, selection.end.par()->next());
790 // we have to reset the selection, because the
791 // geometry could have changed
792 setCursor(bview, selection.start.par(), selection.start.pos());
793 selection.cursor = cursor;
794 setCursor(bview, selection.end.par(), selection.end.pos());
797 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
798 tmpcursor.boundary());
802 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
804 Row * tmprow = cur.row();
805 int y = cur.y() - tmprow->baseline();
807 setHeightOfRow(bview, tmprow);
809 while (tmprow->previous()
810 && tmprow->previous()->par() == tmprow->par()) {
811 tmprow = tmprow->previous();
812 y -= tmprow->height();
813 setHeightOfRow(bview, tmprow);
816 // we can set the refreshing parameters now
817 status(bview, LyXText::NEED_MORE_REFRESH);
819 refresh_row = tmprow;
820 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
824 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
826 Row * tmprow = cur.row();
828 int y = cur.y() - tmprow->baseline();
829 setHeightOfRow(bview, tmprow);
831 while (tmprow->previous()
832 && tmprow->previous()->par() == tmprow->par()) {
833 tmprow = tmprow->previous();
834 y -= tmprow->height();
837 // we can set the refreshing parameters now
838 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
840 refresh_row = tmprow;
842 status(bview, LyXText::NEED_MORE_REFRESH);
843 setCursor(bview, cur.par(), cur.pos());
847 // deletes and inserts again all paragaphs between the cursor
848 // and the specified par
849 // This function is needed after SetLayout and SetFont etc.
850 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
851 Paragraph const * endpar) const
854 Paragraph * tmppar = 0;
855 Paragraph * first_phys_par = 0;
857 Row * tmprow = cur.row();
859 int y = cur.y() - tmprow->baseline();
861 if (!tmprow->previous()) {
862 // a trick/hack for UNDO
863 // Can somebody please tell me _why_ this solves
865 first_phys_par = firstParagraph();
867 first_phys_par = tmprow->par();
868 while (tmprow->previous()
869 && tmprow->previous()->par() == first_phys_par)
871 tmprow = tmprow->previous();
872 y -= tmprow->height();
876 // we can set the refreshing parameters now
877 status(bview, LyXText::NEED_MORE_REFRESH);
879 refresh_row = tmprow->previous(); /* the real refresh row will
880 be deleted, so I store
884 tmppar = tmprow->next()->par();
887 while (tmppar != endpar) {
888 removeRow(tmprow->next());
890 tmppar = tmprow->next()->par();
895 // remove the first one
896 tmprow2 = tmprow; /* this is because tmprow->previous()
898 tmprow = tmprow->previous();
901 tmppar = first_phys_par;
905 insertParagraph(bview, tmppar, tmprow);
909 while (tmprow->next()
910 && tmprow->next()->par() == tmppar) {
911 tmprow = tmprow->next();
913 tmppar = tmppar->next();
915 } while (tmppar && tmppar != endpar);
917 // this is because of layout changes
919 refresh_y -= refresh_row->height();
920 setHeightOfRow(bview, refresh_row);
922 refresh_row = firstrow;
924 setHeightOfRow(bview, refresh_row);
927 if (tmprow && tmprow->next())
928 setHeightOfRow(bview, tmprow->next());
932 bool LyXText::fullRebreak(BufferView * bview)
938 if (need_break_row) {
939 breakAgain(bview, need_break_row);
947 // important for the screen
950 /* the cursor set functions have a special mechanism. When they
951 * realize, that you left an empty paragraph, they will delete it.
952 * They also delete the corresponding row */
954 // need the selection cursor:
955 void LyXText::setSelection(BufferView * bview)
957 bool const lsel = selection.set();
959 if (!selection.set()) {
960 last_sel_cursor = selection.cursor;
961 selection.start = selection.cursor;
962 selection.end = selection.cursor;
967 // first the toggling area
968 if (cursor.y() < last_sel_cursor.y()
969 || (cursor.y() == last_sel_cursor.y()
970 && cursor.x() < last_sel_cursor.x())) {
971 toggle_end_cursor = last_sel_cursor;
972 toggle_cursor = cursor;
974 toggle_end_cursor = cursor;
975 toggle_cursor = last_sel_cursor;
978 last_sel_cursor = cursor;
980 // and now the whole selection
982 if (selection.cursor.par() == cursor.par())
983 if (selection.cursor.pos() < cursor.pos()) {
984 selection.end = cursor;
985 selection.start = selection.cursor;
987 selection.end = selection.cursor;
988 selection.start = cursor;
990 else if (selection.cursor.y() < cursor.y() ||
991 (selection.cursor.y() == cursor.y()
992 && selection.cursor.x() < cursor.x())) {
993 selection.end = cursor;
994 selection.start = selection.cursor;
997 selection.end = selection.cursor;
998 selection.start = cursor;
1001 // a selection with no contents is not a selection
1002 if (selection.start.par() == selection.end.par() &&
1003 selection.start.pos() == selection.end.pos())
1004 selection.set(false);
1006 if (inset_owner && (selection.set() || lsel))
1007 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1011 string const LyXText::selectionAsString(Buffer const * buffer,
1014 if (!selection.set()) return string();
1017 // Special handling if the whole selection is within one paragraph
1018 if (selection.start.par() == selection.end.par()) {
1019 result += selection.start.par()->asString(buffer,
1020 selection.start.pos(),
1021 selection.end.pos(),
1026 // The selection spans more than one paragraph
1028 // First paragraph in selection
1029 result += selection.start.par()->asString(buffer,
1030 selection.start.pos(),
1031 selection.start.par()->size(),
1035 // The paragraphs in between (if any)
1036 LyXCursor tmpcur(selection.start);
1037 tmpcur.par(tmpcur.par()->next());
1038 while (tmpcur.par() != selection.end.par()) {
1039 result += tmpcur.par()->asString(buffer, 0,
1040 tmpcur.par()->size(),
1042 tmpcur.par(tmpcur.par()->next());
1045 // Last paragraph in selection
1046 result += selection.end.par()->asString(buffer, 0,
1047 selection.end.pos(), label);
1053 void LyXText::clearSelection() const
1055 selection.set(false);
1056 selection.mark(false);
1057 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1061 void LyXText::cursorHome(BufferView * bview) const
1063 setCursor(bview, cursor.par(), cursor.row()->pos());
1067 void LyXText::cursorEnd(BufferView * bview) const
1069 if (!cursor.row()->next()
1070 || cursor.row()->next()->par() != cursor.row()->par()) {
1071 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1073 if (cursor.par()->size() &&
1074 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1075 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1076 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1078 setCursor(bview,cursor.par(),
1079 rowLast(cursor.row()) + 1);
1085 void LyXText::cursorTop(BufferView * bview) const
1087 while (cursor.par()->previous())
1088 cursor.par(cursor.par()->previous());
1089 setCursor(bview, cursor.par(), 0);
1093 void LyXText::cursorBottom(BufferView * bview) const
1095 while (cursor.par()->next())
1096 cursor.par(cursor.par()->next());
1097 setCursor(bview, cursor.par(), cursor.par()->size());
1101 void LyXText::toggleFree(BufferView * bview,
1102 LyXFont const & font, bool toggleall)
1104 // If the mask is completely neutral, tell user
1105 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1106 // Could only happen with user style
1107 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1111 // Try implicit word selection
1112 // If there is a change in the language the implicit word selection
1114 LyXCursor resetCursor = cursor;
1115 bool implicitSelection = (font.language() == ignore_language
1116 && font.number() == LyXFont::IGNORE)
1117 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1120 setFont(bview, font, toggleall);
1122 // Implicit selections are cleared afterwards
1123 //and cursor is set to the original position.
1124 if (implicitSelection) {
1126 cursor = resetCursor;
1127 setCursor(bview, cursor.par(), cursor.pos());
1128 selection.cursor = cursor;
1131 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1135 string LyXText::getStringToIndex(BufferView * bview)
1139 // Try implicit word selection
1140 // If there is a change in the language the implicit word selection
1142 LyXCursor resetCursor = cursor;
1143 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1145 if (!selection.set()) {
1146 bview->owner()->message(_("Nothing to index!"));
1149 if (selection.start.par() != selection.end.par()) {
1150 bview->owner()->message(_("Cannot index more than one paragraph!"));
1154 idxstring = selectionAsString(bview->buffer(), false);
1156 // Implicit selections are cleared afterwards
1157 //and cursor is set to the original position.
1158 if (implicitSelection) {
1160 cursor = resetCursor;
1161 setCursor(bview, cursor.par(), cursor.pos());
1162 selection.cursor = cursor;
1168 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1169 Paragraph const * par) const
1171 if (textclasslist.Style(buf->params.textclass,
1172 par->getLayout()).labeltype != LABEL_MANUAL)
1175 return par->beginningOfMainBody();
1179 /* the DTP switches for paragraphs. LyX will store them in the
1180 * first physicla paragraph. When a paragraph is broken, the top settings
1181 * rest, the bottom settings are given to the new one. So I can make shure,
1182 * they do not duplicate themself and you cannnot make dirty things with
1185 void LyXText::setParagraph(BufferView * bview,
1186 bool line_top, bool line_bottom,
1187 bool pagebreak_top, bool pagebreak_bottom,
1188 VSpace const & space_top,
1189 VSpace const & space_bottom,
1190 Spacing const & spacing,
1192 string labelwidthstring,
1195 LyXCursor tmpcursor = cursor;
1196 if (!selection.set()) {
1197 selection.start = cursor;
1198 selection.end = cursor;
1201 // make sure that the depth behind the selection are restored, too
1202 Paragraph * endpar = selection.end.par()->next();
1203 Paragraph * undoendpar = endpar;
1205 if (endpar && endpar->getDepth()) {
1206 while (endpar && endpar->getDepth()) {
1207 endpar = endpar->next();
1208 undoendpar = endpar;
1212 // because of parindents etc.
1213 endpar = endpar->next();
1216 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1219 Paragraph * tmppar = selection.end.par();
1220 while (tmppar != selection.start.par()->previous()) {
1221 setCursor(bview, tmppar, 0);
1222 status(bview, LyXText::NEED_MORE_REFRESH);
1223 refresh_row = cursor.row();
1224 refresh_y = cursor.y() - cursor.row()->baseline();
1225 cursor.par()->params().lineTop(line_top);
1226 cursor.par()->params().lineBottom(line_bottom);
1227 cursor.par()->params().pagebreakTop(pagebreak_top);
1228 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1229 cursor.par()->params().spaceTop(space_top);
1230 cursor.par()->params().spaceBottom(space_bottom);
1231 cursor.par()->params().spacing(spacing);
1232 // does the layout allow the new alignment?
1233 if (align == LYX_ALIGN_LAYOUT)
1234 align = textclasslist
1235 .Style(bview->buffer()->params.textclass,
1236 cursor.par()->getLayout()).align;
1237 if (align & textclasslist
1238 .Style(bview->buffer()->params.textclass,
1239 cursor.par()->getLayout()).alignpossible) {
1240 if (align == textclasslist
1241 .Style(bview->buffer()->params.textclass,
1242 cursor.par()->getLayout()).align)
1243 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1245 cursor.par()->params().align(align);
1247 cursor.par()->setLabelWidthString(labelwidthstring);
1248 cursor.par()->params().noindent(noindent);
1249 tmppar = cursor.par()->previous();
1252 redoParagraphs(bview, selection.start, endpar);
1255 setCursor(bview, selection.start.par(), selection.start.pos());
1256 selection.cursor = cursor;
1257 setCursor(bview, selection.end.par(), selection.end.pos());
1258 setSelection(bview);
1259 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1261 bview->updateInset(inset_owner, true);
1265 char loweralphaCounter(int n)
1267 if (n < 1 || n > 26)
1277 char alphaCounter(int n)
1279 if (n < 1 || n > 26)
1287 char hebrewCounter(int n)
1289 static const char hebrew[22] = {
1290 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1291 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1292 '÷', 'ø', 'ù', 'ú'
1294 if (n < 1 || n > 22)
1302 string const romanCounter(int n)
1304 static char const * roman[20] = {
1305 "i", "ii", "iii", "iv", "v",
1306 "vi", "vii", "viii", "ix", "x",
1307 "xi", "xii", "xiii", "xiv", "xv",
1308 "xvi", "xvii", "xviii", "xix", "xx"
1310 if (n < 1 || n > 20)
1319 // set the counter of a paragraph. This includes the labels
1320 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1322 LyXLayout const & layout =
1323 textclasslist.Style(buf->params.textclass,
1326 LyXTextClass const & textclass =
1327 textclasslist.TextClass(buf->params.textclass);
1329 // copy the prev-counters to this one,
1330 // unless this is the first paragraph
1331 if (par->previous()) {
1332 for (int i = 0; i < 10; ++i) {
1333 par->setCounter(i, par->previous()->getFirstCounter(i));
1335 par->params().appendix(par->previous()->params().appendix());
1336 if (!par->params().appendix() && par->params().startOfAppendix()) {
1337 par->params().appendix(true);
1338 for (int i = 0; i < 10; ++i) {
1339 par->setCounter(i, 0);
1342 par->enumdepth = par->previous()->enumdepth;
1343 par->itemdepth = par->previous()->itemdepth;
1345 for (int i = 0; i < 10; ++i) {
1346 par->setCounter(i, 0);
1348 par->params().appendix(par->params().startOfAppendix());
1353 /* Maybe we have to increment the enumeration depth.
1354 * BUT, enumeration in a footnote is considered in isolation from its
1355 * surrounding paragraph so don't increment if this is the
1356 * first line of the footnote
1357 * AND, bibliographies can't have their depth changed ie. they
1358 * are always of depth 0
1361 && par->previous()->getDepth() < par->getDepth()
1362 && textclasslist.Style(buf->params.textclass,
1363 par->previous()->getLayout()
1364 ).labeltype == LABEL_COUNTER_ENUMI
1365 && par->enumdepth < 3
1366 && layout.labeltype != LABEL_BIBLIO) {
1370 // Maybe we have to decrement the enumeration depth, see note above
1372 && par->previous()->getDepth() > par->getDepth()
1373 && layout.labeltype != LABEL_BIBLIO) {
1374 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1375 par->setCounter(6 + par->enumdepth,
1376 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1377 /* reset the counters.
1378 * A depth change is like a breaking layout
1380 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1381 par->setCounter(i, 0);
1384 if (!par->params().labelString().empty()) {
1385 par->params().labelString(string());
1388 if (layout.margintype == MARGIN_MANUAL) {
1389 if (par->params().labelWidthString().empty()) {
1390 par->setLabelWidthString(layout.labelstring());
1393 par->setLabelWidthString(string());
1396 // is it a layout that has an automatic label?
1397 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1399 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1400 if (i >= 0 && i<= buf->params.secnumdepth) {
1401 par->incCounter(i); // increment the counter
1403 // Is there a label? Useful for Chapter layout
1404 if (!par->params().appendix()) {
1405 if (!layout.labelstring().empty())
1406 par->params().labelString(layout.labelstring());
1408 par->params().labelString(string());
1410 if (!layout.labelstring_appendix().empty())
1411 par->params().labelString(layout.labelstring_appendix());
1413 par->params().labelString(string());
1418 if (!par->params().appendix()) {
1419 switch (2 * LABEL_COUNTER_CHAPTER -
1420 textclass.maxcounter() + i) {
1421 case LABEL_COUNTER_CHAPTER:
1422 s << par->getCounter(i);
1424 case LABEL_COUNTER_SECTION:
1425 s << par->getCounter(i - 1) << '.'
1426 << par->getCounter(i);
1428 case LABEL_COUNTER_SUBSECTION:
1429 s << par->getCounter(i - 2) << '.'
1430 << par->getCounter(i - 1) << '.'
1431 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSUBSECTION:
1434 s << par->getCounter(i - 3) << '.'
1435 << par->getCounter(i - 2) << '.'
1436 << par->getCounter(i - 1) << '.'
1437 << par->getCounter(i);
1440 case LABEL_COUNTER_PARAGRAPH:
1441 s << par->getCounter(i - 4) << '.'
1442 << par->getCounter(i - 3) << '.'
1443 << par->getCounter(i - 2) << '.'
1444 << par->getCounter(i - 1) << '.'
1445 << par->getCounter(i);
1447 case LABEL_COUNTER_SUBPARAGRAPH:
1448 s << par->getCounter(i - 5) << '.'
1449 << par->getCounter(i - 4) << '.'
1450 << par->getCounter(i - 3) << '.'
1451 << par->getCounter(i - 2) << '.'
1452 << par->getCounter(i - 1) << '.'
1453 << par->getCounter(i);
1457 // Can this ever be reached? And in the
1458 // case it is, how can this be correct?
1460 s << par->getCounter(i) << '.';
1463 } else { // appendix
1464 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1465 case LABEL_COUNTER_CHAPTER:
1466 if (par->isRightToLeftPar(buf->params))
1467 s << hebrewCounter(par->getCounter(i));
1469 s << alphaCounter(par->getCounter(i));
1471 case LABEL_COUNTER_SECTION:
1472 if (par->isRightToLeftPar(buf->params))
1473 s << hebrewCounter(par->getCounter(i - 1));
1475 s << alphaCounter(par->getCounter(i - 1));
1478 << par->getCounter(i);
1481 case LABEL_COUNTER_SUBSECTION:
1482 if (par->isRightToLeftPar(buf->params))
1483 s << hebrewCounter(par->getCounter(i - 2));
1485 s << alphaCounter(par->getCounter(i - 2));
1488 << par->getCounter(i-1) << '.'
1489 << par->getCounter(i);
1492 case LABEL_COUNTER_SUBSUBSECTION:
1493 if (par->isRightToLeftPar(buf->params))
1494 s << hebrewCounter(par->getCounter(i-3));
1496 s << alphaCounter(par->getCounter(i-3));
1499 << par->getCounter(i-2) << '.'
1500 << par->getCounter(i-1) << '.'
1501 << par->getCounter(i);
1504 case LABEL_COUNTER_PARAGRAPH:
1505 if (par->isRightToLeftPar(buf->params))
1506 s << hebrewCounter(par->getCounter(i-4));
1508 s << alphaCounter(par->getCounter(i-4));
1511 << par->getCounter(i-3) << '.'
1512 << par->getCounter(i-2) << '.'
1513 << par->getCounter(i-1) << '.'
1514 << par->getCounter(i);
1517 case LABEL_COUNTER_SUBPARAGRAPH:
1518 if (par->isRightToLeftPar(buf->params))
1519 s << hebrewCounter(par->getCounter(i-5));
1521 s << alphaCounter(par->getCounter(i-5));
1524 << par->getCounter(i-4) << '.'
1525 << par->getCounter(i-3) << '.'
1526 << par->getCounter(i-2) << '.'
1527 << par->getCounter(i-1) << '.'
1528 << par->getCounter(i);
1532 // Can this ever be reached? And in the
1533 // case it is, how can this be correct?
1535 s << par->getCounter(i) << '.';
1541 par->params().labelString(par->params().labelString() +s.str().c_str());
1542 // We really want to remove the c_str as soon as
1545 for (i++; i < 10; ++i) {
1546 // reset the following counters
1547 par->setCounter(i, 0);
1549 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1550 for (i++; i < 10; ++i) {
1551 // reset the following counters
1552 par->setCounter(i, 0);
1554 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1555 par->incCounter(i + par->enumdepth);
1556 int number = par->getCounter(i + par->enumdepth);
1560 switch (par->enumdepth) {
1562 if (par->isRightToLeftPar(buf->params))
1564 << hebrewCounter(number)
1568 << loweralphaCounter(number)
1572 if (par->isRightToLeftPar(buf->params))
1573 s << '.' << romanCounter(number);
1575 s << romanCounter(number) << '.';
1578 if (par->isRightToLeftPar(buf->params))
1580 << alphaCounter(number);
1582 s << alphaCounter(number)
1586 if (par->isRightToLeftPar(buf->params))
1593 par->params().labelString(s.str().c_str());
1595 for (i += par->enumdepth + 1; i < 10; ++i) {
1596 // reset the following counters
1597 par->setCounter(i, 0);
1601 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1602 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1604 int number = par->getCounter(i);
1606 InsetCommandParams p( "bibitem" );
1607 par->bibkey = new InsetBibKey(p);
1609 par->bibkey->setCounter(number);
1610 par->params().labelString(layout.labelstring());
1612 // In biblio should't be following counters but...
1614 string s = layout.labelstring();
1616 // the caption hack:
1617 if (layout.labeltype == LABEL_SENSITIVE) {
1618 bool isOK (par->inInset() && par->inInset()->owner() &&
1619 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1622 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1624 = floatList.getType(tmp->type());
1625 // We should get the correct number here too.
1626 s = fl.name() + " #:";
1628 /* par->SetLayout(0);
1629 s = layout->labelstring; */
1630 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1631 ? " :úåòîùî øñç" : "Senseless: ";
1634 par->params().labelString(s);
1636 /* reset the enumeration counter. They are always resetted
1637 * when there is any other layout between */
1638 for (int i = 6 + par->enumdepth; i < 10; ++i)
1639 par->setCounter(i, 0);
1644 // Updates all counters BEHIND the row. Changed paragraphs
1645 // with a dynamic left margin will be rebroken.
1646 void LyXText::updateCounters(BufferView * bview, Row * row) const
1654 par = row->par()->next();
1658 while (row->par() != par)
1661 setCounter(bview->buffer(), par);
1663 // now check for the headline layouts. remember that they
1664 // have a dynamic left margin
1665 if ((textclasslist.Style(bview->buffer()->params.textclass,
1666 par->layout).margintype == MARGIN_DYNAMIC
1667 || textclasslist.Style(bview->buffer()->params.textclass,
1668 par->layout).labeltype == LABEL_SENSITIVE)) {
1670 // Rebreak the paragraph
1671 removeParagraph(row);
1672 appendParagraph(bview, row);
1679 void LyXText::insertInset(BufferView * bview, Inset * inset)
1681 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1683 setUndo(bview, Undo::INSERT,
1684 cursor.par(), cursor.par()->next());
1685 cursor.par()->insertInset(cursor.pos(), inset);
1686 // Just to rebreak and refresh correctly.
1687 // The character will not be inserted a second time
1688 insertChar(bview, Paragraph::META_INSET);
1690 // If we enter a highly editable inset the cursor should be to before
1691 // the inset. This couldn't happen before as Undo was not handled inside
1692 // inset now after the Undo LyX tries to call inset->Edit(...) again
1693 // and cannot do this as the cursor is behind the inset and GetInset
1694 // does not return the inset!
1695 if (isHighlyEditableInset(inset)) {
1696 cursorLeft(bview, true);
1702 void LyXText::copyEnvironmentType()
1704 copylayouttype = cursor.par()->getLayout();
1708 void LyXText::pasteEnvironmentType(BufferView * bview)
1710 setLayout(bview, copylayouttype);
1714 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1716 // Stuff what we got on the clipboard. Even if there is no selection.
1718 // There is a problem with having the stuffing here in that the
1719 // larger the selection the slower LyX will get. This can be
1720 // solved by running the line below only when the selection has
1721 // finished. The solution used currently just works, to make it
1722 // faster we need to be more clever and probably also have more
1723 // calls to stuffClipboard. (Lgb)
1724 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1726 // This doesn't make sense, if there is no selection
1727 if (!selection.set())
1730 // OK, we have a selection. This is always between selection.start
1731 // and selection.end
1733 // make sure that the depth behind the selection are restored, too
1734 Paragraph * endpar = selection.end.par()->next();
1735 Paragraph * undoendpar = endpar;
1737 if (endpar && endpar->getDepth()) {
1738 while (endpar && endpar->getDepth()) {
1739 endpar = endpar->next();
1740 undoendpar = endpar;
1742 } else if (endpar) {
1743 endpar = endpar->next(); // because of parindents etc.
1746 setUndo(bview, Undo::DELETE,
1747 selection.start.par(), undoendpar);
1749 // there are two cases: cut only within one paragraph or
1750 // more than one paragraph
1751 if (selection.start.par() == selection.end.par()) {
1752 // only within one paragraph
1753 endpar = selection.end.par();
1754 int pos = selection.end.pos();
1755 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1756 selection.start.pos(), pos,
1757 bview->buffer()->params.textclass,
1759 selection.end.pos(pos);
1761 endpar = selection.end.par();
1762 int pos = selection.end.pos();
1763 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1764 selection.start.pos(), pos,
1765 bview->buffer()->params.textclass,
1768 selection.end.par(endpar);
1769 selection.end.pos(pos);
1770 cursor.pos(selection.end.pos());
1772 endpar = endpar->next();
1774 // sometimes necessary
1776 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1778 redoParagraphs(bview, selection.start, endpar);
1780 // cutSelection can invalidate the cursor so we need to set
1782 cursor = selection.start;
1784 // need a valid cursor. (Lgb)
1787 setCursor(bview, cursor.par(), cursor.pos());
1788 selection.cursor = cursor;
1789 updateCounters(bview, cursor.row());
1793 void LyXText::copySelection(BufferView * bview)
1795 // Stuff what we got on the clipboard. Even if there is no selection.
1797 // There is a problem with having the stuffing here in that the
1798 // larger the selection the slower LyX will get. This can be
1799 // solved by running the line below only when the selection has
1800 // finished. The solution used currently just works, to make it
1801 // faster we need to be more clever and probably also have more
1802 // calls to stuffClipboard. (Lgb)
1803 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1805 // this doesnt make sense, if there is no selection
1806 if (!selection.set())
1809 // ok we have a selection. This is always between selection.start
1810 // and sel_end cursor
1812 // copy behind a space if there is one
1813 while (selection.start.par()->size() > selection.start.pos()
1814 && selection.start.par()->isLineSeparator(selection.start.pos())
1815 && (selection.start.par() != selection.end.par()
1816 || selection.start.pos() < selection.end.pos()))
1817 selection.start.pos(selection.start.pos() + 1);
1819 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1820 selection.start.pos(), selection.end.pos(),
1821 bview->buffer()->params.textclass);
1825 void LyXText::pasteSelection(BufferView * bview)
1827 // this does not make sense, if there is nothing to paste
1828 if (!CutAndPaste::checkPastePossible(cursor.par()))
1831 setUndo(bview, Undo::INSERT,
1832 cursor.par(), cursor.par()->next());
1835 Paragraph * actpar = cursor.par();
1836 int pos = cursor.pos();
1838 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1839 bview->buffer()->params.textclass);
1841 redoParagraphs(bview, cursor, endpar);
1843 setCursor(bview, cursor.par(), cursor.pos());
1846 setCursor(bview, actpar, pos);
1847 updateCounters(bview, cursor.row());
1851 // returns a pointer to the very first Paragraph
1852 Paragraph * LyXText::firstParagraph() const
1854 return ownerParagraph();
1858 // sets the selection over the number of characters of string, no check!!
1859 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1864 selection.cursor = cursor;
1865 for (string::size_type i = 0; i < str.length(); ++i)
1867 setSelection(bview);
1871 // simple replacing. The font of the first selected character is used
1872 void LyXText::replaceSelectionWithString(BufferView * bview,
1875 setCursorParUndo(bview);
1878 if (!selection.set()) { // create a dummy selection
1879 selection.end = cursor;
1880 selection.start = cursor;
1883 // Get font setting before we cut
1884 pos_type pos = selection.end.pos();
1885 LyXFont const font = selection.start.par()
1886 ->getFontSettings(bview->buffer()->params,
1887 selection.start.pos());
1889 // Insert the new string
1890 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1891 selection.end.par()->insertChar(pos, (*cit), font);
1895 // Cut the selection
1896 cutSelection(bview, true, false);
1902 // needed to insert the selection
1903 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1905 Paragraph * par = cursor.par();
1906 pos_type pos = cursor.pos();
1907 Paragraph * endpar = cursor.par()->next();
1909 setCursorParUndo(bview);
1911 // only to be sure, should not be neccessary
1914 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1916 redoParagraphs(bview, cursor, endpar);
1917 setCursor(bview, cursor.par(), cursor.pos());
1918 selection.cursor = cursor;
1919 setCursor(bview, par, pos);
1920 setSelection(bview);
1924 // turns double-CR to single CR, others where converted into one
1925 // blank. Then InsertStringAsLines is called
1926 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1928 string linestr(str);
1929 bool newline_inserted = false;
1930 for (string::size_type i = 0; i < linestr.length(); ++i) {
1931 if (linestr[i] == '\n') {
1932 if (newline_inserted) {
1933 // we know that \r will be ignored by
1934 // InsertStringA. Of course, it is a dirty
1935 // trick, but it works...
1936 linestr[i - 1] = '\r';
1940 newline_inserted = true;
1942 } else if (IsPrintable(linestr[i])) {
1943 newline_inserted = false;
1946 insertStringAsLines(bview, linestr);
1950 bool LyXText::gotoNextInset(BufferView * bview,
1951 std::vector<Inset::Code> const & codes,
1952 string const & contents) const
1954 LyXCursor res = cursor;
1957 if (res.pos() < res.par()->size() - 1) {
1958 res.pos(res.pos() + 1);
1960 res.par(res.par()->next());
1964 } while (res.par() &&
1965 !(res.par()->isInset(res.pos())
1966 && (inset = res.par()->getInset(res.pos())) != 0
1967 && find(codes.begin(), codes.end(), inset->lyxCode())
1969 && (contents.empty() ||
1970 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1974 setCursor(bview, res.par(), res.pos(), false);
1981 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1984 LyXCursor tmpcursor;
1988 Row * row = getRow(par, pos, y);
1990 // is there a break one row above
1991 if (row->previous() && row->previous()->par() == row->par()) {
1992 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1993 if (z >= row->pos()) {
1994 // set the dimensions of the row above
1995 y -= row->previous()->height();
1997 refresh_row = row->previous();
1998 status(bview, LyXText::NEED_MORE_REFRESH);
2000 breakAgain(bview, row->previous());
2002 // set the cursor again. Otherwise
2003 // dangling pointers are possible
2004 setCursor(bview, cursor.par(), cursor.pos(),
2005 false, cursor.boundary());
2006 selection.cursor = cursor;
2011 int const tmpheight = row->height();
2012 pos_type const tmplast = rowLast(row);
2016 breakAgain(bview, row);
2017 if (row->height() == tmpheight && rowLast(row) == tmplast)
2018 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2020 status(bview, LyXText::NEED_MORE_REFRESH);
2022 // check the special right address boxes
2023 if (textclasslist.Style(bview->buffer()->params.textclass,
2024 par->getLayout()).margintype
2025 == MARGIN_RIGHT_ADDRESS_BOX)
2033 redoDrawingOfParagraph(bview, tmpcursor);
2036 // set the cursor again. Otherwise dangling pointers are possible
2037 // also set the selection
2039 if (selection.set()) {
2041 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2042 false, selection.cursor.boundary());
2043 selection.cursor = cursor;
2044 setCursorIntern(bview, selection.start.par(),
2045 selection.start.pos(),
2046 false, selection.start.boundary());
2047 selection.start = cursor;
2048 setCursorIntern(bview, selection.end.par(),
2049 selection.end.pos(),
2050 false, selection.end.boundary());
2051 selection.end = cursor;
2052 setCursorIntern(bview, last_sel_cursor.par(),
2053 last_sel_cursor.pos(),
2054 false, last_sel_cursor.boundary());
2055 last_sel_cursor = cursor;
2058 setCursorIntern(bview, cursor.par(), cursor.pos(),
2059 false, cursor.boundary());
2063 // returns false if inset wasn't found
2064 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2066 // first check the current paragraph
2067 int pos = cursor.par()->getPositionOfInset(inset);
2069 checkParagraph(bview, cursor.par(), pos);
2073 // check every paragraph
2075 Paragraph * par = firstParagraph();
2077 pos = par->getPositionOfInset(inset);
2079 checkParagraph(bview, par, pos);
2089 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2091 bool setfont, bool boundary) const
2093 LyXCursor old_cursor = cursor;
2094 setCursorIntern(bview, par, pos, setfont, boundary);
2095 deleteEmptyParagraphMechanism(bview, old_cursor);
2099 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2100 pos_type pos, bool boundary) const
2104 cur.boundary(boundary);
2106 // get the cursor y position in text
2108 Row * row = getRow(par, pos, y);
2109 // y is now the beginning of the cursor row
2110 y += row->baseline();
2111 // y is now the cursor baseline
2114 // now get the cursors x position
2116 float fill_separator;
2118 float fill_label_hfill;
2119 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2121 pos_type cursor_vpos = 0;
2122 pos_type last = rowLastPrintable(row);
2124 if (pos > last + 1) // This shouldn't happen.
2126 else if (pos < row->pos())
2129 if (last < row->pos())
2130 cursor_vpos = row->pos();
2131 else if (pos > last && !boundary)
2132 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2133 ? row->pos() : last + 1;
2134 else if (pos > row->pos() &&
2135 (pos > last || boundary))
2136 /// Place cursor after char at (logical) position pos - 1
2137 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2138 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2140 /// Place cursor before char at (logical) position pos
2141 cursor_vpos = (bidi_level(pos) % 2 == 0)
2142 ? log2vis(pos) : log2vis(pos) + 1;
2144 pos_type main_body =
2145 beginningOfMainBody(bview->buffer(), row->par());
2146 if ((main_body > 0) &&
2147 ((main_body-1 > last) ||
2148 !row->par()->isLineSeparator(main_body-1)))
2151 for (pos_type vpos = row->pos();
2152 vpos < cursor_vpos; ++vpos) {
2153 pos = vis2log(vpos);
2154 if (main_body > 0 && pos == main_body - 1) {
2155 x += fill_label_hfill +
2156 lyxfont::width(textclasslist.Style(
2157 bview->buffer()->params.textclass,
2158 row->par()->getLayout())
2160 getLabelFont(bview->buffer(), row->par()));
2161 if (row->par()->isLineSeparator(main_body-1))
2162 x -= singleWidth(bview, row->par(),main_body-1);
2164 if (hfillExpansion(bview->buffer(), row, pos)) {
2165 x += singleWidth(bview, row->par(), pos);
2166 if (pos >= main_body)
2169 x += fill_label_hfill;
2170 } else if (row->par()->isSeparator(pos)) {
2171 x += singleWidth(bview, row->par(), pos);
2172 if (pos >= main_body)
2173 x += fill_separator;
2175 x += singleWidth(bview, row->par(), pos);
2184 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2186 bool setfont, bool boundary) const
2188 InsetText * it = static_cast<InsetText *>(par->inInset());
2190 if (it != inset_owner) {
2191 lyxerr << "InsetText is " << it << endl;
2192 lyxerr << "inset_owner is " << inset_owner << endl;
2193 #warning I believe this code is wrong. (Lgb)
2194 #warning Jürgen, have a look at this. (Lgb)
2195 #warning Hmmm, I guess you are right but we
2196 #warning should verify when this is needed
2197 // Jürgen, would you like to have a look?
2198 // I guess we need to move the outer cursor
2199 // and open and lock the inset (bla bla bla)
2200 // stuff I don't know... so can you have a look?
2202 // I moved the lyxerr stuff in here so we can see if
2203 // this is actually really needed and where!
2205 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2211 setCursor(bview, cursor, par, pos, boundary);
2213 setCurrentFont(bview);
2217 void LyXText::setCurrentFont(BufferView * bview) const
2219 pos_type pos = cursor.pos();
2220 if (cursor.boundary() && pos > 0)
2224 if (pos == cursor.par()->size())
2226 else // potentional bug... BUG (Lgb)
2227 if (cursor.par()->isSeparator(pos)) {
2228 if (pos > cursor.row()->pos() &&
2229 bidi_level(pos) % 2 ==
2230 bidi_level(pos - 1) % 2)
2232 else if (pos + 1 < cursor.par()->size())
2238 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2239 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2241 if (cursor.pos() == cursor.par()->size() &&
2242 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2243 !cursor.boundary()) {
2244 Language const * lang =
2245 cursor.par()->getParLanguage(bview->buffer()->params);
2246 current_font.setLanguage(lang);
2247 current_font.setNumber(LyXFont::OFF);
2248 real_current_font.setLanguage(lang);
2249 real_current_font.setNumber(LyXFont::OFF);
2254 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2256 LyXCursor old_cursor = cursor;
2258 setCursorFromCoordinates(bview, cursor, x, y);
2259 setCurrentFont(bview);
2260 deleteEmptyParagraphMechanism(bview, old_cursor);
2264 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2267 // Get the row first.
2269 Row * row = getRowNearY(y);
2271 pos_type const column = getColumnNearX(bview, row, x, bound);
2272 cur.par(row->par());
2273 cur.pos(row->pos() + column);
2275 cur.y(y + row->baseline());
2277 cur.boundary(bound);
2281 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2283 if (cursor.pos() > 0) {
2284 bool boundary = cursor.boundary();
2285 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2286 if (!internal && !boundary &&
2287 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2288 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2289 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2290 Paragraph * par = cursor.par()->previous();
2291 setCursor(bview, par, par->size());
2296 void LyXText::cursorRight(BufferView * bview, bool internal) const
2298 if (!internal && cursor.boundary() &&
2299 !cursor.par()->isNewline(cursor.pos()))
2300 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2301 else if (cursor.pos() < cursor.par()->size()) {
2302 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2304 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2305 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2306 } else if (cursor.par()->next())
2307 setCursor(bview, cursor.par()->next(), 0);
2311 void LyXText::cursorUp(BufferView * bview) const
2313 setCursorFromCoordinates(bview, cursor.x_fix(),
2314 cursor.y() - cursor.row()->baseline() - 1);
2318 void LyXText::cursorDown(BufferView * bview) const
2320 setCursorFromCoordinates(bview, cursor.x_fix(),
2321 cursor.y() - cursor.row()->baseline()
2322 + cursor.row()->height() + 1);
2326 void LyXText::cursorUpParagraph(BufferView * bview) const
2328 if (cursor.pos() > 0) {
2329 setCursor(bview, cursor.par(), 0);
2331 else if (cursor.par()->previous()) {
2332 setCursor(bview, cursor.par()->previous(), 0);
2337 void LyXText::cursorDownParagraph(BufferView * bview) const
2339 if (cursor.par()->next()) {
2340 setCursor(bview, cursor.par()->next(), 0);
2342 setCursor(bview, cursor.par(), cursor.par()->size());
2346 // fix the cursor `cur' after a characters has been deleted at `where'
2347 // position. Called by deleteEmptyParagraphMechanism
2348 void LyXText::fixCursorAfterDelete(BufferView * bview,
2350 LyXCursor const & where) const
2352 // if cursor is not in the paragraph where the delete occured,
2354 if (cur.par() != where.par())
2357 // if cursor position is after the place where the delete occured,
2359 if (cur.pos() > where.pos())
2360 cur.pos(cur.pos()-1);
2362 // recompute row et al. for this cursor
2363 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2367 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2368 LyXCursor const & old_cursor) const
2370 // don't delete anything if this is the ONLY paragraph!
2371 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2374 // Would be wrong to delete anything if we have a selection.
2375 if (selection.set()) return;
2377 // We allow all kinds of "mumbo-jumbo" when freespacing.
2378 if (textclasslist.Style(bview->buffer()->params.textclass,
2379 old_cursor.par()->getLayout()).free_spacing ||
2380 old_cursor.par()->isFreeSpacing())
2385 bool deleted = false;
2387 /* Ok I'll put some comments here about what is missing.
2388 I have fixed BackSpace (and thus Delete) to not delete
2389 double-spaces automagically. I have also changed Cut,
2390 Copy and Paste to hopefully do some sensible things.
2391 There are still some small problems that can lead to
2392 double spaces stored in the document file or space at
2393 the beginning of paragraphs. This happens if you have
2394 the cursor betwenn to spaces and then save. Or if you
2395 cut and paste and the selection have a space at the
2396 beginning and then save right after the paste. I am
2397 sure none of these are very hard to fix, but I will
2398 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2399 that I can get some feedback. (Lgb)
2402 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2403 // delete the LineSeparator.
2406 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2407 // delete the LineSeparator.
2410 // If the pos around the old_cursor were spaces, delete one of them.
2411 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2412 // Only if the cursor has really moved
2414 if (old_cursor.pos() > 0
2415 && old_cursor.pos() < old_cursor.par()->size()
2416 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2417 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2418 old_cursor.par()->erase(old_cursor.pos() - 1);
2419 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2421 #ifdef WITH_WARNINGS
2422 #warning This will not work anymore when we have multiple views of the same buffer
2423 // In this case, we will have to correct also the cursors held by
2424 // other bufferviews. It will probably be easier to do that in a more
2425 // automated way in LyXCursor code. (JMarc 26/09/2001)
2427 // correct all cursors held by the LyXText
2428 fixCursorAfterDelete(bview, cursor, old_cursor);
2429 fixCursorAfterDelete(bview, selection.cursor,
2431 fixCursorAfterDelete(bview, selection.start,
2433 fixCursorAfterDelete(bview, selection.end, old_cursor);
2434 fixCursorAfterDelete(bview, last_sel_cursor,
2436 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2437 fixCursorAfterDelete(bview, toggle_end_cursor,
2443 // Do not delete empty paragraphs with keepempty set.
2444 if ((textclasslist.Style(bview->buffer()->params.textclass,
2445 old_cursor.par()->getLayout())).keepempty)
2448 // only do our magic if we changed paragraph
2449 if (old_cursor.par() == cursor.par())
2452 if ((old_cursor.par()->size() == 0
2453 || (old_cursor.par()->size() == 1
2454 && old_cursor.par()->isLineSeparator(0)))) {
2455 // ok, we will delete anything
2456 LyXCursor tmpcursor;
2458 // make sure that you do not delete any environments
2459 status(bview, LyXText::NEED_MORE_REFRESH);
2462 if (old_cursor.row()->previous()) {
2463 refresh_row = old_cursor.row()->previous();
2464 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2466 cursor = old_cursor; // that undo can restore the right cursor position
2467 Paragraph * endpar = old_cursor.par()->next();
2468 if (endpar && endpar->getDepth()) {
2469 while (endpar && endpar->getDepth()) {
2470 endpar = endpar->next();
2473 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2477 removeRow(old_cursor.row());
2478 if (ownerParagraph() == old_cursor.par()) {
2479 ownerParagraph(ownerParagraph()->next());
2482 delete old_cursor.par();
2484 /* Breakagain the next par. Needed because of
2485 * the parindent that can occur or dissappear.
2486 * The next row can change its height, if
2487 * there is another layout before */
2488 if (refresh_row->next()) {
2489 breakAgain(bview, refresh_row->next());
2490 updateCounters(bview, refresh_row);
2492 setHeightOfRow(bview, refresh_row);
2494 refresh_row = old_cursor.row()->next();
2495 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2498 cursor = old_cursor; // that undo can restore the right cursor position
2499 Paragraph * endpar = old_cursor.par()->next();
2500 if (endpar && endpar->getDepth()) {
2501 while (endpar && endpar->getDepth()) {
2502 endpar = endpar->next();
2505 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2509 removeRow(old_cursor.row());
2511 if (ownerParagraph() == old_cursor.par()) {
2512 ownerParagraph(ownerParagraph()->next());
2515 delete old_cursor.par();
2517 /* Breakagain the next par. Needed because of
2518 the parindent that can occur or dissappear.
2519 The next row can change its height, if
2520 there is another layout before */
2522 breakAgain(bview, refresh_row);
2523 updateCounters(bview, refresh_row->previous());
2528 setCursorIntern(bview, cursor.par(), cursor.pos());
2530 if (selection.cursor.par() == old_cursor.par()
2531 && selection.cursor.pos() == selection.cursor.pos()) {
2532 // correct selection
2533 selection.cursor = cursor;
2537 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2538 redoParagraphs(bview, old_cursor,
2539 old_cursor.par()->next());
2541 setCursorIntern(bview, cursor.par(), cursor.pos());
2542 selection.cursor = cursor;
2548 void LyXText::toggleAppendix(BufferView * bview)
2550 Paragraph * par = cursor.par();
2551 bool start = !par->params().startOfAppendix();
2553 // ensure that we have only one start_of_appendix in this document
2554 Paragraph * tmp = firstParagraph();
2555 for (; tmp; tmp = tmp->next()) {
2556 tmp->params().startOfAppendix(false);
2559 par->params().startOfAppendix(start);
2561 // we can set the refreshing parameters now
2562 status(bview, LyXText::NEED_MORE_REFRESH);
2564 refresh_row = 0; // not needed for full update
2565 updateCounters(bview, 0);
2566 setCursor(bview, cursor.par(), cursor.pos());
2570 Paragraph * LyXText::ownerParagraph() const
2573 return inset_owner->paragraph();
2575 return bv_owner->buffer()->paragraph;
2579 void LyXText::ownerParagraph(Paragraph * p) const
2582 inset_owner->paragraph(p);
2584 bv_owner->buffer()->paragraph = p;
2589 void LyXText::ownerParagraph(int id, Paragraph * p) const
2591 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2592 if (op && op->inInset()) {
2593 static_cast<InsetText *>(op->inInset())->paragraph(p);
2596 inset_owner->paragraph(p);
2598 bv_owner->buffer()->paragraph = p;
2604 LyXText::text_status LyXText::status() const
2610 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2612 // well as much as I know && binds more then || so the above and the
2613 // below are identical (this for your known use of parentesis!)
2614 // Now some explanation:
2615 // We should only go up with refreshing code so this means that if
2616 // we have a MORE refresh we should never set it to LITTLE if we still
2617 // didn't handle it (and then it will be UNCHANGED. Now as long as
2618 // we stay inside one LyXText this may work but we need to tell the
2619 // outermost LyXText that it should REALLY draw us if there is some
2620 // change in a Inset::LyXText. So you see that when we are inside a
2621 // inset's LyXText we give the LITTLE to the outermost LyXText to
2622 // tell'em that it should redraw the actual row (where the inset
2623 // resides! Capito?!
2625 if ((status_ != NEED_MORE_REFRESH)
2626 || (status_ == NEED_MORE_REFRESH
2627 && st != NEED_VERY_LITTLE_REFRESH))
2630 if (inset_owner && st != UNCHANGED) {
2631 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2632 if (!bview->text->refresh_row) {
2633 bview->text->refresh_row = bview->text->cursor.row();
2634 bview->text->refresh_y = bview->text->cursor.y() -
2635 bview->text->cursor.row()->baseline();