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"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
66 LyXText::LyXText(InsetText * inset)
67 : number_of_rows(0), height(0), width(0), first(0),
68 bv_owner(0), inset_owner(inset), the_locking_inset(0),
69 need_break_row(0), refresh_y(0), refresh_row(0),
70 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
74 void LyXText::init(BufferView * bview, bool reinit)
77 // Delete all rows, this does not touch the paragraphs!
78 Row * tmprow = firstrow;
80 tmprow = firstrow->next();
84 lastrow = refresh_row = need_break_row = 0;
85 width = height = copylayouttype = 0;
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font);
132 tmpfont.realize(textclasslist.
133 Style(buf->params.textclass,
134 par->getLayout()).font,
135 buf->params.language);
137 par_depth = par->getDepth();
141 #ifndef INHERIT_LANGUAGE
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
144 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
145 buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout const & layout =
167 textclasslist.Style(buf->params.textclass, par->getLayout());
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout.labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params,
177 #ifndef INHERIT_LANGUAGE
178 return f.realize(layout.reslabelfont);
180 return f.realize(layout.reslabelfont, buf->params.language);
183 LyXFont f = par->getFontSettings(buf->params, pos);
184 #ifndef INHERIT_LANGUAGE
185 return f.realize(layout.resfont);
187 return f.realize(layout.resfont, buf->params.language);
192 // The uncommon case need not be optimized as much
196 if (pos < beginningOfMainBody(buf, par)) {
198 layoutfont = layout.labelfont;
201 layoutfont = layout.font;
204 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
205 #ifndef INHERIT_LANGUAGE
206 tmpfont.realize(layoutfont);
208 tmpfont.realize(layoutfont, buf->params.language);
211 return realizeFont(tmpfont, buf, par);
215 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
217 LyXLayout const & layout =
218 textclasslist.Style(buf->params.textclass, par->getLayout());
220 Paragraph::depth_type par_depth = par->getDepth();
223 return layout.resfont;
226 return realizeFont(layout.font, buf, par);
230 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
232 LyXLayout const & layout =
233 textclasslist.Style(buf->params.textclass, par->getLayout());
235 Paragraph::depth_type par_depth = par->getDepth();
238 return layout.reslabelfont;
241 return realizeFont(layout.labelfont, buf, par);
245 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
246 pos_type pos, LyXFont const & fnt,
249 Buffer const * buf = bv->buffer();
250 LyXFont font = getFont(buf, par, pos);
251 font.update(fnt, buf->params.language, toggleall);
252 // Let the insets convert their font
253 if (par->isInset(pos)) {
254 Inset * inset = par->getInset(pos);
255 if (isEditableInset(inset)) {
256 UpdatableInset * uinset =
257 static_cast<UpdatableInset *>(inset);
258 uinset->setFont(bv, fnt, toggleall, true);
262 LyXLayout const & layout =
263 textclasslist.Style(buf->params.textclass,
266 // Get concrete layout font to reduce against
269 if (pos < beginningOfMainBody(buf, par))
270 layoutfont = layout.labelfont;
272 layoutfont = layout.font;
274 // Realize against environment font information
275 if (par->getDepth()){
276 Paragraph * tp = par;
277 while (!layoutfont.resolved() && tp && tp->getDepth()) {
278 tp = tp->outerHook();
280 #ifndef INHERIT_LANGUAGE
281 layoutfont.realize(textclasslist.
282 Style(buf->params.textclass,
283 tp->getLayout()).font);
285 layoutfont.realize(textclasslist.
286 Style(buf->params.textclass,
287 tp->getLayout()).font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
296 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
297 buf->params.language);
300 // Now, reduce font against full layout font
301 font.reduce(layoutfont);
303 par->setFont(pos, font);
307 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
308 pos_type pos, LyXFont const & fnt)
312 LyXLayout const & layout =
313 textclasslist.Style(buf->params.textclass,
316 // Get concrete layout font to reduce against
319 if (pos < beginningOfMainBody(buf, par))
320 layoutfont = layout.labelfont;
322 layoutfont = layout.font;
324 // Realize against environment font information
325 if (par->getDepth()){
326 Paragraph * tp = par;
327 while (!layoutfont.resolved() && tp && tp->getDepth()) {
328 tp = tp->outerHook();
330 #ifndef INHERIT_LANGUAGE
331 layoutfont.realize(textclasslist.
332 Style(buf->params.textclass,
333 tp->getLayout()).font);
335 layoutfont.realize(textclasslist.
336 Style(buf->params.textclass,
337 tp->getLayout()).font,
338 buf->params.language);
343 #ifndef INHERIT_LANGUAGE
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
346 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
347 buf->params.language);
350 // Now, reduce font against full layout font
351 font.reduce(layoutfont);
353 par->setFont(pos, font);
357 // inserts a new row behind the specified row, increments
358 // the touched counters
359 void LyXText::insertRow(Row * row, Paragraph * par,
362 Row * tmprow = new Row;
365 tmprow->next(firstrow);
368 tmprow->previous(row);
369 tmprow->next(row->next());
374 tmprow->next()->previous(tmprow);
376 if (tmprow->previous())
377 tmprow->previous()->next(tmprow);
389 // removes the row and reset the touched counters
390 void LyXText::removeRow(Row * row) const
392 Row * row_prev = row->previous();
394 row->next()->previous(row_prev);
396 firstrow = row->next();
397 // lyx::Assert(firstrow);
399 row_prev->next(row->next());
401 if (row == lastrow) {
402 lyx::Assert(!row->next());
405 if (refresh_row == row) {
406 refresh_row = row_prev ? row_prev : row->next();
407 // what about refresh_y, refresh_height
410 height -= row->height(); // the text becomes smaller
413 --number_of_rows; // one row less
417 // remove all following rows of the paragraph of the specified row.
418 void LyXText::removeParagraph(Row * row) const
420 Paragraph * tmppar = row->par();
424 while (row && row->par() == tmppar) {
425 tmprow = row->next();
432 // insert the specified paragraph behind the specified row
433 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
436 insertRow(row, par, 0); /* insert a new row, starting
439 setCounter(bview->buffer(), par); // set the counters
441 // and now append the whole paragraph behind the new row
444 appendParagraph(bview, firstrow);
446 row->next()->height(0);
447 appendParagraph(bview, row->next());
452 Inset * LyXText::getInset() const
455 if (cursor.pos() == 0 && cursor.par()->bibkey) {
456 inset = cursor.par()->bibkey;
457 } else if (cursor.pos() < cursor.par()->size()
458 && cursor.par()->isInset(cursor.pos())) {
459 inset = cursor.par()->getInset(cursor.pos());
465 void LyXText::toggleInset(BufferView * bview)
467 Inset * inset = getInset();
468 // is there an editable inset at cursor position?
469 if (!isEditableInset(inset)) {
470 // No, try to see if we are inside a collapsable inset
471 if (inset_owner && inset_owner->owner()
472 && inset_owner->owner()->isOpen()) {
473 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
474 inset_owner->owner()->close(bview);
478 //bview->owner()->message(inset->editMessage());
480 // do we want to keep this?? (JMarc)
481 if (!isHighlyEditableInset(inset))
482 setCursorParUndo(bview);
484 if (inset->isOpen()) {
490 inset->open(bview, !inset->isOpen());
495 /* used in setlayout */
496 // Asger is not sure we want to do this...
497 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
500 LyXLayout const & layout =
501 textclasslist.Style(buf->params.textclass, par->getLayout());
504 for (pos_type pos = 0; pos < par->size(); ++pos) {
505 if (pos < beginningOfMainBody(buf, par))
506 layoutfont = layout.labelfont;
508 layoutfont = layout.font;
510 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
511 tmpfont.reduce(layoutfont);
512 par->setFont(pos, tmpfont);
517 Paragraph * LyXText::setLayout(BufferView * bview,
518 LyXCursor & cur, LyXCursor & sstart_cur,
519 LyXCursor & send_cur,
520 lyx::layout_type layout)
522 Paragraph * endpar = send_cur.par()->next();
523 Paragraph * undoendpar = endpar;
525 if (endpar && endpar->getDepth()) {
526 while (endpar && endpar->getDepth()) {
527 endpar = endpar->next();
531 endpar = endpar->next(); // because of parindents etc.
534 setUndo(bview, Undo::EDIT,
535 sstart_cur.par(), undoendpar);
537 // ok we have a selection. This is always between sstart_cur
538 // and sel_end cursor
541 LyXLayout const & lyxlayout =
542 textclasslist.Style(bview->buffer()->params.textclass, layout);
544 while (cur.par() != send_cur.par()) {
545 cur.par()->setLayout(layout);
546 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
547 Paragraph * fppar = cur.par();
548 fppar->params().spaceTop(lyxlayout.fill_top ?
549 VSpace(VSpace::VFILL)
550 : VSpace(VSpace::NONE));
551 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
552 VSpace(VSpace::VFILL)
553 : VSpace(VSpace::NONE));
554 if (lyxlayout.margintype == MARGIN_MANUAL)
555 cur.par()->setLabelWidthString(lyxlayout.labelstring());
556 if (lyxlayout.labeltype != LABEL_BIBLIO
558 delete fppar->bibkey;
561 cur.par(cur.par()->next());
563 cur.par()->setLayout(layout);
564 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
565 Paragraph * fppar = cur.par();
566 fppar->params().spaceTop(lyxlayout.fill_top ?
567 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
568 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
569 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
570 if (lyxlayout.margintype == MARGIN_MANUAL)
571 cur.par()->setLabelWidthString(lyxlayout.labelstring());
572 if (lyxlayout.labeltype != LABEL_BIBLIO
574 delete fppar->bibkey;
581 // set layout over selection and make a total rebreak of those paragraphs
582 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
584 LyXCursor tmpcursor = cursor; /* store the current cursor */
586 // if there is no selection just set the layout
587 // of the current paragraph */
588 if (!selection.set()) {
589 selection.start = cursor; // dummy selection
590 selection.end = cursor;
592 Paragraph * endpar = setLayout(bview, cursor, selection.start,
593 selection.end, layout);
594 redoParagraphs(bview, selection.start, endpar);
596 // we have to reset the selection, because the
597 // geometry could have changed
598 setCursor(bview, selection.start.par(),
599 selection.start.pos(), false);
600 selection.cursor = cursor;
601 setCursor(bview, selection.end.par(), selection.end.pos(), false);
602 updateCounters(bview, cursor.row());
605 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
609 // increment depth over selection and
610 // make a total rebreak of those paragraphs
611 void LyXText::incDepth(BufferView * bview)
613 // If there is no selection, just use the current paragraph
614 if (!selection.set()) {
615 selection.start = cursor; // dummy selection
616 selection.end = cursor;
619 // We end at the next paragraph with depth 0
620 Paragraph * endpar = selection.end.par()->next();
622 Paragraph * undoendpar = endpar;
624 if (endpar && endpar->getDepth()) {
625 while (endpar && endpar->getDepth()) {
626 endpar = endpar->next();
630 endpar = endpar->next(); // because of parindents etc.
633 setUndo(bview, Undo::EDIT,
634 selection.start.par(), undoendpar);
636 LyXCursor tmpcursor = cursor; // store the current cursor
638 // ok we have a selection. This is always between sel_start_cursor
639 // and sel_end cursor
640 cursor = selection.start;
642 bool anything_changed = false;
645 // NOTE: you can't change the depth of a bibliography entry
647 textclasslist.Style(bview->buffer()->params.textclass,
648 cursor.par()->getLayout()
649 ).labeltype != LABEL_BIBLIO) {
650 Paragraph * prev = cursor.par()->previous();
653 && (prev->getDepth() - cursor.par()->getDepth() > 0
654 || (prev->getDepth() == cursor.par()->getDepth()
655 && textclasslist.Style(bview->buffer()->params.textclass,
656 prev->getLayout()).isEnvironment()))) {
657 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
658 anything_changed = true;
661 if (cursor.par() == selection.end.par())
663 cursor.par(cursor.par()->next());
666 // if nothing changed set all depth to 0
667 if (!anything_changed) {
668 cursor = selection.start;
669 while (cursor.par() != selection.end.par()) {
670 cursor.par()->params().depth(0);
671 cursor.par(cursor.par()->next());
673 cursor.par()->params().depth(0);
676 redoParagraphs(bview, selection.start, endpar);
678 // we have to reset the selection, because the
679 // geometry could have changed
680 setCursor(bview, selection.start.par(), selection.start.pos());
681 selection.cursor = cursor;
682 setCursor(bview, selection.end.par(), selection.end.pos());
683 updateCounters(bview, cursor.row());
686 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
690 // decrement depth over selection and
691 // make a total rebreak of those paragraphs
692 void LyXText::decDepth(BufferView * bview)
694 // if there is no selection just set the layout
695 // of the current paragraph
696 if (!selection.set()) {
697 selection.start = cursor; // dummy selection
698 selection.end = cursor;
700 Paragraph * endpar = selection.end.par()->next();
701 Paragraph * undoendpar = endpar;
703 if (endpar && endpar->getDepth()) {
704 while (endpar && endpar->getDepth()) {
705 endpar = endpar->next();
709 endpar = endpar->next(); // because of parindents etc.
712 setUndo(bview, Undo::EDIT,
713 selection.start.par(), undoendpar);
715 LyXCursor tmpcursor = cursor; // store the current cursor
717 // ok we have a selection. This is always between sel_start_cursor
718 // and sel_end cursor
719 cursor = selection.start;
722 if (cursor.par()->params().depth()) {
723 cursor.par()->params()
724 .depth(cursor.par()->params().depth() - 1);
726 if (cursor.par() == selection.end.par()) {
729 cursor.par(cursor.par()->next());
732 redoParagraphs(bview, selection.start, endpar);
734 // we have to reset the selection, because the
735 // geometry could have changed
736 setCursor(bview, selection.start.par(),
737 selection.start.pos());
738 selection.cursor = cursor;
739 setCursor(bview, selection.end.par(), selection.end.pos());
740 updateCounters(bview, cursor.row());
743 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
747 // set font over selection and make a total rebreak of those paragraphs
748 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
750 // if there is no selection just set the current_font
751 if (!selection.set()) {
752 // Determine basis font
754 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
756 layoutfont = getLabelFont(bview->buffer(),
759 layoutfont = getLayoutFont(bview->buffer(),
762 // Update current font
763 real_current_font.update(font,
764 bview->buffer()->params.language,
767 // Reduce to implicit settings
768 current_font = real_current_font;
769 current_font.reduce(layoutfont);
770 // And resolve it completely
771 #ifndef INHERIT_LANGUAGE
772 real_current_font.realize(layoutfont);
774 real_current_font.realize(layoutfont,
775 bview->buffer()->params.language);
780 LyXCursor tmpcursor = cursor; // store the current cursor
782 // ok we have a selection. This is always between sel_start_cursor
783 // and sel_end cursor
785 setUndo(bview, Undo::EDIT,
786 selection.start.par(), selection.end.par()->next());
788 cursor = selection.start;
789 while (cursor.par() != selection.end.par() ||
790 (cursor.pos() < selection.end.pos()))
792 if (cursor.pos() < cursor.par()->size()) {
793 // an open footnote should behave
795 setCharFont(bview, cursor.par(), cursor.pos(),
797 cursor.pos(cursor.pos() + 1);
800 cursor.par(cursor.par()->next());
805 redoParagraphs(bview, selection.start, selection.end.par()->next());
807 // we have to reset the selection, because the
808 // geometry could have changed, but we keep
809 // it for user convenience
810 setCursor(bview, selection.start.par(), selection.start.pos());
811 selection.cursor = cursor;
812 setCursor(bview, selection.end.par(), selection.end.pos());
814 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
815 tmpcursor.boundary());
819 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
821 Row * tmprow = cur.row();
822 int y = cur.y() - tmprow->baseline();
824 setHeightOfRow(bview, tmprow);
826 while (tmprow->previous()
827 && tmprow->previous()->par() == tmprow->par()) {
828 tmprow = tmprow->previous();
829 y -= tmprow->height();
830 setHeightOfRow(bview, tmprow);
833 // we can set the refreshing parameters now
834 status(bview, LyXText::NEED_MORE_REFRESH);
836 refresh_row = tmprow;
837 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
841 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
843 Row * tmprow = cur.row();
845 int y = cur.y() - tmprow->baseline();
846 setHeightOfRow(bview, tmprow);
848 while (tmprow->previous()
849 && tmprow->previous()->par() == tmprow->par()) {
850 tmprow = tmprow->previous();
851 y -= tmprow->height();
854 // we can set the refreshing parameters now
855 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
857 refresh_row = tmprow;
859 status(bview, LyXText::NEED_MORE_REFRESH);
860 setCursor(bview, cur.par(), cur.pos());
864 // deletes and inserts again all paragaphs between the cursor
865 // and the specified par
866 // This function is needed after SetLayout and SetFont etc.
867 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
868 Paragraph const * endpar) const
871 Paragraph * tmppar = 0;
872 Paragraph * first_phys_par = 0;
874 Row * tmprow = cur.row();
876 int y = cur.y() - tmprow->baseline();
878 if (!tmprow->previous()) {
879 // a trick/hack for UNDO
880 // This is needed because in an UNDO/REDO we could have changed
881 // the ownerParagrah() so the paragraph inside the row is NOT
882 // my really first par anymore. Got it Lars ;) (Jug 20011206)
883 first_phys_par = ownerParagraph();
885 first_phys_par = tmprow->par();
886 while (tmprow->previous()
887 && tmprow->previous()->par() == first_phys_par)
889 tmprow = tmprow->previous();
890 y -= tmprow->height();
894 // we can set the refreshing parameters now
895 status(bview, LyXText::NEED_MORE_REFRESH);
897 refresh_row = tmprow->previous(); /* the real refresh row will
898 be deleted, so I store
902 tmppar = tmprow->next()->par();
905 while (tmprow->next() && tmppar != endpar) {
906 removeRow(tmprow->next());
907 if (tmprow->next()) {
908 tmppar = tmprow->next()->par();
914 // remove the first one
915 tmprow2 = tmprow; /* this is because tmprow->previous()
917 tmprow = tmprow->previous();
920 tmppar = first_phys_par;
924 insertParagraph(bview, tmppar, tmprow);
928 while (tmprow->next()
929 && tmprow->next()->par() == tmppar) {
930 tmprow = tmprow->next();
932 tmppar = tmppar->next();
934 } while (tmppar && tmppar != endpar);
936 // this is because of layout changes
938 refresh_y -= refresh_row->height();
939 setHeightOfRow(bview, refresh_row);
941 refresh_row = firstrow;
943 setHeightOfRow(bview, refresh_row);
946 if (tmprow && tmprow->next())
947 setHeightOfRow(bview, tmprow->next());
951 bool LyXText::fullRebreak(BufferView * bview)
957 if (need_break_row) {
958 breakAgain(bview, need_break_row);
966 // important for the screen
969 /* the cursor set functions have a special mechanism. When they
970 * realize, that you left an empty paragraph, they will delete it.
971 * They also delete the corresponding row */
973 // need the selection cursor:
974 void LyXText::setSelection(BufferView * bview)
976 bool const lsel = selection.set();
978 if (!selection.set()) {
979 last_sel_cursor = selection.cursor;
980 selection.start = selection.cursor;
981 selection.end = selection.cursor;
986 // first the toggling area
987 if (cursor.y() < last_sel_cursor.y()
988 || (cursor.y() == last_sel_cursor.y()
989 && cursor.x() < last_sel_cursor.x())) {
990 toggle_end_cursor = last_sel_cursor;
991 toggle_cursor = cursor;
993 toggle_end_cursor = cursor;
994 toggle_cursor = last_sel_cursor;
997 last_sel_cursor = cursor;
999 // and now the whole selection
1001 if (selection.cursor.par() == cursor.par())
1002 if (selection.cursor.pos() < cursor.pos()) {
1003 selection.end = cursor;
1004 selection.start = selection.cursor;
1006 selection.end = selection.cursor;
1007 selection.start = cursor;
1009 else if (selection.cursor.y() < cursor.y() ||
1010 (selection.cursor.y() == cursor.y()
1011 && selection.cursor.x() < cursor.x())) {
1012 selection.end = cursor;
1013 selection.start = selection.cursor;
1016 selection.end = selection.cursor;
1017 selection.start = cursor;
1020 // a selection with no contents is not a selection
1021 if (selection.start.par() == selection.end.par() &&
1022 selection.start.pos() == selection.end.pos())
1023 selection.set(false);
1025 if (inset_owner && (selection.set() || lsel))
1026 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1030 string const LyXText::selectionAsString(Buffer const * buffer,
1033 if (!selection.set()) return string();
1036 // Special handling if the whole selection is within one paragraph
1037 if (selection.start.par() == selection.end.par()) {
1038 result += selection.start.par()->asString(buffer,
1039 selection.start.pos(),
1040 selection.end.pos(),
1045 // The selection spans more than one paragraph
1047 // First paragraph in selection
1048 result += selection.start.par()->asString(buffer,
1049 selection.start.pos(),
1050 selection.start.par()->size(),
1054 // The paragraphs in between (if any)
1055 LyXCursor tmpcur(selection.start);
1056 tmpcur.par(tmpcur.par()->next());
1057 while (tmpcur.par() != selection.end.par()) {
1058 result += tmpcur.par()->asString(buffer, 0,
1059 tmpcur.par()->size(),
1061 tmpcur.par(tmpcur.par()->next());
1064 // Last paragraph in selection
1065 result += selection.end.par()->asString(buffer, 0,
1066 selection.end.pos(), label);
1072 void LyXText::clearSelection() const
1074 selection.set(false);
1075 selection.mark(false);
1076 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1080 void LyXText::cursorHome(BufferView * bview) const
1082 setCursor(bview, cursor.par(), cursor.row()->pos());
1086 void LyXText::cursorEnd(BufferView * bview) const
1088 if (!cursor.row()->next()
1089 || cursor.row()->next()->par() != cursor.row()->par()) {
1090 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1092 if (cursor.par()->size() &&
1093 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1094 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1095 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1097 setCursor(bview,cursor.par(),
1098 rowLast(cursor.row()) + 1);
1104 void LyXText::cursorTop(BufferView * bview) const
1106 while (cursor.par()->previous())
1107 cursor.par(cursor.par()->previous());
1108 setCursor(bview, cursor.par(), 0);
1112 void LyXText::cursorBottom(BufferView * bview) const
1114 while (cursor.par()->next())
1115 cursor.par(cursor.par()->next());
1116 setCursor(bview, cursor.par(), cursor.par()->size());
1120 void LyXText::toggleFree(BufferView * bview,
1121 LyXFont const & font, bool toggleall)
1123 // If the mask is completely neutral, tell user
1124 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1125 // Could only happen with user style
1126 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1130 // Try implicit word selection
1131 // If there is a change in the language the implicit word selection
1133 LyXCursor resetCursor = cursor;
1134 bool implicitSelection = (font.language() == ignore_language
1135 && font.number() == LyXFont::IGNORE)
1136 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1139 setFont(bview, font, toggleall);
1141 // Implicit selections are cleared afterwards
1142 //and cursor is set to the original position.
1143 if (implicitSelection) {
1145 cursor = resetCursor;
1146 setCursor(bview, cursor.par(), cursor.pos());
1147 selection.cursor = cursor;
1150 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1154 string LyXText::getStringToIndex(BufferView * bview)
1158 // Try implicit word selection
1159 // If there is a change in the language the implicit word selection
1161 LyXCursor resetCursor = cursor;
1162 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1164 if (!selection.set()) {
1165 bview->owner()->message(_("Nothing to index!"));
1168 if (selection.start.par() != selection.end.par()) {
1169 bview->owner()->message(_("Cannot index more than one paragraph!"));
1173 idxstring = selectionAsString(bview->buffer(), false);
1175 // Implicit selections are cleared afterwards
1176 //and cursor is set to the original position.
1177 if (implicitSelection) {
1179 cursor = resetCursor;
1180 setCursor(bview, cursor.par(), cursor.pos());
1181 selection.cursor = cursor;
1187 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1188 Paragraph const * par) const
1190 if (textclasslist.Style(buf->params.textclass,
1191 par->getLayout()).labeltype != LABEL_MANUAL)
1194 return par->beginningOfMainBody();
1198 /* the DTP switches for paragraphs. LyX will store them in the
1199 * first physicla paragraph. When a paragraph is broken, the top settings
1200 * rest, the bottom settings are given to the new one. So I can make shure,
1201 * they do not duplicate themself and you cannnot make dirty things with
1204 void LyXText::setParagraph(BufferView * bview,
1205 bool line_top, bool line_bottom,
1206 bool pagebreak_top, bool pagebreak_bottom,
1207 VSpace const & space_top,
1208 VSpace const & space_bottom,
1209 Spacing const & spacing,
1211 string labelwidthstring,
1214 LyXCursor tmpcursor = cursor;
1215 if (!selection.set()) {
1216 selection.start = cursor;
1217 selection.end = cursor;
1220 // make sure that the depth behind the selection are restored, too
1221 Paragraph * endpar = selection.end.par()->next();
1222 Paragraph * undoendpar = endpar;
1224 if (endpar && endpar->getDepth()) {
1225 while (endpar && endpar->getDepth()) {
1226 endpar = endpar->next();
1227 undoendpar = endpar;
1231 // because of parindents etc.
1232 endpar = endpar->next();
1235 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1238 Paragraph * tmppar = selection.end.par();
1239 while (tmppar != selection.start.par()->previous()) {
1240 setCursor(bview, tmppar, 0);
1241 status(bview, LyXText::NEED_MORE_REFRESH);
1242 refresh_row = cursor.row();
1243 refresh_y = cursor.y() - cursor.row()->baseline();
1244 cursor.par()->params().lineTop(line_top);
1245 cursor.par()->params().lineBottom(line_bottom);
1246 cursor.par()->params().pagebreakTop(pagebreak_top);
1247 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1248 cursor.par()->params().spaceTop(space_top);
1249 cursor.par()->params().spaceBottom(space_bottom);
1250 cursor.par()->params().spacing(spacing);
1251 // does the layout allow the new alignment?
1252 if (align == LYX_ALIGN_LAYOUT)
1253 align = textclasslist
1254 .Style(bview->buffer()->params.textclass,
1255 cursor.par()->getLayout()).align;
1256 if (align & textclasslist
1257 .Style(bview->buffer()->params.textclass,
1258 cursor.par()->getLayout()).alignpossible) {
1259 if (align == textclasslist
1260 .Style(bview->buffer()->params.textclass,
1261 cursor.par()->getLayout()).align)
1262 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1264 cursor.par()->params().align(align);
1266 cursor.par()->setLabelWidthString(labelwidthstring);
1267 cursor.par()->params().noindent(noindent);
1268 tmppar = cursor.par()->previous();
1271 redoParagraphs(bview, selection.start, endpar);
1274 setCursor(bview, selection.start.par(), selection.start.pos());
1275 selection.cursor = cursor;
1276 setCursor(bview, selection.end.par(), selection.end.pos());
1277 setSelection(bview);
1278 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1280 bview->updateInset(inset_owner, true);
1284 char loweralphaCounter(int n)
1286 if (n < 1 || n > 26)
1296 char alphaCounter(int n)
1298 if (n < 1 || n > 26)
1306 char hebrewCounter(int n)
1308 static const char hebrew[22] = {
1309 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1310 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1311 '÷', 'ø', 'ù', 'ú'
1313 if (n < 1 || n > 22)
1321 string const romanCounter(int n)
1323 static char const * roman[20] = {
1324 "i", "ii", "iii", "iv", "v",
1325 "vi", "vii", "viii", "ix", "x",
1326 "xi", "xii", "xiii", "xiv", "xv",
1327 "xvi", "xvii", "xviii", "xix", "xx"
1329 if (n < 1 || n > 20)
1338 // set the counter of a paragraph. This includes the labels
1339 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1341 LyXLayout const & layout =
1342 textclasslist.Style(buf->params.textclass,
1345 LyXTextClass const & textclass =
1346 textclasslist.TextClass(buf->params.textclass);
1348 // copy the prev-counters to this one,
1349 // unless this is the first paragraph
1350 if (par->previous()) {
1351 for (int i = 0; i < 10; ++i) {
1352 par->setCounter(i, par->previous()->getFirstCounter(i));
1354 par->params().appendix(par->previous()->params().appendix());
1355 if (!par->params().appendix() && par->params().startOfAppendix()) {
1356 par->params().appendix(true);
1357 for (int i = 0; i < 10; ++i) {
1358 par->setCounter(i, 0);
1361 par->enumdepth = par->previous()->enumdepth;
1362 par->itemdepth = par->previous()->itemdepth;
1364 for (int i = 0; i < 10; ++i) {
1365 par->setCounter(i, 0);
1367 par->params().appendix(par->params().startOfAppendix());
1372 /* Maybe we have to increment the enumeration depth.
1373 * BUT, enumeration in a footnote is considered in isolation from its
1374 * surrounding paragraph so don't increment if this is the
1375 * first line of the footnote
1376 * AND, bibliographies can't have their depth changed ie. they
1377 * are always of depth 0
1380 && par->previous()->getDepth() < par->getDepth()
1381 && textclasslist.Style(buf->params.textclass,
1382 par->previous()->getLayout()
1383 ).labeltype == LABEL_COUNTER_ENUMI
1384 && par->enumdepth < 3
1385 && layout.labeltype != LABEL_BIBLIO) {
1389 // Maybe we have to decrement the enumeration depth, see note above
1391 && par->previous()->getDepth() > par->getDepth()
1392 && layout.labeltype != LABEL_BIBLIO) {
1393 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1394 par->setCounter(6 + par->enumdepth,
1395 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1396 /* reset the counters.
1397 * A depth change is like a breaking layout
1399 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1400 par->setCounter(i, 0);
1403 if (!par->params().labelString().empty()) {
1404 par->params().labelString(string());
1407 if (layout.margintype == MARGIN_MANUAL) {
1408 if (par->params().labelWidthString().empty()) {
1409 par->setLabelWidthString(layout.labelstring());
1412 par->setLabelWidthString(string());
1415 // is it a layout that has an automatic label?
1416 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1418 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1419 if (i >= 0 && i<= buf->params.secnumdepth) {
1420 par->incCounter(i); // increment the counter
1422 // Is there a label? Useful for Chapter layout
1423 if (!par->params().appendix()) {
1424 if (!layout.labelstring().empty())
1425 par->params().labelString(layout.labelstring());
1427 par->params().labelString(string());
1429 if (!layout.labelstring_appendix().empty())
1430 par->params().labelString(layout.labelstring_appendix());
1432 par->params().labelString(string());
1437 if (!par->params().appendix()) {
1438 switch (2 * LABEL_COUNTER_CHAPTER -
1439 textclass.maxcounter() + i) {
1440 case LABEL_COUNTER_CHAPTER:
1441 s << par->getCounter(i);
1443 case LABEL_COUNTER_SECTION:
1444 s << par->getCounter(i - 1) << '.'
1445 << par->getCounter(i);
1447 case LABEL_COUNTER_SUBSECTION:
1448 s << par->getCounter(i - 2) << '.'
1449 << par->getCounter(i - 1) << '.'
1450 << par->getCounter(i);
1452 case LABEL_COUNTER_SUBSUBSECTION:
1453 s << par->getCounter(i - 3) << '.'
1454 << par->getCounter(i - 2) << '.'
1455 << par->getCounter(i - 1) << '.'
1456 << par->getCounter(i);
1459 case LABEL_COUNTER_PARAGRAPH:
1460 s << par->getCounter(i - 4) << '.'
1461 << par->getCounter(i - 3) << '.'
1462 << par->getCounter(i - 2) << '.'
1463 << par->getCounter(i - 1) << '.'
1464 << par->getCounter(i);
1466 case LABEL_COUNTER_SUBPARAGRAPH:
1467 s << par->getCounter(i - 5) << '.'
1468 << par->getCounter(i - 4) << '.'
1469 << par->getCounter(i - 3) << '.'
1470 << par->getCounter(i - 2) << '.'
1471 << par->getCounter(i - 1) << '.'
1472 << par->getCounter(i);
1476 // Can this ever be reached? And in the
1477 // case it is, how can this be correct?
1479 s << par->getCounter(i) << '.';
1482 } else { // appendix
1483 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1484 case LABEL_COUNTER_CHAPTER:
1485 if (par->isRightToLeftPar(buf->params))
1486 s << hebrewCounter(par->getCounter(i));
1488 s << alphaCounter(par->getCounter(i));
1490 case LABEL_COUNTER_SECTION:
1491 if (par->isRightToLeftPar(buf->params))
1492 s << hebrewCounter(par->getCounter(i - 1));
1494 s << alphaCounter(par->getCounter(i - 1));
1497 << par->getCounter(i);
1500 case LABEL_COUNTER_SUBSECTION:
1501 if (par->isRightToLeftPar(buf->params))
1502 s << hebrewCounter(par->getCounter(i - 2));
1504 s << alphaCounter(par->getCounter(i - 2));
1507 << par->getCounter(i-1) << '.'
1508 << par->getCounter(i);
1511 case LABEL_COUNTER_SUBSUBSECTION:
1512 if (par->isRightToLeftPar(buf->params))
1513 s << hebrewCounter(par->getCounter(i-3));
1515 s << alphaCounter(par->getCounter(i-3));
1518 << par->getCounter(i-2) << '.'
1519 << par->getCounter(i-1) << '.'
1520 << par->getCounter(i);
1523 case LABEL_COUNTER_PARAGRAPH:
1524 if (par->isRightToLeftPar(buf->params))
1525 s << hebrewCounter(par->getCounter(i-4));
1527 s << alphaCounter(par->getCounter(i-4));
1530 << par->getCounter(i-3) << '.'
1531 << par->getCounter(i-2) << '.'
1532 << par->getCounter(i-1) << '.'
1533 << par->getCounter(i);
1536 case LABEL_COUNTER_SUBPARAGRAPH:
1537 if (par->isRightToLeftPar(buf->params))
1538 s << hebrewCounter(par->getCounter(i-5));
1540 s << alphaCounter(par->getCounter(i-5));
1543 << par->getCounter(i-4) << '.'
1544 << par->getCounter(i-3) << '.'
1545 << par->getCounter(i-2) << '.'
1546 << par->getCounter(i-1) << '.'
1547 << par->getCounter(i);
1551 // Can this ever be reached? And in the
1552 // case it is, how can this be correct?
1554 s << par->getCounter(i) << '.';
1560 par->params().labelString(par->params().labelString() +s.str().c_str());
1561 // We really want to remove the c_str as soon as
1564 for (i++; i < 10; ++i) {
1565 // reset the following counters
1566 par->setCounter(i, 0);
1568 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1569 for (i++; i < 10; ++i) {
1570 // reset the following counters
1571 par->setCounter(i, 0);
1573 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1574 par->incCounter(i + par->enumdepth);
1575 int number = par->getCounter(i + par->enumdepth);
1579 switch (par->enumdepth) {
1581 if (par->isRightToLeftPar(buf->params))
1583 << hebrewCounter(number)
1587 << loweralphaCounter(number)
1591 if (par->isRightToLeftPar(buf->params))
1592 s << '.' << romanCounter(number);
1594 s << romanCounter(number) << '.';
1597 if (par->isRightToLeftPar(buf->params))
1599 << alphaCounter(number);
1601 s << alphaCounter(number)
1605 if (par->isRightToLeftPar(buf->params))
1612 par->params().labelString(s.str().c_str());
1614 for (i += par->enumdepth + 1; i < 10; ++i) {
1615 // reset the following counters
1616 par->setCounter(i, 0);
1620 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1621 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1623 int number = par->getCounter(i);
1625 InsetCommandParams p( "bibitem" );
1626 par->bibkey = new InsetBibKey(p);
1628 par->bibkey->setCounter(number);
1629 par->params().labelString(layout.labelstring());
1631 // In biblio should't be following counters but...
1633 string s = layout.labelstring();
1635 // the caption hack:
1636 if (layout.labeltype == LABEL_SENSITIVE) {
1637 bool isOK (par->inInset() && par->inInset()->owner() &&
1638 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1641 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1643 = floatList.getType(tmp->type());
1644 // We should get the correct number here too.
1645 s = fl.name() + " #:";
1647 /* par->SetLayout(0);
1648 s = layout->labelstring; */
1649 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1650 ? " :úåòîùî øñç" : "Senseless: ";
1653 par->params().labelString(s);
1655 /* reset the enumeration counter. They are always resetted
1656 * when there is any other layout between */
1657 for (int i = 6 + par->enumdepth; i < 10; ++i)
1658 par->setCounter(i, 0);
1663 // Updates all counters BEHIND the row. Changed paragraphs
1664 // with a dynamic left margin will be rebroken.
1665 void LyXText::updateCounters(BufferView * bview, Row * row) const
1673 par = row->par()->next();
1677 while (row->par() != par)
1680 setCounter(bview->buffer(), par);
1682 // now check for the headline layouts. remember that they
1683 // have a dynamic left margin
1684 if ((textclasslist.Style(bview->buffer()->params.textclass,
1685 par->layout).margintype == MARGIN_DYNAMIC
1686 || textclasslist.Style(bview->buffer()->params.textclass,
1687 par->layout).labeltype == LABEL_SENSITIVE)) {
1689 // Rebreak the paragraph
1690 removeParagraph(row);
1691 appendParagraph(bview, row);
1698 void LyXText::insertInset(BufferView * bview, Inset * inset)
1700 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1702 // I don't know if this is necessary here (Jug 20020102)
1703 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1704 cursor.par()->insertInset(cursor.pos(), inset);
1705 // Just to rebreak and refresh correctly.
1706 // The character will not be inserted a second time
1707 insertChar(bview, Paragraph::META_INSET);
1709 // If we enter a highly editable inset the cursor should be to before
1710 // the inset. This couldn't happen before as Undo was not handled inside
1711 // inset now after the Undo LyX tries to call inset->Edit(...) again
1712 // and cannot do this as the cursor is behind the inset and GetInset
1713 // does not return the inset!
1714 if (isHighlyEditableInset(inset)) {
1715 cursorLeft(bview, true);
1721 void LyXText::copyEnvironmentType()
1723 copylayouttype = cursor.par()->getLayout();
1727 void LyXText::pasteEnvironmentType(BufferView * bview)
1729 setLayout(bview, copylayouttype);
1733 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1735 // Stuff what we got on the clipboard. Even if there is no selection.
1737 // There is a problem with having the stuffing here in that the
1738 // larger the selection the slower LyX will get. This can be
1739 // solved by running the line below only when the selection has
1740 // finished. The solution used currently just works, to make it
1741 // faster we need to be more clever and probably also have more
1742 // calls to stuffClipboard. (Lgb)
1743 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1745 // This doesn't make sense, if there is no selection
1746 if (!selection.set())
1749 // OK, we have a selection. This is always between selection.start
1750 // and selection.end
1752 // make sure that the depth behind the selection are restored, too
1753 Paragraph * endpar = selection.end.par()->next();
1754 Paragraph * undoendpar = endpar;
1756 if (endpar && endpar->getDepth()) {
1757 while (endpar && endpar->getDepth()) {
1758 endpar = endpar->next();
1759 undoendpar = endpar;
1761 } else if (endpar) {
1762 endpar = endpar->next(); // because of parindents etc.
1765 setUndo(bview, Undo::DELETE,
1766 selection.start.par(), undoendpar);
1768 // there are two cases: cut only within one paragraph or
1769 // more than one paragraph
1770 if (selection.start.par() == selection.end.par()) {
1771 // only within one paragraph
1772 endpar = selection.end.par();
1773 int pos = selection.end.pos();
1774 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1775 selection.start.pos(), pos,
1776 bview->buffer()->params.textclass,
1778 selection.end.pos(pos);
1780 endpar = selection.end.par();
1781 int pos = selection.end.pos();
1782 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1783 selection.start.pos(), pos,
1784 bview->buffer()->params.textclass,
1787 selection.end.par(endpar);
1788 selection.end.pos(pos);
1789 cursor.pos(selection.end.pos());
1791 endpar = endpar->next();
1793 // sometimes necessary
1795 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1797 redoParagraphs(bview, selection.start, endpar);
1799 // cutSelection can invalidate the cursor so we need to set
1801 cursor = selection.start;
1803 // need a valid cursor. (Lgb)
1806 setCursor(bview, cursor.par(), cursor.pos());
1807 selection.cursor = cursor;
1808 updateCounters(bview, cursor.row());
1812 void LyXText::copySelection(BufferView * bview)
1814 // stuff the selection onto the X clipboard, from an explicit copy request
1815 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1817 // this doesnt make sense, if there is no selection
1818 if (!selection.set())
1821 // ok we have a selection. This is always between selection.start
1822 // and sel_end cursor
1824 // copy behind a space if there is one
1825 while (selection.start.par()->size() > selection.start.pos()
1826 && selection.start.par()->isLineSeparator(selection.start.pos())
1827 && (selection.start.par() != selection.end.par()
1828 || selection.start.pos() < selection.end.pos()))
1829 selection.start.pos(selection.start.pos() + 1);
1831 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1832 selection.start.pos(), selection.end.pos(),
1833 bview->buffer()->params.textclass);
1837 void LyXText::pasteSelection(BufferView * bview)
1839 // this does not make sense, if there is nothing to paste
1840 if (!CutAndPaste::checkPastePossible(cursor.par()))
1843 setUndo(bview, Undo::INSERT,
1844 cursor.par(), cursor.par()->next());
1847 Paragraph * actpar = cursor.par();
1848 int pos = cursor.pos();
1850 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1851 bview->buffer()->params.textclass);
1853 redoParagraphs(bview, cursor, endpar);
1855 setCursor(bview, cursor.par(), cursor.pos());
1858 setCursor(bview, actpar, pos);
1859 updateCounters(bview, cursor.row());
1863 // sets the selection over the number of characters of string, no check!!
1864 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1869 selection.cursor = cursor;
1870 for (string::size_type i = 0; i < str.length(); ++i)
1872 setSelection(bview);
1876 // simple replacing. The font of the first selected character is used
1877 void LyXText::replaceSelectionWithString(BufferView * bview,
1880 setCursorParUndo(bview);
1883 if (!selection.set()) { // create a dummy selection
1884 selection.end = cursor;
1885 selection.start = cursor;
1888 // Get font setting before we cut
1889 pos_type pos = selection.end.pos();
1890 LyXFont const font = selection.start.par()
1891 ->getFontSettings(bview->buffer()->params,
1892 selection.start.pos());
1894 // Insert the new string
1895 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1896 selection.end.par()->insertChar(pos, (*cit), font);
1900 // Cut the selection
1901 cutSelection(bview, true, false);
1907 // needed to insert the selection
1908 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1910 Paragraph * par = cursor.par();
1911 pos_type pos = cursor.pos();
1912 Paragraph * endpar = cursor.par()->next();
1914 setCursorParUndo(bview);
1916 // only to be sure, should not be neccessary
1919 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1921 redoParagraphs(bview, cursor, endpar);
1922 setCursor(bview, cursor.par(), cursor.pos());
1923 selection.cursor = cursor;
1924 setCursor(bview, par, pos);
1925 setSelection(bview);
1929 // turns double-CR to single CR, others where converted into one
1930 // blank. Then InsertStringAsLines is called
1931 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1933 string linestr(str);
1934 bool newline_inserted = false;
1935 for (string::size_type i = 0; i < linestr.length(); ++i) {
1936 if (linestr[i] == '\n') {
1937 if (newline_inserted) {
1938 // we know that \r will be ignored by
1939 // InsertStringA. Of course, it is a dirty
1940 // trick, but it works...
1941 linestr[i - 1] = '\r';
1945 newline_inserted = true;
1947 } else if (IsPrintable(linestr[i])) {
1948 newline_inserted = false;
1951 insertStringAsLines(bview, linestr);
1955 bool LyXText::gotoNextInset(BufferView * bview,
1956 std::vector<Inset::Code> const & codes,
1957 string const & contents) const
1959 LyXCursor res = cursor;
1962 if (res.pos() < res.par()->size() - 1) {
1963 res.pos(res.pos() + 1);
1965 res.par(res.par()->next());
1969 } while (res.par() &&
1970 !(res.par()->isInset(res.pos())
1971 && (inset = res.par()->getInset(res.pos())) != 0
1972 && find(codes.begin(), codes.end(), inset->lyxCode())
1974 && (contents.empty() ||
1975 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1979 setCursor(bview, res.par(), res.pos(), false);
1986 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1989 LyXCursor tmpcursor;
1993 Row * row = getRow(par, pos, y);
1995 // is there a break one row above
1996 if (row->previous() && row->previous()->par() == row->par()) {
1997 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1998 if (z >= row->pos()) {
1999 // set the dimensions of the row above
2000 y -= row->previous()->height();
2002 refresh_row = row->previous();
2003 status(bview, LyXText::NEED_MORE_REFRESH);
2005 breakAgain(bview, row->previous());
2007 // set the cursor again. Otherwise
2008 // dangling pointers are possible
2009 setCursor(bview, cursor.par(), cursor.pos(),
2010 false, cursor.boundary());
2011 selection.cursor = cursor;
2016 int const tmpheight = row->height();
2017 pos_type const tmplast = rowLast(row);
2021 breakAgain(bview, row);
2022 if (row->height() == tmpheight && rowLast(row) == tmplast)
2023 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2025 status(bview, LyXText::NEED_MORE_REFRESH);
2027 // check the special right address boxes
2028 if (textclasslist.Style(bview->buffer()->params.textclass,
2029 par->getLayout()).margintype
2030 == MARGIN_RIGHT_ADDRESS_BOX)
2038 redoDrawingOfParagraph(bview, tmpcursor);
2041 // set the cursor again. Otherwise dangling pointers are possible
2042 // also set the selection
2044 if (selection.set()) {
2046 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2047 false, selection.cursor.boundary());
2048 selection.cursor = cursor;
2049 setCursorIntern(bview, selection.start.par(),
2050 selection.start.pos(),
2051 false, selection.start.boundary());
2052 selection.start = cursor;
2053 setCursorIntern(bview, selection.end.par(),
2054 selection.end.pos(),
2055 false, selection.end.boundary());
2056 selection.end = cursor;
2057 setCursorIntern(bview, last_sel_cursor.par(),
2058 last_sel_cursor.pos(),
2059 false, last_sel_cursor.boundary());
2060 last_sel_cursor = cursor;
2063 setCursorIntern(bview, cursor.par(), cursor.pos(),
2064 false, cursor.boundary());
2068 // returns false if inset wasn't found
2069 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2071 // first check the current paragraph
2072 int pos = cursor.par()->getPositionOfInset(inset);
2074 checkParagraph(bview, cursor.par(), pos);
2078 // check every paragraph
2080 Paragraph * par = ownerParagraph();
2082 pos = par->getPositionOfInset(inset);
2084 checkParagraph(bview, par, pos);
2094 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2096 bool setfont, bool boundary) const
2098 LyXCursor old_cursor = cursor;
2099 setCursorIntern(bview, par, pos, setfont, boundary);
2100 return deleteEmptyParagraphMechanism(bview, old_cursor);
2104 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2105 pos_type pos, bool boundary) const
2112 cur.boundary(boundary);
2114 // get the cursor y position in text
2116 Row * row = getRow(par, pos, y);
2117 // y is now the beginning of the cursor row
2118 y += row->baseline();
2119 // y is now the cursor baseline
2122 // now get the cursors x position
2124 float fill_separator;
2126 float fill_label_hfill;
2127 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2129 pos_type cursor_vpos = 0;
2130 pos_type last = rowLastPrintable(row);
2132 if (pos > last + 1) {
2133 // This shouldn't happen.
2136 } else if (pos < row->pos()) {
2141 if (last < row->pos())
2142 cursor_vpos = row->pos();
2143 else if (pos > last && !boundary)
2144 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2145 ? row->pos() : last + 1;
2146 else if (pos > row->pos() &&
2147 (pos > last || boundary))
2148 /// Place cursor after char at (logical) position pos - 1
2149 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2150 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2152 /// Place cursor before char at (logical) position pos
2153 cursor_vpos = (bidi_level(pos) % 2 == 0)
2154 ? log2vis(pos) : log2vis(pos) + 1;
2156 pos_type main_body =
2157 beginningOfMainBody(bview->buffer(), row->par());
2158 if ((main_body > 0) &&
2159 ((main_body-1 > last) ||
2160 !row->par()->isLineSeparator(main_body-1)))
2163 for (pos_type vpos = row->pos();
2164 vpos < cursor_vpos; ++vpos) {
2165 pos = vis2log(vpos);
2166 if (main_body > 0 && pos == main_body - 1) {
2167 x += fill_label_hfill +
2168 lyxfont::width(textclasslist.Style(
2169 bview->buffer()->params.textclass,
2170 row->par()->getLayout())
2172 getLabelFont(bview->buffer(), row->par()));
2173 if (row->par()->isLineSeparator(main_body-1))
2174 x -= singleWidth(bview, row->par(),main_body-1);
2176 if (hfillExpansion(bview->buffer(), row, pos)) {
2177 x += singleWidth(bview, row->par(), pos);
2178 if (pos >= main_body)
2181 x += fill_label_hfill;
2182 } else if (row->par()->isSeparator(pos)) {
2183 x += singleWidth(bview, row->par(), pos);
2184 if (pos >= main_body)
2185 x += fill_separator;
2187 x += singleWidth(bview, row->par(), pos);
2196 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2197 pos_type pos, bool setfont, bool boundary) const
2199 InsetText * it = static_cast<InsetText *>(par->inInset());
2201 if (it != inset_owner) {
2202 lyxerr << "InsetText is " << it << endl;
2203 lyxerr << "inset_owner is " << inset_owner << endl;
2204 #warning I believe this code is wrong. (Lgb)
2205 #warning Jürgen, have a look at this. (Lgb)
2206 #warning Hmmm, I guess you are right but we
2207 #warning should verify when this is needed
2208 // Jürgen, would you like to have a look?
2209 // I guess we need to move the outer cursor
2210 // and open and lock the inset (bla bla bla)
2211 // stuff I don't know... so can you have a look?
2213 // I moved the lyxerr stuff in here so we can see if
2214 // this is actually really needed and where!
2216 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2221 setCursor(bview, cursor, par, pos, boundary);
2223 setCurrentFont(bview);
2227 void LyXText::setCurrentFont(BufferView * bview) const
2229 pos_type pos = cursor.pos();
2230 if (cursor.boundary() && pos > 0)
2234 if (pos == cursor.par()->size())
2236 else // potentional bug... BUG (Lgb)
2237 if (cursor.par()->isSeparator(pos)) {
2238 if (pos > cursor.row()->pos() &&
2239 bidi_level(pos) % 2 ==
2240 bidi_level(pos - 1) % 2)
2242 else if (pos + 1 < cursor.par()->size())
2248 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2249 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2251 if (cursor.pos() == cursor.par()->size() &&
2252 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2253 !cursor.boundary()) {
2254 Language const * lang =
2255 cursor.par()->getParLanguage(bview->buffer()->params);
2256 current_font.setLanguage(lang);
2257 current_font.setNumber(LyXFont::OFF);
2258 real_current_font.setLanguage(lang);
2259 real_current_font.setNumber(LyXFont::OFF);
2264 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2266 LyXCursor old_cursor = cursor;
2268 setCursorFromCoordinates(bview, cursor, x, y);
2269 setCurrentFont(bview);
2270 deleteEmptyParagraphMechanism(bview, old_cursor);
2274 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2277 // Get the row first.
2279 Row * row = getRowNearY(y);
2281 pos_type const column = getColumnNearX(bview, row, x, bound);
2282 cur.par(row->par());
2283 cur.pos(row->pos() + column);
2285 cur.y(y + row->baseline());
2287 cur.boundary(bound);
2291 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2293 if (cursor.pos() > 0) {
2294 bool boundary = cursor.boundary();
2295 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2296 if (!internal && !boundary &&
2297 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2298 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2299 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2300 Paragraph * par = cursor.par()->previous();
2301 setCursor(bview, par, par->size());
2306 void LyXText::cursorRight(BufferView * bview, bool internal) const
2308 if (!internal && cursor.boundary() &&
2309 !cursor.par()->isNewline(cursor.pos()))
2310 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2311 else if (cursor.pos() < cursor.par()->size()) {
2312 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2314 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2315 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2316 } else if (cursor.par()->next())
2317 setCursor(bview, cursor.par()->next(), 0);
2321 void LyXText::cursorUp(BufferView * bview) const
2323 setCursorFromCoordinates(bview, cursor.x_fix(),
2324 cursor.y() - cursor.row()->baseline() - 1);
2328 void LyXText::cursorDown(BufferView * bview) const
2330 setCursorFromCoordinates(bview, cursor.x_fix(),
2331 cursor.y() - cursor.row()->baseline()
2332 + cursor.row()->height() + 1);
2336 void LyXText::cursorUpParagraph(BufferView * bview) const
2338 if (cursor.pos() > 0) {
2339 setCursor(bview, cursor.par(), 0);
2341 else if (cursor.par()->previous()) {
2342 setCursor(bview, cursor.par()->previous(), 0);
2347 void LyXText::cursorDownParagraph(BufferView * bview) const
2349 if (cursor.par()->next()) {
2350 setCursor(bview, cursor.par()->next(), 0);
2352 setCursor(bview, cursor.par(), cursor.par()->size());
2356 // fix the cursor `cur' after a characters has been deleted at `where'
2357 // position. Called by deleteEmptyParagraphMechanism
2358 void LyXText::fixCursorAfterDelete(BufferView * bview,
2360 LyXCursor const & where) const
2362 // if cursor is not in the paragraph where the delete occured,
2364 if (cur.par() != where.par())
2367 // if cursor position is after the place where the delete occured,
2369 if (cur.pos() > where.pos())
2370 cur.pos(cur.pos()-1);
2372 // recompute row et al. for this cursor
2373 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2377 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2378 LyXCursor const & old_cursor) const
2380 // Would be wrong to delete anything if we have a selection.
2381 if (selection.set()) return false;
2383 // We allow all kinds of "mumbo-jumbo" when freespacing.
2384 if (textclasslist.Style(bview->buffer()->params.textclass,
2385 old_cursor.par()->getLayout()).free_spacing
2386 || old_cursor.par()->isFreeSpacing())
2391 /* Ok I'll put some comments here about what is missing.
2392 I have fixed BackSpace (and thus Delete) to not delete
2393 double-spaces automagically. I have also changed Cut,
2394 Copy and Paste to hopefully do some sensible things.
2395 There are still some small problems that can lead to
2396 double spaces stored in the document file or space at
2397 the beginning of paragraphs. This happens if you have
2398 the cursor betwenn to spaces and then save. Or if you
2399 cut and paste and the selection have a space at the
2400 beginning and then save right after the paste. I am
2401 sure none of these are very hard to fix, but I will
2402 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2403 that I can get some feedback. (Lgb)
2406 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2407 // delete the LineSeparator.
2410 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2411 // delete the LineSeparator.
2414 // If the pos around the old_cursor were spaces, delete one of them.
2415 if (old_cursor.par() != cursor.par()
2416 || old_cursor.pos() != cursor.pos()) {
2417 // Only if the cursor has really moved
2419 if (old_cursor.pos() > 0
2420 && old_cursor.pos() < old_cursor.par()->size()
2421 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2422 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2423 old_cursor.par()->erase(old_cursor.pos() - 1);
2424 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2426 #ifdef WITH_WARNINGS
2427 #warning This will not work anymore when we have multiple views of the same buffer
2428 // In this case, we will have to correct also the cursors held by
2429 // other bufferviews. It will probably be easier to do that in a more
2430 // automated way in LyXCursor code. (JMarc 26/09/2001)
2432 // correct all cursors held by the LyXText
2433 fixCursorAfterDelete(bview, cursor, old_cursor);
2434 fixCursorAfterDelete(bview, selection.cursor,
2436 fixCursorAfterDelete(bview, selection.start,
2438 fixCursorAfterDelete(bview, selection.end, old_cursor);
2439 fixCursorAfterDelete(bview, last_sel_cursor,
2441 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2442 fixCursorAfterDelete(bview, toggle_end_cursor,
2448 // don't delete anything if this is the ONLY paragraph!
2449 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2452 // Do not delete empty paragraphs with keepempty set.
2453 if ((textclasslist.Style(bview->buffer()->params.textclass,
2454 old_cursor.par()->getLayout())).keepempty)
2457 // only do our magic if we changed paragraph
2458 if (old_cursor.par() == cursor.par())
2461 // record if we have deleted a paragraph
2462 // we can't possibly have deleted a paragraph before this point
2463 bool deleted = false;
2465 if ((old_cursor.par()->size() == 0
2466 || (old_cursor.par()->size() == 1
2467 && old_cursor.par()->isLineSeparator(0)))) {
2468 // ok, we will delete anything
2469 LyXCursor tmpcursor;
2471 // make sure that you do not delete any environments
2472 status(bview, LyXText::NEED_MORE_REFRESH);
2475 if (old_cursor.row()->previous()) {
2476 refresh_row = old_cursor.row()->previous();
2477 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2479 cursor = old_cursor; // that undo can restore the right cursor position
2480 Paragraph * endpar = old_cursor.par()->next();
2481 if (endpar && endpar->getDepth()) {
2482 while (endpar && endpar->getDepth()) {
2483 endpar = endpar->next();
2486 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2490 removeRow(old_cursor.row());
2491 if (ownerParagraph() == old_cursor.par()) {
2492 ownerParagraph(ownerParagraph()->next());
2495 delete old_cursor.par();
2497 /* Breakagain the next par. Needed because of
2498 * the parindent that can occur or dissappear.
2499 * The next row can change its height, if
2500 * there is another layout before */
2501 if (refresh_row->next()) {
2502 breakAgain(bview, refresh_row->next());
2503 updateCounters(bview, refresh_row);
2505 setHeightOfRow(bview, refresh_row);
2507 refresh_row = old_cursor.row()->next();
2508 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2511 cursor = old_cursor; // that undo can restore the right cursor position
2512 Paragraph * endpar = old_cursor.par()->next();
2513 if (endpar && endpar->getDepth()) {
2514 while (endpar && endpar->getDepth()) {
2515 endpar = endpar->next();
2518 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2522 removeRow(old_cursor.row());
2524 if (ownerParagraph() == old_cursor.par()) {
2525 ownerParagraph(ownerParagraph()->next());
2528 delete old_cursor.par();
2530 /* Breakagain the next par. Needed because of
2531 the parindent that can occur or dissappear.
2532 The next row can change its height, if
2533 there is another layout before */
2535 breakAgain(bview, refresh_row);
2536 updateCounters(bview, refresh_row->previous());
2541 setCursorIntern(bview, cursor.par(), cursor.pos());
2543 if (selection.cursor.par() == old_cursor.par()
2544 && selection.cursor.pos() == old_cursor.pos()) {
2545 // correct selection
2546 selection.cursor = cursor;
2550 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2551 redoParagraphs(bview, old_cursor,
2552 old_cursor.par()->next());
2554 setCursorIntern(bview, cursor.par(), cursor.pos());
2555 selection.cursor = cursor;
2562 void LyXText::toggleAppendix(BufferView * bview)
2564 Paragraph * par = cursor.par();
2565 bool start = !par->params().startOfAppendix();
2567 // ensure that we have only one start_of_appendix in this document
2568 Paragraph * tmp = ownerParagraph();
2569 for (; tmp; tmp = tmp->next()) {
2570 tmp->params().startOfAppendix(false);
2573 par->params().startOfAppendix(start);
2575 // we can set the refreshing parameters now
2576 status(bview, LyXText::NEED_MORE_REFRESH);
2578 refresh_row = 0; // not needed for full update
2579 updateCounters(bview, 0);
2580 setCursor(bview, cursor.par(), cursor.pos());
2584 Paragraph * LyXText::ownerParagraph() const
2587 return inset_owner->paragraph();
2589 return bv_owner->buffer()->paragraph;
2593 void LyXText::ownerParagraph(Paragraph * p) const
2596 inset_owner->paragraph(p);
2598 bv_owner->buffer()->paragraph = p;
2603 void LyXText::ownerParagraph(int id, Paragraph * p) const
2605 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2606 if (op && op->inInset()) {
2607 static_cast<InsetText *>(op->inInset())->paragraph(p);
2614 LyXText::text_status LyXText::status() const
2620 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2622 // well as much as I know && binds more then || so the above and the
2623 // below are identical (this for your known use of parentesis!)
2624 // Now some explanation:
2625 // We should only go up with refreshing code so this means that if
2626 // we have a MORE refresh we should never set it to LITTLE if we still
2627 // didn't handle it (and then it will be UNCHANGED. Now as long as
2628 // we stay inside one LyXText this may work but we need to tell the
2629 // outermost LyXText that it should REALLY draw us if there is some
2630 // change in a Inset::LyXText. So you see that when we are inside a
2631 // inset's LyXText we give the LITTLE to the outermost LyXText to
2632 // tell'em that it should redraw the actual row (where the inset
2633 // resides! Capito?!
2635 if ((status_ != NEED_MORE_REFRESH)
2636 || (status_ == NEED_MORE_REFRESH
2637 && st != NEED_VERY_LITTLE_REFRESH))
2640 if (inset_owner && st != UNCHANGED) {
2641 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2642 if (!bview->text->refresh_row) {
2643 bview->text->refresh_row = bview->text->cursor.row();
2644 bview->text->refresh_y = bview->text->cursor.y() -
2645 bview->text->cursor.row()->baseline();