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"
25 #include "lyx_gui_misc.h"
27 #include "BufferView.h"
29 #include "CutAndPaste.h"
35 #include "FloatList.h"
37 #include "ParagraphParameters.h"
39 #include "insets/inseterror.h"
40 #include "insets/insetbib.h"
41 #include "insets/insetspecialchar.h"
42 #include "insets/insettext.h"
43 #include "insets/insetfloat.h"
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
58 LyXText::LyXText(BufferView * bv)
59 : number_of_rows(0), height(0), width(0), first(0),
60 bv_owner(bv), inset_owner(0), the_locking_inset(0),
61 need_break_row(0), refresh_y(0), refresh_row(0),
62 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
67 LyXText::LyXText(InsetText * inset)
68 : number_of_rows(0), height(0), width(0), first(0),
69 bv_owner(0), inset_owner(inset), the_locking_inset(0),
70 need_break_row(0), refresh_y(0), refresh_row(0),
71 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
75 void LyXText::init(BufferView * bview, bool reinit)
78 // Delete all rows, this does not touch the paragraphs!
79 Row * tmprow = firstrow;
81 tmprow = firstrow->next();
85 lastrow = refresh_row = need_break_row = 0;
86 width = height = copylayouttype = 0;
87 number_of_rows = first = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(bview, par, lastrow);
98 setCursorIntern(bview, firstrow->par(), 0);
99 selection.cursor = cursor;
105 // Delete all rows, this does not touch the paragraphs!
106 Row * tmprow = firstrow;
108 tmprow = firstrow->next();
117 LyXFont const realizeFont(LyXFont const & font,
121 LyXFont tmpfont(font);
122 Paragraph::depth_type par_depth = par->getDepth();
124 // Resolve against environment font information
125 while (par && par_depth && !tmpfont.resolved()) {
126 par = par->outerHook();
128 #ifndef INHERIT_LANGUAGE
129 tmpfont.realize(textclasslist.
130 Style(buf->params.textclass,
131 par->getLayout()).font);
133 tmpfont.realize(textclasslist.
134 Style(buf->params.textclass,
135 par->getLayout()).font,
136 buf->params.language);
138 par_depth = par->getDepth();
142 #ifndef INHERIT_LANGUAGE
143 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
145 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
146 buf->params.language);
155 // Gets the fully instantiated font at a given position in a paragraph
156 // Basically the same routine as Paragraph::getFont() in paragraph.C.
157 // The difference is that this one is used for displaying, and thus we
158 // are allowed to make cosmetic improvements. For instance make footnotes
160 // If position is -1, we get the layout font of the paragraph.
161 // If position is -2, we get the font of the manual label of the paragraph.
162 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
165 lyx::Assert(pos >= 0);
167 LyXLayout const & layout =
168 textclasslist.Style(buf->params.textclass, par->getLayout());
170 Paragraph::depth_type par_depth = par->getDepth();
171 // We specialize the 95% common case:
173 if (layout.labeltype == LABEL_MANUAL
174 && pos < beginningOfMainBody(buf, par)) {
176 LyXFont f = par->getFontSettings(buf->params, pos);
178 par->inInset()->getDrawFont(f);
179 #ifndef INHERIT_LANGUAGE
180 return f.realize(layout.reslabelfont);
182 return f.realize(layout.reslabelfont, buf->params.language);
185 LyXFont f = par->getFontSettings(buf->params, pos);
187 par->inInset()->getDrawFont(f);
188 #ifndef INHERIT_LANGUAGE
189 return f.realize(layout.resfont);
191 return f.realize(layout.resfont, buf->params.language);
196 // The uncommon case need not be optimized as much
200 if (pos < beginningOfMainBody(buf, par)) {
202 layoutfont = layout.labelfont;
205 layoutfont = layout.font;
208 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
209 #ifndef INHERIT_LANGUAGE
210 tmpfont.realize(layoutfont);
212 tmpfont.realize(layoutfont, buf->params.language);
215 par->inInset()->getDrawFont(tmpfont);
217 return realizeFont(tmpfont, buf, par);
221 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
223 LyXLayout const & layout =
224 textclasslist.Style(buf->params.textclass, par->getLayout());
226 Paragraph::depth_type par_depth = par->getDepth();
229 return layout.resfont;
232 return realizeFont(layout.font, buf, par);
236 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
238 LyXLayout const & layout =
239 textclasslist.Style(buf->params.textclass, par->getLayout());
241 Paragraph::depth_type par_depth = par->getDepth();
244 return layout.reslabelfont;
247 return realizeFont(layout.labelfont, buf, par);
251 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
252 pos_type pos, LyXFont const & fnt,
255 Buffer const * buf = bv->buffer();
256 LyXFont font = getFont(buf, par, pos);
257 font.update(fnt, buf->params.language, toggleall);
258 // Let the insets convert their font
259 if (par->isInset(pos)) {
260 Inset * inset = par->getInset(pos);
261 if (isEditableInset(inset)) {
262 UpdatableInset * uinset =
263 static_cast<UpdatableInset *>(inset);
264 uinset->setFont(bv, fnt, toggleall, true);
268 LyXLayout const & layout =
269 textclasslist.Style(buf->params.textclass,
272 // Get concrete layout font to reduce against
275 if (pos < beginningOfMainBody(buf, par))
276 layoutfont = layout.labelfont;
278 layoutfont = layout.font;
280 // Realize against environment font information
281 if (par->getDepth()) {
282 Paragraph * tp = par;
283 while (!layoutfont.resolved() && tp && tp->getDepth()) {
284 tp = tp->outerHook();
286 #ifndef INHERIT_LANGUAGE
287 layoutfont.realize(textclasslist.
288 Style(buf->params.textclass,
289 tp->getLayout()).font);
291 layoutfont.realize(textclasslist.
292 Style(buf->params.textclass,
293 tp->getLayout()).font,
294 buf->params.language);
299 #ifndef INHERIT_LANGUAGE
300 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
302 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
303 buf->params.language);
306 // Now, reduce font against full layout font
307 font.reduce(layoutfont);
309 par->setFont(pos, font);
313 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
314 pos_type pos, LyXFont const & fnt)
318 LyXLayout const & layout =
319 textclasslist.Style(buf->params.textclass,
322 // Get concrete layout font to reduce against
325 if (pos < beginningOfMainBody(buf, par))
326 layoutfont = layout.labelfont;
328 layoutfont = layout.font;
330 // Realize against environment font information
331 if (par->getDepth()) {
332 Paragraph * tp = par;
333 while (!layoutfont.resolved() && tp && tp->getDepth()) {
334 tp = tp->outerHook();
336 #ifndef INHERIT_LANGUAGE
337 layoutfont.realize(textclasslist.
338 Style(buf->params.textclass,
339 tp->getLayout()).font);
341 layoutfont.realize(textclasslist.
342 Style(buf->params.textclass,
343 tp->getLayout()).font,
344 buf->params.language);
349 #ifndef INHERIT_LANGUAGE
350 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
352 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
353 buf->params.language);
356 // Now, reduce font against full layout font
357 font.reduce(layoutfont);
359 par->setFont(pos, font);
363 // inserts a new row behind the specified row, increments
364 // the touched counters
365 void LyXText::insertRow(Row * row, Paragraph * par,
368 Row * tmprow = new Row;
371 tmprow->next(firstrow);
374 tmprow->previous(row);
375 tmprow->next(row->next());
380 tmprow->next()->previous(tmprow);
382 if (tmprow->previous())
383 tmprow->previous()->next(tmprow);
395 // removes the row and reset the touched counters
396 void LyXText::removeRow(Row * row) const
398 Row * row_prev = row->previous();
400 row->next()->previous(row_prev);
402 firstrow = row->next();
403 // lyx::Assert(firstrow);
405 row_prev->next(row->next());
407 if (row == lastrow) {
408 lyx::Assert(!row->next());
411 if (refresh_row == row) {
412 refresh_row = row_prev ? row_prev : row->next();
413 // what about refresh_y, refresh_height
416 height -= row->height(); // the text becomes smaller
419 --number_of_rows; // one row less
423 // remove all following rows of the paragraph of the specified row.
424 void LyXText::removeParagraph(Row * row) const
426 Paragraph * tmppar = row->par();
430 while (row && row->par() == tmppar) {
431 tmprow = row->next();
438 // insert the specified paragraph behind the specified row
439 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
442 insertRow(row, par, 0); /* insert a new row, starting
445 setCounter(bview->buffer(), par); // set the counters
447 // and now append the whole paragraph behind the new row
450 appendParagraph(bview, firstrow);
452 row->next()->height(0);
453 appendParagraph(bview, row->next());
458 Inset * LyXText::getInset() const
461 if (cursor.pos() == 0 && cursor.par()->bibkey) {
462 inset = cursor.par()->bibkey;
463 } else if (cursor.pos() < cursor.par()->size()
464 && cursor.par()->isInset(cursor.pos())) {
465 inset = cursor.par()->getInset(cursor.pos());
471 void LyXText::toggleInset(BufferView * bview)
473 Inset * inset = getInset();
474 // is there an editable inset at cursor position?
475 if (!isEditableInset(inset)) {
476 // No, try to see if we are inside a collapsable inset
477 if (inset_owner && inset_owner->owner()
478 && inset_owner->owner()->isOpen()) {
479 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
480 inset_owner->owner()->close(bview);
484 //bview->owner()->message(inset->editMessage());
486 // do we want to keep this?? (JMarc)
487 if (!isHighlyEditableInset(inset))
488 setCursorParUndo(bview);
490 if (inset->isOpen()) {
496 inset->open(bview, !inset->isOpen());
501 /* used in setlayout */
502 // Asger is not sure we want to do this...
503 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
506 LyXLayout const & layout =
507 textclasslist.Style(buf->params.textclass, par->getLayout());
510 for (pos_type pos = 0; pos < par->size(); ++pos) {
511 if (pos < beginningOfMainBody(buf, par))
512 layoutfont = layout.labelfont;
514 layoutfont = layout.font;
516 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
517 tmpfont.reduce(layoutfont);
518 par->setFont(pos, tmpfont);
523 Paragraph * LyXText::setLayout(BufferView * bview,
524 LyXCursor & cur, LyXCursor & sstart_cur,
525 LyXCursor & send_cur,
526 lyx::layout_type layout)
528 Paragraph * endpar = send_cur.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 sstart_cur.par(), undoendpar);
543 // ok we have a selection. This is always between sstart_cur
544 // and sel_end cursor
547 LyXLayout const & lyxlayout =
548 textclasslist.Style(bview->buffer()->params.textclass, layout);
550 while (cur.par() != send_cur.par()) {
551 cur.par()->setLayout(layout);
552 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
553 Paragraph * fppar = cur.par();
554 fppar->params().spaceTop(lyxlayout.fill_top ?
555 VSpace(VSpace::VFILL)
556 : VSpace(VSpace::NONE));
557 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
558 VSpace(VSpace::VFILL)
559 : VSpace(VSpace::NONE));
560 if (lyxlayout.margintype == MARGIN_MANUAL)
561 cur.par()->setLabelWidthString(lyxlayout.labelstring());
562 if (lyxlayout.labeltype != LABEL_BIBLIO
564 delete fppar->bibkey;
567 cur.par(cur.par()->next());
569 cur.par()->setLayout(layout);
570 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
571 Paragraph * fppar = cur.par();
572 fppar->params().spaceTop(lyxlayout.fill_top ?
573 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
574 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
575 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
576 if (lyxlayout.margintype == MARGIN_MANUAL)
577 cur.par()->setLabelWidthString(lyxlayout.labelstring());
578 if (lyxlayout.labeltype != LABEL_BIBLIO
580 delete fppar->bibkey;
587 // set layout over selection and make a total rebreak of those paragraphs
588 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
590 LyXCursor tmpcursor = cursor; /* store the current cursor */
592 // if there is no selection just set the layout
593 // of the current paragraph */
594 if (!selection.set()) {
595 selection.start = cursor; // dummy selection
596 selection.end = cursor;
598 Paragraph * endpar = setLayout(bview, cursor, selection.start,
599 selection.end, layout);
600 redoParagraphs(bview, selection.start, endpar);
602 // we have to reset the selection, because the
603 // geometry could have changed
604 setCursor(bview, selection.start.par(),
605 selection.start.pos(), false);
606 selection.cursor = cursor;
607 setCursor(bview, selection.end.par(), selection.end.pos(), false);
608 updateCounters(bview, cursor.row());
611 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
615 // increment depth over selection and
616 // make a total rebreak of those paragraphs
617 void LyXText::incDepth(BufferView * bview)
619 // If there is no selection, just use the current paragraph
620 if (!selection.set()) {
621 selection.start = cursor; // dummy selection
622 selection.end = cursor;
625 // We end at the next paragraph with depth 0
626 Paragraph * endpar = selection.end.par()->next();
628 Paragraph * undoendpar = endpar;
630 if (endpar && endpar->getDepth()) {
631 while (endpar && endpar->getDepth()) {
632 endpar = endpar->next();
636 endpar = endpar->next(); // because of parindents etc.
639 setUndo(bview, Undo::EDIT,
640 selection.start.par(), undoendpar);
642 LyXCursor tmpcursor = cursor; // store the current cursor
644 // ok we have a selection. This is always between sel_start_cursor
645 // and sel_end cursor
646 cursor = selection.start;
648 bool anything_changed = false;
651 // NOTE: you can't change the depth of a bibliography entry
652 if (textclasslist.Style(bview->buffer()->params.textclass,
653 cursor.par()->getLayout()).labeltype != LABEL_BIBLIO) {
654 Paragraph * prev = cursor.par()->previous();
658 = prev->getDepth() - cursor.par()->getDepth();
660 // (1) the previous para is already
661 // deeper (depth_diff > 0)
662 // (2) the previous para is a
663 // list-environment at the same
664 // depth as this para.
665 if (depth_diff > 0 || (depth_diff > -1
666 && textclasslist.Style(bview->buffer()->params.textclass,
667 prev->getLayout()).isEnvironment())) {
668 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
669 anything_changed = true;
673 if (cursor.par() == selection.end.par())
675 cursor.par(cursor.par()->next());
678 // if nothing changed set all depth to 0
679 if (!anything_changed) {
680 cursor = selection.start;
681 while (cursor.par() != selection.end.par()) {
682 cursor.par()->params().depth(0);
683 cursor.par(cursor.par()->next());
685 cursor.par()->params().depth(0);
688 redoParagraphs(bview, selection.start, endpar);
690 // we have to reset the selection, because the
691 // geometry could have changed
692 setCursor(bview, selection.start.par(), selection.start.pos());
693 selection.cursor = cursor;
694 setCursor(bview, selection.end.par(), selection.end.pos());
695 updateCounters(bview, cursor.row());
698 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
702 // decrement depth over selection and
703 // make a total rebreak of those paragraphs
704 void LyXText::decDepth(BufferView * bview)
706 // if there is no selection just set the layout
707 // of the current paragraph
708 if (!selection.set()) {
709 selection.start = cursor; // dummy selection
710 selection.end = cursor;
712 Paragraph * endpar = selection.end.par()->next();
713 Paragraph * undoendpar = endpar;
715 if (endpar && endpar->getDepth()) {
716 while (endpar && endpar->getDepth()) {
717 endpar = endpar->next();
721 endpar = endpar->next(); // because of parindents etc.
724 setUndo(bview, Undo::EDIT,
725 selection.start.par(), undoendpar);
727 LyXCursor tmpcursor = cursor; // store the current cursor
729 // ok we have a selection. This is always between sel_start_cursor
730 // and sel_end cursor
731 cursor = selection.start;
734 if (cursor.par()->params().depth()) {
735 cursor.par()->params()
736 .depth(cursor.par()->params().depth() - 1);
738 if (cursor.par() == selection.end.par()) {
741 cursor.par(cursor.par()->next());
744 redoParagraphs(bview, selection.start, endpar);
746 // we have to reset the selection, because the
747 // geometry could have changed
748 setCursor(bview, selection.start.par(),
749 selection.start.pos());
750 selection.cursor = cursor;
751 setCursor(bview, selection.end.par(), selection.end.pos());
752 updateCounters(bview, cursor.row());
755 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
759 // set font over selection and make a total rebreak of those paragraphs
760 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
762 // if there is no selection just set the current_font
763 if (!selection.set()) {
764 // Determine basis font
766 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
768 layoutfont = getLabelFont(bview->buffer(),
771 layoutfont = getLayoutFont(bview->buffer(),
774 // Update current font
775 real_current_font.update(font,
776 bview->buffer()->params.language,
779 // Reduce to implicit settings
780 current_font = real_current_font;
781 current_font.reduce(layoutfont);
782 // And resolve it completely
783 #ifndef INHERIT_LANGUAGE
784 real_current_font.realize(layoutfont);
786 real_current_font.realize(layoutfont,
787 bview->buffer()->params.language);
792 LyXCursor tmpcursor = cursor; // store the current cursor
794 // ok we have a selection. This is always between sel_start_cursor
795 // and sel_end cursor
797 setUndo(bview, Undo::EDIT,
798 selection.start.par(), selection.end.par()->next());
800 cursor = selection.start;
801 while (cursor.par() != selection.end.par() ||
802 (cursor.pos() < selection.end.pos()))
804 if (cursor.pos() < cursor.par()->size()) {
805 // an open footnote should behave
807 setCharFont(bview, cursor.par(), cursor.pos(),
809 cursor.pos(cursor.pos() + 1);
812 cursor.par(cursor.par()->next());
817 redoParagraphs(bview, selection.start, selection.end.par()->next());
819 // we have to reset the selection, because the
820 // geometry could have changed, but we keep
821 // it for user convenience
822 setCursor(bview, selection.start.par(), selection.start.pos());
823 selection.cursor = cursor;
824 setCursor(bview, selection.end.par(), selection.end.pos());
826 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
827 tmpcursor.boundary());
831 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
833 Row * tmprow = cur.row();
834 int y = cur.y() - tmprow->baseline();
836 setHeightOfRow(bview, tmprow);
838 while (tmprow->previous()
839 && tmprow->previous()->par() == tmprow->par()) {
840 tmprow = tmprow->previous();
841 y -= tmprow->height();
842 setHeightOfRow(bview, tmprow);
845 // we can set the refreshing parameters now
846 status(bview, LyXText::NEED_MORE_REFRESH);
848 refresh_row = tmprow;
849 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
853 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
855 Row * tmprow = cur.row();
857 int y = cur.y() - tmprow->baseline();
858 setHeightOfRow(bview, tmprow);
860 while (tmprow->previous()
861 && tmprow->previous()->par() == tmprow->par()) {
862 tmprow = tmprow->previous();
863 y -= tmprow->height();
866 // we can set the refreshing parameters now
867 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
869 refresh_row = tmprow;
871 status(bview, LyXText::NEED_MORE_REFRESH);
872 setCursor(bview, cur.par(), cur.pos());
876 // deletes and inserts again all paragaphs between the cursor
877 // and the specified par
878 // This function is needed after SetLayout and SetFont etc.
879 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
880 Paragraph const * endpar) const
883 Paragraph * tmppar = 0;
884 Paragraph * first_phys_par = 0;
886 Row * tmprow = cur.row();
888 int y = cur.y() - tmprow->baseline();
890 if (!tmprow->previous()) {
891 // a trick/hack for UNDO
892 // This is needed because in an UNDO/REDO we could have changed
893 // the ownerParagrah() so the paragraph inside the row is NOT
894 // my really first par anymore. Got it Lars ;) (Jug 20011206)
895 first_phys_par = ownerParagraph();
897 first_phys_par = tmprow->par();
898 while (tmprow->previous()
899 && tmprow->previous()->par() == first_phys_par)
901 tmprow = tmprow->previous();
902 y -= tmprow->height();
906 // we can set the refreshing parameters now
907 status(bview, LyXText::NEED_MORE_REFRESH);
909 refresh_row = tmprow->previous(); /* the real refresh row will
910 be deleted, so I store
914 tmppar = tmprow->next()->par();
917 while (tmprow->next() && tmppar != endpar) {
918 removeRow(tmprow->next());
919 if (tmprow->next()) {
920 tmppar = tmprow->next()->par();
926 // remove the first one
927 tmprow2 = tmprow; /* this is because tmprow->previous()
929 tmprow = tmprow->previous();
932 tmppar = first_phys_par;
936 insertParagraph(bview, tmppar, tmprow);
940 while (tmprow->next()
941 && tmprow->next()->par() == tmppar) {
942 tmprow = tmprow->next();
944 tmppar = tmppar->next();
946 } while (tmppar && tmppar != endpar);
948 // this is because of layout changes
950 refresh_y -= refresh_row->height();
951 setHeightOfRow(bview, refresh_row);
953 refresh_row = firstrow;
955 setHeightOfRow(bview, refresh_row);
958 if (tmprow && tmprow->next())
959 setHeightOfRow(bview, tmprow->next());
963 bool LyXText::fullRebreak(BufferView * bview)
969 if (need_break_row) {
970 breakAgain(bview, need_break_row);
978 // important for the screen
981 /* the cursor set functions have a special mechanism. When they
982 * realize, that you left an empty paragraph, they will delete it.
983 * They also delete the corresponding row */
985 // need the selection cursor:
986 void LyXText::setSelection(BufferView * bview)
988 bool const lsel = selection.set();
990 if (!selection.set()) {
991 last_sel_cursor = selection.cursor;
992 selection.start = selection.cursor;
993 selection.end = selection.cursor;
998 // first the toggling area
999 if (cursor.y() < last_sel_cursor.y()
1000 || (cursor.y() == last_sel_cursor.y()
1001 && cursor.x() < last_sel_cursor.x())) {
1002 toggle_end_cursor = last_sel_cursor;
1003 toggle_cursor = cursor;
1005 toggle_end_cursor = cursor;
1006 toggle_cursor = last_sel_cursor;
1009 last_sel_cursor = cursor;
1011 // and now the whole selection
1013 if (selection.cursor.par() == cursor.par())
1014 if (selection.cursor.pos() < cursor.pos()) {
1015 selection.end = cursor;
1016 selection.start = selection.cursor;
1018 selection.end = selection.cursor;
1019 selection.start = cursor;
1021 else if (selection.cursor.y() < cursor.y() ||
1022 (selection.cursor.y() == cursor.y()
1023 && selection.cursor.x() < cursor.x())) {
1024 selection.end = cursor;
1025 selection.start = selection.cursor;
1028 selection.end = selection.cursor;
1029 selection.start = cursor;
1032 // a selection with no contents is not a selection
1033 if (selection.start.par() == selection.end.par() &&
1034 selection.start.pos() == selection.end.pos())
1035 selection.set(false);
1037 if (inset_owner && (selection.set() || lsel))
1038 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1042 string const LyXText::selectionAsString(Buffer const * buffer,
1045 if (!selection.set()) return string();
1048 // Special handling if the whole selection is within one paragraph
1049 if (selection.start.par() == selection.end.par()) {
1050 result += selection.start.par()->asString(buffer,
1051 selection.start.pos(),
1052 selection.end.pos(),
1057 // The selection spans more than one paragraph
1059 // First paragraph in selection
1060 result += selection.start.par()->asString(buffer,
1061 selection.start.pos(),
1062 selection.start.par()->size(),
1066 // The paragraphs in between (if any)
1067 LyXCursor tmpcur(selection.start);
1068 tmpcur.par(tmpcur.par()->next());
1069 while (tmpcur.par() != selection.end.par()) {
1070 result += tmpcur.par()->asString(buffer, 0,
1071 tmpcur.par()->size(),
1073 tmpcur.par(tmpcur.par()->next());
1076 // Last paragraph in selection
1077 result += selection.end.par()->asString(buffer, 0,
1078 selection.end.pos(), label);
1084 void LyXText::clearSelection() const
1086 selection.set(false);
1087 selection.mark(false);
1088 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1092 void LyXText::cursorHome(BufferView * bview) const
1094 setCursor(bview, cursor.par(), cursor.row()->pos());
1098 void LyXText::cursorEnd(BufferView * bview) const
1100 if (!cursor.row()->next()
1101 || cursor.row()->next()->par() != cursor.row()->par()) {
1102 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1104 if (cursor.par()->size() &&
1105 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1106 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1107 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1109 setCursor(bview,cursor.par(),
1110 rowLast(cursor.row()) + 1);
1116 void LyXText::cursorTop(BufferView * bview) const
1118 while (cursor.par()->previous())
1119 cursor.par(cursor.par()->previous());
1120 setCursor(bview, cursor.par(), 0);
1124 void LyXText::cursorBottom(BufferView * bview) const
1126 while (cursor.par()->next())
1127 cursor.par(cursor.par()->next());
1128 setCursor(bview, cursor.par(), cursor.par()->size());
1132 void LyXText::toggleFree(BufferView * bview,
1133 LyXFont const & font, bool toggleall)
1135 // If the mask is completely neutral, tell user
1136 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1137 // Could only happen with user style
1138 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1142 // Try implicit word selection
1143 // If there is a change in the language the implicit word selection
1145 LyXCursor resetCursor = cursor;
1146 bool implicitSelection = (font.language() == ignore_language
1147 && font.number() == LyXFont::IGNORE)
1148 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1151 setFont(bview, font, toggleall);
1153 // Implicit selections are cleared afterwards
1154 //and cursor is set to the original position.
1155 if (implicitSelection) {
1157 cursor = resetCursor;
1158 setCursor(bview, cursor.par(), cursor.pos());
1159 selection.cursor = cursor;
1162 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1166 string LyXText::getStringToIndex(BufferView * bview)
1170 // Try implicit word selection
1171 // If there is a change in the language the implicit word selection
1173 LyXCursor resetCursor = cursor;
1174 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1176 if (!selection.set()) {
1177 bview->owner()->message(_("Nothing to index!"));
1180 if (selection.start.par() != selection.end.par()) {
1181 bview->owner()->message(_("Cannot index more than one paragraph!"));
1185 idxstring = selectionAsString(bview->buffer(), false);
1187 // Implicit selections are cleared afterwards
1188 //and cursor is set to the original position.
1189 if (implicitSelection) {
1191 cursor = resetCursor;
1192 setCursor(bview, cursor.par(), cursor.pos());
1193 selection.cursor = cursor;
1199 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1200 Paragraph const * par) const
1202 if (textclasslist.Style(buf->params.textclass,
1203 par->getLayout()).labeltype != LABEL_MANUAL)
1206 return par->beginningOfMainBody();
1210 /* the DTP switches for paragraphs. LyX will store them in the
1211 * first physicla paragraph. When a paragraph is broken, the top settings
1212 * rest, the bottom settings are given to the new one. So I can make shure,
1213 * they do not duplicate themself and you cannnot make dirty things with
1216 void LyXText::setParagraph(BufferView * bview,
1217 bool line_top, bool line_bottom,
1218 bool pagebreak_top, bool pagebreak_bottom,
1219 VSpace const & space_top,
1220 VSpace const & space_bottom,
1221 Spacing const & spacing,
1223 string labelwidthstring,
1226 LyXCursor tmpcursor = cursor;
1227 if (!selection.set()) {
1228 selection.start = cursor;
1229 selection.end = cursor;
1232 // make sure that the depth behind the selection are restored, too
1233 Paragraph * endpar = selection.end.par()->next();
1234 Paragraph * undoendpar = endpar;
1236 if (endpar && endpar->getDepth()) {
1237 while (endpar && endpar->getDepth()) {
1238 endpar = endpar->next();
1239 undoendpar = endpar;
1243 // because of parindents etc.
1244 endpar = endpar->next();
1247 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1250 Paragraph * tmppar = selection.end.par();
1251 while (tmppar != selection.start.par()->previous()) {
1252 setCursor(bview, tmppar, 0);
1253 status(bview, LyXText::NEED_MORE_REFRESH);
1254 refresh_row = cursor.row();
1255 refresh_y = cursor.y() - cursor.row()->baseline();
1256 cursor.par()->params().lineTop(line_top);
1257 cursor.par()->params().lineBottom(line_bottom);
1258 cursor.par()->params().pagebreakTop(pagebreak_top);
1259 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1260 cursor.par()->params().spaceTop(space_top);
1261 cursor.par()->params().spaceBottom(space_bottom);
1262 cursor.par()->params().spacing(spacing);
1263 // does the layout allow the new alignment?
1264 if (align == LYX_ALIGN_LAYOUT)
1265 align = textclasslist
1266 .Style(bview->buffer()->params.textclass,
1267 cursor.par()->getLayout()).align;
1268 if (align & textclasslist
1269 .Style(bview->buffer()->params.textclass,
1270 cursor.par()->getLayout()).alignpossible) {
1271 if (align == textclasslist
1272 .Style(bview->buffer()->params.textclass,
1273 cursor.par()->getLayout()).align)
1274 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1276 cursor.par()->params().align(align);
1278 cursor.par()->setLabelWidthString(labelwidthstring);
1279 cursor.par()->params().noindent(noindent);
1280 tmppar = cursor.par()->previous();
1283 redoParagraphs(bview, selection.start, endpar);
1286 setCursor(bview, selection.start.par(), selection.start.pos());
1287 selection.cursor = cursor;
1288 setCursor(bview, selection.end.par(), selection.end.pos());
1289 setSelection(bview);
1290 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1292 bview->updateInset(inset_owner, true);
1296 char loweralphaCounter(int n)
1298 if (n < 1 || n > 26)
1308 char alphaCounter(int n)
1310 if (n < 1 || n > 26)
1318 char hebrewCounter(int n)
1320 static const char hebrew[22] = {
1321 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1322 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1323 '÷', 'ø', 'ù', 'ú'
1325 if (n < 1 || n > 22)
1333 string const romanCounter(int n)
1335 static char const * roman[20] = {
1336 "i", "ii", "iii", "iv", "v",
1337 "vi", "vii", "viii", "ix", "x",
1338 "xi", "xii", "xiii", "xiv", "xv",
1339 "xvi", "xvii", "xviii", "xix", "xx"
1341 if (n < 1 || n > 20)
1350 // set the counter of a paragraph. This includes the labels
1351 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1353 LyXLayout const & layout =
1354 textclasslist.Style(buf->params.textclass,
1357 LyXTextClass const & textclass =
1358 textclasslist.TextClass(buf->params.textclass);
1360 // copy the prev-counters to this one,
1361 // unless this is the first paragraph
1362 if (par->previous()) {
1363 for (int i = 0; i < 10; ++i) {
1364 par->setCounter(i, par->previous()->getFirstCounter(i));
1366 par->params().appendix(par->previous()->params().appendix());
1367 if (!par->params().appendix() && par->params().startOfAppendix()) {
1368 par->params().appendix(true);
1369 for (int i = 0; i < 10; ++i) {
1370 par->setCounter(i, 0);
1373 par->enumdepth = par->previous()->enumdepth;
1374 par->itemdepth = par->previous()->itemdepth;
1376 for (int i = 0; i < 10; ++i) {
1377 par->setCounter(i, 0);
1379 par->params().appendix(par->params().startOfAppendix());
1384 /* Maybe we have to increment the enumeration depth.
1385 * BUT, enumeration in a footnote is considered in isolation from its
1386 * surrounding paragraph so don't increment if this is the
1387 * first line of the footnote
1388 * AND, bibliographies can't have their depth changed ie. they
1389 * are always of depth 0
1392 && par->previous()->getDepth() < par->getDepth()
1393 && textclasslist.Style(buf->params.textclass,
1394 par->previous()->getLayout()
1395 ).labeltype == LABEL_COUNTER_ENUMI
1396 && par->enumdepth < 3
1397 && layout.labeltype != LABEL_BIBLIO) {
1401 // Maybe we have to decrement the enumeration depth, see note above
1403 && par->previous()->getDepth() > par->getDepth()
1404 && layout.labeltype != LABEL_BIBLIO) {
1405 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1406 par->setCounter(6 + par->enumdepth,
1407 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1408 /* reset the counters.
1409 * A depth change is like a breaking layout
1411 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1412 par->setCounter(i, 0);
1415 if (!par->params().labelString().empty()) {
1416 par->params().labelString(string());
1419 if (layout.margintype == MARGIN_MANUAL) {
1420 if (par->params().labelWidthString().empty()) {
1421 par->setLabelWidthString(layout.labelstring());
1424 par->setLabelWidthString(string());
1427 // is it a layout that has an automatic label?
1428 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1430 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1431 if (i >= 0 && i<= buf->params.secnumdepth) {
1432 par->incCounter(i); // increment the counter
1434 // Is there a label? Useful for Chapter layout
1435 if (!par->params().appendix()) {
1436 if (!layout.labelstring().empty())
1437 par->params().labelString(layout.labelstring());
1439 par->params().labelString(string());
1441 if (!layout.labelstring_appendix().empty())
1442 par->params().labelString(layout.labelstring_appendix());
1444 par->params().labelString(string());
1449 if (!par->params().appendix()) {
1450 switch (2 * LABEL_COUNTER_CHAPTER -
1451 textclass.maxcounter() + i) {
1452 case LABEL_COUNTER_CHAPTER:
1453 s << par->getCounter(i);
1455 case LABEL_COUNTER_SECTION:
1456 s << par->getCounter(i - 1) << '.'
1457 << par->getCounter(i);
1459 case LABEL_COUNTER_SUBSECTION:
1460 s << par->getCounter(i - 2) << '.'
1461 << par->getCounter(i - 1) << '.'
1462 << par->getCounter(i);
1464 case LABEL_COUNTER_SUBSUBSECTION:
1465 s << par->getCounter(i - 3) << '.'
1466 << par->getCounter(i - 2) << '.'
1467 << par->getCounter(i - 1) << '.'
1468 << par->getCounter(i);
1471 case LABEL_COUNTER_PARAGRAPH:
1472 s << par->getCounter(i - 4) << '.'
1473 << par->getCounter(i - 3) << '.'
1474 << par->getCounter(i - 2) << '.'
1475 << par->getCounter(i - 1) << '.'
1476 << par->getCounter(i);
1478 case LABEL_COUNTER_SUBPARAGRAPH:
1479 s << par->getCounter(i - 5) << '.'
1480 << par->getCounter(i - 4) << '.'
1481 << par->getCounter(i - 3) << '.'
1482 << par->getCounter(i - 2) << '.'
1483 << par->getCounter(i - 1) << '.'
1484 << par->getCounter(i);
1488 // Can this ever be reached? And in the
1489 // case it is, how can this be correct?
1491 s << par->getCounter(i) << '.';
1494 } else { // appendix
1495 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1496 case LABEL_COUNTER_CHAPTER:
1497 if (par->isRightToLeftPar(buf->params))
1498 s << hebrewCounter(par->getCounter(i));
1500 s << alphaCounter(par->getCounter(i));
1502 case LABEL_COUNTER_SECTION:
1503 if (par->isRightToLeftPar(buf->params))
1504 s << hebrewCounter(par->getCounter(i - 1));
1506 s << alphaCounter(par->getCounter(i - 1));
1509 << par->getCounter(i);
1512 case LABEL_COUNTER_SUBSECTION:
1513 if (par->isRightToLeftPar(buf->params))
1514 s << hebrewCounter(par->getCounter(i - 2));
1516 s << alphaCounter(par->getCounter(i - 2));
1519 << par->getCounter(i-1) << '.'
1520 << par->getCounter(i);
1523 case LABEL_COUNTER_SUBSUBSECTION:
1524 if (par->isRightToLeftPar(buf->params))
1525 s << hebrewCounter(par->getCounter(i-3));
1527 s << alphaCounter(par->getCounter(i-3));
1530 << par->getCounter(i-2) << '.'
1531 << par->getCounter(i-1) << '.'
1532 << par->getCounter(i);
1535 case LABEL_COUNTER_PARAGRAPH:
1536 if (par->isRightToLeftPar(buf->params))
1537 s << hebrewCounter(par->getCounter(i-4));
1539 s << alphaCounter(par->getCounter(i-4));
1542 << par->getCounter(i-3) << '.'
1543 << par->getCounter(i-2) << '.'
1544 << par->getCounter(i-1) << '.'
1545 << par->getCounter(i);
1548 case LABEL_COUNTER_SUBPARAGRAPH:
1549 if (par->isRightToLeftPar(buf->params))
1550 s << hebrewCounter(par->getCounter(i-5));
1552 s << alphaCounter(par->getCounter(i-5));
1555 << par->getCounter(i-4) << '.'
1556 << par->getCounter(i-3) << '.'
1557 << par->getCounter(i-2) << '.'
1558 << par->getCounter(i-1) << '.'
1559 << par->getCounter(i);
1563 // Can this ever be reached? And in the
1564 // case it is, how can this be correct?
1566 s << par->getCounter(i) << '.';
1572 par->params().labelString(par->params().labelString() +s.str().c_str());
1573 // We really want to remove the c_str as soon as
1576 for (i++; i < 10; ++i) {
1577 // reset the following counters
1578 par->setCounter(i, 0);
1580 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1581 for (i++; i < 10; ++i) {
1582 // reset the following counters
1583 par->setCounter(i, 0);
1585 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1586 par->incCounter(i + par->enumdepth);
1587 int number = par->getCounter(i + par->enumdepth);
1591 switch (par->enumdepth) {
1593 if (par->isRightToLeftPar(buf->params))
1595 << hebrewCounter(number)
1599 << loweralphaCounter(number)
1603 if (par->isRightToLeftPar(buf->params))
1604 s << '.' << romanCounter(number);
1606 s << romanCounter(number) << '.';
1609 if (par->isRightToLeftPar(buf->params))
1611 << alphaCounter(number);
1613 s << alphaCounter(number)
1617 if (par->isRightToLeftPar(buf->params))
1624 par->params().labelString(s.str().c_str());
1626 for (i += par->enumdepth + 1; i < 10; ++i) {
1627 // reset the following counters
1628 par->setCounter(i, 0);
1632 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1633 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1635 int number = par->getCounter(i);
1637 InsetCommandParams p("bibitem" );
1638 par->bibkey = new InsetBibKey(p);
1640 par->bibkey->setCounter(number);
1641 par->params().labelString(layout.labelstring());
1643 // In biblio should't be following counters but...
1645 string s = layout.labelstring();
1647 // the caption hack:
1648 if (layout.labeltype == LABEL_SENSITIVE) {
1649 bool isOK (par->inInset() && par->inInset()->owner() &&
1650 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1653 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1655 = floatList.getType(tmp->type());
1656 // We should get the correct number here too.
1657 s = fl.name() + " #:";
1659 /* par->SetLayout(0);
1660 s = layout->labelstring; */
1661 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1662 ? " :úåòîùî øñç" : "Senseless: ";
1665 par->params().labelString(s);
1667 /* reset the enumeration counter. They are always resetted
1668 * when there is any other layout between */
1669 for (int i = 6 + par->enumdepth; i < 10; ++i)
1670 par->setCounter(i, 0);
1675 // Updates all counters BEHIND the row. Changed paragraphs
1676 // with a dynamic left margin will be rebroken.
1677 void LyXText::updateCounters(BufferView * bview, Row * row) const
1685 par = row->par()->next();
1689 while (row->par() != par)
1692 setCounter(bview->buffer(), par);
1694 // now check for the headline layouts. remember that they
1695 // have a dynamic left margin
1696 if ((textclasslist.Style(bview->buffer()->params.textclass,
1697 par->layout).margintype == MARGIN_DYNAMIC
1698 || textclasslist.Style(bview->buffer()->params.textclass,
1699 par->layout).labeltype == LABEL_SENSITIVE)) {
1701 // Rebreak the paragraph
1702 removeParagraph(row);
1703 appendParagraph(bview, row);
1710 void LyXText::insertInset(BufferView * bview, Inset * inset)
1712 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1714 // I don't know if this is necessary here (Jug 20020102)
1715 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1716 cursor.par()->insertInset(cursor.pos(), inset);
1717 // Just to rebreak and refresh correctly.
1718 // The character will not be inserted a second time
1719 insertChar(bview, Paragraph::META_INSET);
1721 // If we enter a highly editable inset the cursor should be to before
1722 // the inset. This couldn't happen before as Undo was not handled inside
1723 // inset now after the Undo LyX tries to call inset->Edit(...) again
1724 // and cannot do this as the cursor is behind the inset and GetInset
1725 // does not return the inset!
1726 if (isHighlyEditableInset(inset)) {
1727 cursorLeft(bview, true);
1733 void LyXText::copyEnvironmentType()
1735 copylayouttype = cursor.par()->getLayout();
1739 void LyXText::pasteEnvironmentType(BufferView * bview)
1741 setLayout(bview, copylayouttype);
1745 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1747 // Stuff what we got on the clipboard. Even if there is no selection.
1749 // There is a problem with having the stuffing here in that the
1750 // larger the selection the slower LyX will get. This can be
1751 // solved by running the line below only when the selection has
1752 // finished. The solution used currently just works, to make it
1753 // faster we need to be more clever and probably also have more
1754 // calls to stuffClipboard. (Lgb)
1755 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1757 // This doesn't make sense, if there is no selection
1758 if (!selection.set())
1761 // OK, we have a selection. This is always between selection.start
1762 // and selection.end
1764 // make sure that the depth behind the selection are restored, too
1765 Paragraph * endpar = selection.end.par()->next();
1766 Paragraph * undoendpar = endpar;
1768 if (endpar && endpar->getDepth()) {
1769 while (endpar && endpar->getDepth()) {
1770 endpar = endpar->next();
1771 undoendpar = endpar;
1773 } else if (endpar) {
1774 endpar = endpar->next(); // because of parindents etc.
1777 setUndo(bview, Undo::DELETE,
1778 selection.start.par(), undoendpar);
1780 // there are two cases: cut only within one paragraph or
1781 // more than one paragraph
1782 if (selection.start.par() == selection.end.par()) {
1783 // only within one paragraph
1784 endpar = selection.end.par();
1785 int pos = selection.end.pos();
1786 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1787 selection.start.pos(), pos,
1788 bview->buffer()->params.textclass,
1790 selection.end.pos(pos);
1792 endpar = selection.end.par();
1793 int pos = selection.end.pos();
1794 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1795 selection.start.pos(), pos,
1796 bview->buffer()->params.textclass,
1799 selection.end.par(endpar);
1800 selection.end.pos(pos);
1801 cursor.pos(selection.end.pos());
1803 endpar = endpar->next();
1805 // sometimes necessary
1807 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1809 redoParagraphs(bview, selection.start, endpar);
1811 // cutSelection can invalidate the cursor so we need to set
1813 cursor = selection.start;
1815 // need a valid cursor. (Lgb)
1818 setCursor(bview, cursor.par(), cursor.pos());
1819 selection.cursor = cursor;
1820 updateCounters(bview, cursor.row());
1824 void LyXText::copySelection(BufferView * bview)
1826 // stuff the selection onto the X clipboard, from an explicit copy request
1827 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1829 // this doesnt make sense, if there is no selection
1830 if (!selection.set())
1833 // ok we have a selection. This is always between selection.start
1834 // and sel_end cursor
1836 // copy behind a space if there is one
1837 while (selection.start.par()->size() > selection.start.pos()
1838 && selection.start.par()->isLineSeparator(selection.start.pos())
1839 && (selection.start.par() != selection.end.par()
1840 || selection.start.pos() < selection.end.pos()))
1841 selection.start.pos(selection.start.pos() + 1);
1843 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1844 selection.start.pos(), selection.end.pos(),
1845 bview->buffer()->params.textclass);
1849 void LyXText::pasteSelection(BufferView * bview)
1851 // this does not make sense, if there is nothing to paste
1852 if (!CutAndPaste::checkPastePossible(cursor.par()))
1855 setUndo(bview, Undo::INSERT,
1856 cursor.par(), cursor.par()->next());
1859 Paragraph * actpar = cursor.par();
1860 int pos = cursor.pos();
1862 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1863 bview->buffer()->params.textclass);
1865 redoParagraphs(bview, cursor, endpar);
1867 setCursor(bview, cursor.par(), cursor.pos());
1870 setCursor(bview, actpar, pos);
1871 updateCounters(bview, cursor.row());
1875 // sets the selection over the number of characters of string, no check!!
1876 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1881 selection.cursor = cursor;
1882 for (string::size_type i = 0; i < str.length(); ++i)
1884 setSelection(bview);
1888 // simple replacing. The font of the first selected character is used
1889 void LyXText::replaceSelectionWithString(BufferView * bview,
1892 setCursorParUndo(bview);
1895 if (!selection.set()) { // create a dummy selection
1896 selection.end = cursor;
1897 selection.start = cursor;
1900 // Get font setting before we cut
1901 pos_type pos = selection.end.pos();
1902 LyXFont const font = selection.start.par()
1903 ->getFontSettings(bview->buffer()->params,
1904 selection.start.pos());
1906 // Insert the new string
1907 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1908 selection.end.par()->insertChar(pos, (*cit), font);
1912 // Cut the selection
1913 cutSelection(bview, true, false);
1919 // needed to insert the selection
1920 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1922 Paragraph * par = cursor.par();
1923 pos_type pos = cursor.pos();
1924 Paragraph * endpar = cursor.par()->next();
1926 setCursorParUndo(bview);
1928 // only to be sure, should not be neccessary
1931 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1933 redoParagraphs(bview, cursor, endpar);
1934 setCursor(bview, cursor.par(), cursor.pos());
1935 selection.cursor = cursor;
1936 setCursor(bview, par, pos);
1937 setSelection(bview);
1941 // turns double-CR to single CR, others where converted into one
1942 // blank. Then InsertStringAsLines is called
1943 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1945 string linestr(str);
1946 bool newline_inserted = false;
1947 for (string::size_type i = 0; i < linestr.length(); ++i) {
1948 if (linestr[i] == '\n') {
1949 if (newline_inserted) {
1950 // we know that \r will be ignored by
1951 // InsertStringA. Of course, it is a dirty
1952 // trick, but it works...
1953 linestr[i - 1] = '\r';
1957 newline_inserted = true;
1959 } else if (IsPrintable(linestr[i])) {
1960 newline_inserted = false;
1963 insertStringAsLines(bview, linestr);
1967 bool LyXText::gotoNextInset(BufferView * bview,
1968 vector<Inset::Code> const & codes,
1969 string const & contents) const
1971 LyXCursor res = cursor;
1974 if (res.pos() < res.par()->size() - 1) {
1975 res.pos(res.pos() + 1);
1977 res.par(res.par()->next());
1981 } while (res.par() &&
1982 !(res.par()->isInset(res.pos())
1983 && (inset = res.par()->getInset(res.pos())) != 0
1984 && find(codes.begin(), codes.end(), inset->lyxCode())
1986 && (contents.empty() ||
1987 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1991 setCursor(bview, res.par(), res.pos(), false);
1998 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
2001 LyXCursor tmpcursor;
2005 Row * row = getRow(par, pos, y);
2007 // is there a break one row above
2008 if (row->previous() && row->previous()->par() == row->par()) {
2009 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
2010 if (z >= row->pos()) {
2011 // set the dimensions of the row above
2012 y -= row->previous()->height();
2014 refresh_row = row->previous();
2015 status(bview, LyXText::NEED_MORE_REFRESH);
2017 breakAgain(bview, row->previous());
2019 // set the cursor again. Otherwise
2020 // dangling pointers are possible
2021 setCursor(bview, cursor.par(), cursor.pos(),
2022 false, cursor.boundary());
2023 selection.cursor = cursor;
2028 int const tmpheight = row->height();
2029 pos_type const tmplast = rowLast(row);
2033 breakAgain(bview, row);
2034 if (row->height() == tmpheight && rowLast(row) == tmplast)
2035 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2037 status(bview, LyXText::NEED_MORE_REFRESH);
2039 // check the special right address boxes
2040 if (textclasslist.Style(bview->buffer()->params.textclass,
2041 par->getLayout()).margintype
2042 == MARGIN_RIGHT_ADDRESS_BOX)
2050 redoDrawingOfParagraph(bview, tmpcursor);
2053 // set the cursor again. Otherwise dangling pointers are possible
2054 // also set the selection
2056 if (selection.set()) {
2058 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2059 false, selection.cursor.boundary());
2060 selection.cursor = cursor;
2061 setCursorIntern(bview, selection.start.par(),
2062 selection.start.pos(),
2063 false, selection.start.boundary());
2064 selection.start = cursor;
2065 setCursorIntern(bview, selection.end.par(),
2066 selection.end.pos(),
2067 false, selection.end.boundary());
2068 selection.end = cursor;
2069 setCursorIntern(bview, last_sel_cursor.par(),
2070 last_sel_cursor.pos(),
2071 false, last_sel_cursor.boundary());
2072 last_sel_cursor = cursor;
2075 setCursorIntern(bview, cursor.par(), cursor.pos(),
2076 false, cursor.boundary());
2080 // returns false if inset wasn't found
2081 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2083 // first check the current paragraph
2084 int pos = cursor.par()->getPositionOfInset(inset);
2086 checkParagraph(bview, cursor.par(), pos);
2090 // check every paragraph
2092 Paragraph * par = ownerParagraph();
2094 pos = par->getPositionOfInset(inset);
2096 checkParagraph(bview, par, pos);
2106 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2108 bool setfont, bool boundary) const
2110 LyXCursor old_cursor = cursor;
2111 setCursorIntern(bview, par, pos, setfont, boundary);
2112 return deleteEmptyParagraphMechanism(bview, old_cursor);
2116 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2117 pos_type pos, bool boundary) const
2124 cur.boundary(boundary);
2126 // get the cursor y position in text
2128 Row * row = getRow(par, pos, y);
2129 // y is now the beginning of the cursor row
2130 y += row->baseline();
2131 // y is now the cursor baseline
2134 // now get the cursors x position
2136 float fill_separator;
2138 float fill_label_hfill;
2139 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2141 pos_type cursor_vpos = 0;
2142 pos_type last = rowLastPrintable(row);
2144 if (pos > last + 1) {
2145 // This shouldn't happen.
2148 } else if (pos < row->pos()) {
2153 if (last < row->pos())
2154 cursor_vpos = row->pos();
2155 else if (pos > last && !boundary)
2156 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2157 ? row->pos() : last + 1;
2158 else if (pos > row->pos() &&
2159 (pos > last || boundary))
2160 /// Place cursor after char at (logical) position pos - 1
2161 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2162 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2164 /// Place cursor before char at (logical) position pos
2165 cursor_vpos = (bidi_level(pos) % 2 == 0)
2166 ? log2vis(pos) : log2vis(pos) + 1;
2168 pos_type main_body =
2169 beginningOfMainBody(bview->buffer(), row->par());
2170 if ((main_body > 0) &&
2171 ((main_body-1 > last) ||
2172 !row->par()->isLineSeparator(main_body-1)))
2175 for (pos_type vpos = row->pos();
2176 vpos < cursor_vpos; ++vpos) {
2177 pos = vis2log(vpos);
2178 if (main_body > 0 && pos == main_body - 1) {
2179 x += fill_label_hfill +
2180 lyxfont::width(textclasslist.Style(
2181 bview->buffer()->params.textclass,
2182 row->par()->getLayout())
2184 getLabelFont(bview->buffer(), row->par()));
2185 if (row->par()->isLineSeparator(main_body-1))
2186 x -= singleWidth(bview, row->par(),main_body-1);
2188 if (hfillExpansion(bview->buffer(), row, pos)) {
2189 x += singleWidth(bview, row->par(), pos);
2190 if (pos >= main_body)
2193 x += fill_label_hfill;
2194 } else if (row->par()->isSeparator(pos)) {
2195 x += singleWidth(bview, row->par(), pos);
2196 if (pos >= main_body)
2197 x += fill_separator;
2199 x += singleWidth(bview, row->par(), pos);
2208 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2209 pos_type pos, bool setfont, bool boundary) const
2211 InsetText * it = static_cast<InsetText *>(par->inInset());
2213 if (it != inset_owner) {
2214 lyxerr << "InsetText is " << it << endl;
2215 lyxerr << "inset_owner is " << inset_owner << endl;
2216 #ifdef WITH_WARNINGS
2217 #warning I believe this code is wrong. (Lgb)
2218 #warning Jürgen, have a look at this. (Lgb)
2219 #warning Hmmm, I guess you are right but we
2220 #warning should verify when this is needed
2222 // Jürgen, would you like to have a look?
2223 // I guess we need to move the outer cursor
2224 // and open and lock the inset (bla bla bla)
2225 // stuff I don't know... so can you have a look?
2227 // I moved the lyxerr stuff in here so we can see if
2228 // this is actually really needed and where!
2230 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2235 setCursor(bview, cursor, par, pos, boundary);
2237 setCurrentFont(bview);
2241 void LyXText::setCurrentFont(BufferView * bview) const
2243 pos_type pos = cursor.pos();
2244 if (cursor.boundary() && pos > 0)
2248 if (pos == cursor.par()->size())
2250 else // potentional bug... BUG (Lgb)
2251 if (cursor.par()->isSeparator(pos)) {
2252 if (pos > cursor.row()->pos() &&
2253 bidi_level(pos) % 2 ==
2254 bidi_level(pos - 1) % 2)
2256 else if (pos + 1 < cursor.par()->size())
2262 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2263 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2265 if (cursor.pos() == cursor.par()->size() &&
2266 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2267 !cursor.boundary()) {
2268 Language const * lang =
2269 cursor.par()->getParLanguage(bview->buffer()->params);
2270 current_font.setLanguage(lang);
2271 current_font.setNumber(LyXFont::OFF);
2272 real_current_font.setLanguage(lang);
2273 real_current_font.setNumber(LyXFont::OFF);
2278 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2280 LyXCursor old_cursor = cursor;
2282 setCursorFromCoordinates(bview, cursor, x, y);
2283 setCurrentFont(bview);
2284 deleteEmptyParagraphMechanism(bview, old_cursor);
2288 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2291 // Get the row first.
2293 Row * row = getRowNearY(y);
2295 pos_type const column = getColumnNearX(bview, row, x, bound);
2296 cur.par(row->par());
2297 cur.pos(row->pos() + column);
2299 cur.y(y + row->baseline());
2301 cur.boundary(bound);
2305 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2307 if (cursor.pos() > 0) {
2308 bool boundary = cursor.boundary();
2309 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2310 if (!internal && !boundary &&
2311 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2312 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2313 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2314 Paragraph * par = cursor.par()->previous();
2315 setCursor(bview, par, par->size());
2320 void LyXText::cursorRight(BufferView * bview, bool internal) const
2322 if (!internal && cursor.boundary() &&
2323 !cursor.par()->isNewline(cursor.pos()))
2324 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2325 else if (cursor.pos() < cursor.par()->size()) {
2326 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2328 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2329 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2330 } else if (cursor.par()->next())
2331 setCursor(bview, cursor.par()->next(), 0);
2335 void LyXText::cursorUp(BufferView * bview) const
2337 setCursorFromCoordinates(bview, cursor.x_fix(),
2338 cursor.y() - cursor.row()->baseline() - 1);
2342 void LyXText::cursorDown(BufferView * bview) const
2344 setCursorFromCoordinates(bview, cursor.x_fix(),
2345 cursor.y() - cursor.row()->baseline()
2346 + cursor.row()->height() + 1);
2350 void LyXText::cursorUpParagraph(BufferView * bview) const
2352 if (cursor.pos() > 0) {
2353 setCursor(bview, cursor.par(), 0);
2355 else if (cursor.par()->previous()) {
2356 setCursor(bview, cursor.par()->previous(), 0);
2361 void LyXText::cursorDownParagraph(BufferView * bview) const
2363 if (cursor.par()->next()) {
2364 setCursor(bview, cursor.par()->next(), 0);
2366 setCursor(bview, cursor.par(), cursor.par()->size());
2370 // fix the cursor `cur' after a characters has been deleted at `where'
2371 // position. Called by deleteEmptyParagraphMechanism
2372 void LyXText::fixCursorAfterDelete(BufferView * bview,
2374 LyXCursor const & where) const
2376 // if cursor is not in the paragraph where the delete occured,
2378 if (cur.par() != where.par())
2381 // if cursor position is after the place where the delete occured,
2383 if (cur.pos() > where.pos())
2384 cur.pos(cur.pos()-1);
2386 // recompute row et al. for this cursor
2387 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2391 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2392 LyXCursor const & old_cursor) const
2394 // Would be wrong to delete anything if we have a selection.
2395 if (selection.set()) return false;
2397 // We allow all kinds of "mumbo-jumbo" when freespacing.
2398 if (textclasslist.Style(bview->buffer()->params.textclass,
2399 old_cursor.par()->getLayout()).free_spacing
2400 || old_cursor.par()->isFreeSpacing())
2405 /* Ok I'll put some comments here about what is missing.
2406 I have fixed BackSpace (and thus Delete) to not delete
2407 double-spaces automagically. I have also changed Cut,
2408 Copy and Paste to hopefully do some sensible things.
2409 There are still some small problems that can lead to
2410 double spaces stored in the document file or space at
2411 the beginning of paragraphs. This happens if you have
2412 the cursor betwenn to spaces and then save. Or if you
2413 cut and paste and the selection have a space at the
2414 beginning and then save right after the paste. I am
2415 sure none of these are very hard to fix, but I will
2416 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2417 that I can get some feedback. (Lgb)
2420 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2421 // delete the LineSeparator.
2424 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2425 // delete the LineSeparator.
2428 // If the pos around the old_cursor were spaces, delete one of them.
2429 if (old_cursor.par() != cursor.par()
2430 || old_cursor.pos() != cursor.pos()) {
2431 // Only if the cursor has really moved
2433 if (old_cursor.pos() > 0
2434 && old_cursor.pos() < old_cursor.par()->size()
2435 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2436 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2437 old_cursor.par()->erase(old_cursor.pos() - 1);
2438 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2440 #ifdef WITH_WARNINGS
2441 #warning This will not work anymore when we have multiple views of the same buffer
2442 // In this case, we will have to correct also the cursors held by
2443 // other bufferviews. It will probably be easier to do that in a more
2444 // automated way in LyXCursor code. (JMarc 26/09/2001)
2446 // correct all cursors held by the LyXText
2447 fixCursorAfterDelete(bview, cursor, old_cursor);
2448 fixCursorAfterDelete(bview, selection.cursor,
2450 fixCursorAfterDelete(bview, selection.start,
2452 fixCursorAfterDelete(bview, selection.end, old_cursor);
2453 fixCursorAfterDelete(bview, last_sel_cursor,
2455 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2456 fixCursorAfterDelete(bview, toggle_end_cursor,
2462 // don't delete anything if this is the ONLY paragraph!
2463 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2466 // Do not delete empty paragraphs with keepempty set.
2467 if ((textclasslist.Style(bview->buffer()->params.textclass,
2468 old_cursor.par()->getLayout())).keepempty)
2471 // only do our magic if we changed paragraph
2472 if (old_cursor.par() == cursor.par())
2475 // record if we have deleted a paragraph
2476 // we can't possibly have deleted a paragraph before this point
2477 bool deleted = false;
2479 if ((old_cursor.par()->size() == 0
2480 || (old_cursor.par()->size() == 1
2481 && old_cursor.par()->isLineSeparator(0)))) {
2482 // ok, we will delete anything
2483 LyXCursor tmpcursor;
2485 // make sure that you do not delete any environments
2486 status(bview, LyXText::NEED_MORE_REFRESH);
2489 if (old_cursor.row()->previous()) {
2490 refresh_row = old_cursor.row()->previous();
2491 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2493 cursor = old_cursor; // that undo can restore the right cursor position
2494 Paragraph * endpar = old_cursor.par()->next();
2495 if (endpar && endpar->getDepth()) {
2496 while (endpar && endpar->getDepth()) {
2497 endpar = endpar->next();
2500 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2504 removeRow(old_cursor.row());
2505 if (ownerParagraph() == old_cursor.par()) {
2506 ownerParagraph(ownerParagraph()->next());
2509 delete old_cursor.par();
2511 /* Breakagain the next par. Needed because of
2512 * the parindent that can occur or dissappear.
2513 * The next row can change its height, if
2514 * there is another layout before */
2515 if (refresh_row->next()) {
2516 breakAgain(bview, refresh_row->next());
2517 updateCounters(bview, refresh_row);
2519 setHeightOfRow(bview, refresh_row);
2521 refresh_row = old_cursor.row()->next();
2522 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2525 cursor = old_cursor; // that undo can restore the right cursor position
2526 Paragraph * endpar = old_cursor.par()->next();
2527 if (endpar && endpar->getDepth()) {
2528 while (endpar && endpar->getDepth()) {
2529 endpar = endpar->next();
2532 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2536 removeRow(old_cursor.row());
2538 if (ownerParagraph() == old_cursor.par()) {
2539 ownerParagraph(ownerParagraph()->next());
2542 delete old_cursor.par();
2544 /* Breakagain the next par. Needed because of
2545 the parindent that can occur or dissappear.
2546 The next row can change its height, if
2547 there is another layout before */
2549 breakAgain(bview, refresh_row);
2550 updateCounters(bview, refresh_row->previous());
2555 setCursorIntern(bview, cursor.par(), cursor.pos());
2557 if (selection.cursor.par() == old_cursor.par()
2558 && selection.cursor.pos() == old_cursor.pos()) {
2559 // correct selection
2560 selection.cursor = cursor;
2564 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2565 redoParagraphs(bview, old_cursor,
2566 old_cursor.par()->next());
2568 setCursorIntern(bview, cursor.par(), cursor.pos());
2569 selection.cursor = cursor;
2576 void LyXText::toggleAppendix(BufferView * bview)
2578 Paragraph * par = cursor.par();
2579 bool start = !par->params().startOfAppendix();
2581 // ensure that we have only one start_of_appendix in this document
2582 Paragraph * tmp = ownerParagraph();
2583 for (; tmp; tmp = tmp->next()) {
2584 tmp->params().startOfAppendix(false);
2587 par->params().startOfAppendix(start);
2589 // we can set the refreshing parameters now
2590 status(bview, LyXText::NEED_MORE_REFRESH);
2592 refresh_row = 0; // not needed for full update
2593 updateCounters(bview, 0);
2594 setCursor(bview, cursor.par(), cursor.pos());
2598 Paragraph * LyXText::ownerParagraph() const
2601 return inset_owner->paragraph();
2603 return bv_owner->buffer()->paragraph;
2607 void LyXText::ownerParagraph(Paragraph * p) const
2610 inset_owner->paragraph(p);
2612 bv_owner->buffer()->paragraph = p;
2617 void LyXText::ownerParagraph(int id, Paragraph * p) const
2619 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2620 if (op && op->inInset()) {
2621 static_cast<InsetText *>(op->inInset())->paragraph(p);
2628 LyXText::text_status LyXText::status() const
2634 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2636 // well as much as I know && binds more then || so the above and the
2637 // below are identical (this for your known use of parentesis!)
2638 // Now some explanation:
2639 // We should only go up with refreshing code so this means that if
2640 // we have a MORE refresh we should never set it to LITTLE if we still
2641 // didn't handle it (and then it will be UNCHANGED. Now as long as
2642 // we stay inside one LyXText this may work but we need to tell the
2643 // outermost LyXText that it should REALLY draw us if there is some
2644 // change in a Inset::LyXText. So you see that when we are inside a
2645 // inset's LyXText we give the LITTLE to the outermost LyXText to
2646 // tell'em that it should redraw the actual row (where the inset
2647 // resides! Capito?!
2649 if ((status_ != NEED_MORE_REFRESH)
2650 || (status_ == NEED_MORE_REFRESH
2651 && st != NEED_VERY_LITTLE_REFRESH))
2654 if (inset_owner && st != UNCHANGED) {
2655 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2656 if (!bview->text->refresh_row) {
2657 bview->text->refresh_row = bview->text->cursor.row();
2658 bview->text->refresh_y = bview->text->cursor.y() -
2659 bview->text->cursor.row()->baseline();