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);
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 if (cur.par() != send_cur.par())
568 cur.par(cur.par()->next());
569 } while (cur.par() != send_cur.par());
575 // set layout over selection and make a total rebreak of those paragraphs
576 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
578 LyXCursor tmpcursor = cursor; /* store the current cursor */
580 // if there is no selection just set the layout
581 // of the current paragraph */
582 if (!selection.set()) {
583 selection.start = cursor; // dummy selection
584 selection.end = cursor;
586 Paragraph * endpar = setLayout(bview, cursor, selection.start,
587 selection.end, layout);
588 redoParagraphs(bview, selection.start, endpar);
590 // we have to reset the selection, because the
591 // geometry could have changed
592 setCursor(bview, selection.start.par(),
593 selection.start.pos(), false);
594 selection.cursor = cursor;
595 setCursor(bview, selection.end.par(), selection.end.pos(), false);
596 updateCounters(bview, cursor.row());
599 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
603 // increment depth over selection and
604 // make a total rebreak of those paragraphs
605 void LyXText::incDepth(BufferView * bview)
607 // If there is no selection, just use the current paragraph
608 if (!selection.set()) {
609 selection.start = cursor; // dummy selection
610 selection.end = cursor;
613 // We end at the next paragraph with depth 0
614 Paragraph * endpar = selection.end.par()->next();
616 Paragraph * undoendpar = endpar;
618 if (endpar && endpar->getDepth()) {
619 while (endpar && endpar->getDepth()) {
620 endpar = endpar->next();
624 endpar = endpar->next(); // because of parindents etc.
627 setUndo(bview, Undo::EDIT,
628 selection.start.par(), undoendpar);
630 LyXCursor tmpcursor = cursor; // store the current cursor
632 // ok we have a selection. This is always between sel_start_cursor
633 // and sel_end cursor
634 cursor = selection.start;
636 bool anything_changed = false;
639 // NOTE: you can't change the depth of a bibliography entry
640 if (textclasslist.Style(bview->buffer()->params.textclass,
641 cursor.par()->getLayout()).labeltype != LABEL_BIBLIO) {
642 Paragraph * prev = cursor.par()->previous();
646 = prev->getDepth() - cursor.par()->getDepth();
648 // (1) the previous para is already
649 // deeper (depth_diff > 0)
650 // (2) the previous para is a
651 // list-environment at the same
652 // depth as this para.
653 if (depth_diff > 0 || (depth_diff > -1
654 && textclasslist.Style(bview->buffer()->params.textclass,
655 prev->getLayout()).isEnvironment())) {
656 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
657 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 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 #ifdef WITH_WARNINGS
2205 #warning I believe this code is wrong. (Lgb)
2206 #warning Jürgen, have a look at this. (Lgb)
2207 #warning Hmmm, I guess you are right but we
2208 #warning should verify when this is needed
2210 // Jürgen, would you like to have a look?
2211 // I guess we need to move the outer cursor
2212 // and open and lock the inset (bla bla bla)
2213 // stuff I don't know... so can you have a look?
2215 // I moved the lyxerr stuff in here so we can see if
2216 // this is actually really needed and where!
2218 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2223 setCursor(bview, cursor, par, pos, boundary);
2225 setCurrentFont(bview);
2229 void LyXText::setCurrentFont(BufferView * bview) const
2231 pos_type pos = cursor.pos();
2232 if (cursor.boundary() && pos > 0)
2236 if (pos == cursor.par()->size())
2238 else // potentional bug... BUG (Lgb)
2239 if (cursor.par()->isSeparator(pos)) {
2240 if (pos > cursor.row()->pos() &&
2241 bidi_level(pos) % 2 ==
2242 bidi_level(pos - 1) % 2)
2244 else if (pos + 1 < cursor.par()->size())
2250 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2251 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2253 if (cursor.pos() == cursor.par()->size() &&
2254 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2255 !cursor.boundary()) {
2256 Language const * lang =
2257 cursor.par()->getParLanguage(bview->buffer()->params);
2258 current_font.setLanguage(lang);
2259 current_font.setNumber(LyXFont::OFF);
2260 real_current_font.setLanguage(lang);
2261 real_current_font.setNumber(LyXFont::OFF);
2266 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2268 LyXCursor old_cursor = cursor;
2270 setCursorFromCoordinates(bview, cursor, x, y);
2271 setCurrentFont(bview);
2272 deleteEmptyParagraphMechanism(bview, old_cursor);
2276 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2279 // Get the row first.
2281 Row * row = getRowNearY(y);
2283 pos_type const column = getColumnNearX(bview, row, x, bound);
2284 cur.par(row->par());
2285 cur.pos(row->pos() + column);
2287 cur.y(y + row->baseline());
2289 cur.boundary(bound);
2293 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2295 if (cursor.pos() > 0) {
2296 bool boundary = cursor.boundary();
2297 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2298 if (!internal && !boundary &&
2299 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2300 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2301 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2302 Paragraph * par = cursor.par()->previous();
2303 setCursor(bview, par, par->size());
2308 void LyXText::cursorRight(BufferView * bview, bool internal) const
2310 if (!internal && cursor.boundary() &&
2311 !cursor.par()->isNewline(cursor.pos()))
2312 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2313 else if (cursor.pos() < cursor.par()->size()) {
2314 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2316 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2317 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2318 } else if (cursor.par()->next())
2319 setCursor(bview, cursor.par()->next(), 0);
2323 void LyXText::cursorUp(BufferView * bview) const
2325 setCursorFromCoordinates(bview, cursor.x_fix(),
2326 cursor.y() - cursor.row()->baseline() - 1);
2330 void LyXText::cursorDown(BufferView * bview) const
2332 setCursorFromCoordinates(bview, cursor.x_fix(),
2333 cursor.y() - cursor.row()->baseline()
2334 + cursor.row()->height() + 1);
2338 void LyXText::cursorUpParagraph(BufferView * bview) const
2340 if (cursor.pos() > 0) {
2341 setCursor(bview, cursor.par(), 0);
2343 else if (cursor.par()->previous()) {
2344 setCursor(bview, cursor.par()->previous(), 0);
2349 void LyXText::cursorDownParagraph(BufferView * bview) const
2351 if (cursor.par()->next()) {
2352 setCursor(bview, cursor.par()->next(), 0);
2354 setCursor(bview, cursor.par(), cursor.par()->size());
2358 // fix the cursor `cur' after a characters has been deleted at `where'
2359 // position. Called by deleteEmptyParagraphMechanism
2360 void LyXText::fixCursorAfterDelete(BufferView * bview,
2362 LyXCursor const & where) const
2364 // if cursor is not in the paragraph where the delete occured,
2366 if (cur.par() != where.par())
2369 // if cursor position is after the place where the delete occured,
2371 if (cur.pos() > where.pos())
2372 cur.pos(cur.pos()-1);
2374 // recompute row et al. for this cursor
2375 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2379 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2380 LyXCursor const & old_cursor) const
2382 // Would be wrong to delete anything if we have a selection.
2383 if (selection.set()) return false;
2385 // We allow all kinds of "mumbo-jumbo" when freespacing.
2386 if (textclasslist.Style(bview->buffer()->params.textclass,
2387 old_cursor.par()->getLayout()).free_spacing
2388 || old_cursor.par()->isFreeSpacing())
2393 /* Ok I'll put some comments here about what is missing.
2394 I have fixed BackSpace (and thus Delete) to not delete
2395 double-spaces automagically. I have also changed Cut,
2396 Copy and Paste to hopefully do some sensible things.
2397 There are still some small problems that can lead to
2398 double spaces stored in the document file or space at
2399 the beginning of paragraphs. This happens if you have
2400 the cursor betwenn to spaces and then save. Or if you
2401 cut and paste and the selection have a space at the
2402 beginning and then save right after the paste. I am
2403 sure none of these are very hard to fix, but I will
2404 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2405 that I can get some feedback. (Lgb)
2408 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2409 // delete the LineSeparator.
2412 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2413 // delete the LineSeparator.
2416 // If the pos around the old_cursor were spaces, delete one of them.
2417 if (old_cursor.par() != cursor.par()
2418 || old_cursor.pos() != cursor.pos()) {
2419 // Only if the cursor has really moved
2421 if (old_cursor.pos() > 0
2422 && old_cursor.pos() < old_cursor.par()->size()
2423 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2424 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2425 old_cursor.par()->erase(old_cursor.pos() - 1);
2426 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2428 #ifdef WITH_WARNINGS
2429 #warning This will not work anymore when we have multiple views of the same buffer
2430 // In this case, we will have to correct also the cursors held by
2431 // other bufferviews. It will probably be easier to do that in a more
2432 // automated way in LyXCursor code. (JMarc 26/09/2001)
2434 // correct all cursors held by the LyXText
2435 fixCursorAfterDelete(bview, cursor, old_cursor);
2436 fixCursorAfterDelete(bview, selection.cursor,
2438 fixCursorAfterDelete(bview, selection.start,
2440 fixCursorAfterDelete(bview, selection.end, old_cursor);
2441 fixCursorAfterDelete(bview, last_sel_cursor,
2443 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2444 fixCursorAfterDelete(bview, toggle_end_cursor,
2450 // don't delete anything if this is the ONLY paragraph!
2451 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2454 // Do not delete empty paragraphs with keepempty set.
2455 if ((textclasslist.Style(bview->buffer()->params.textclass,
2456 old_cursor.par()->getLayout())).keepempty)
2459 // only do our magic if we changed paragraph
2460 if (old_cursor.par() == cursor.par())
2463 // record if we have deleted a paragraph
2464 // we can't possibly have deleted a paragraph before this point
2465 bool deleted = false;
2467 if ((old_cursor.par()->size() == 0
2468 || (old_cursor.par()->size() == 1
2469 && old_cursor.par()->isLineSeparator(0)))) {
2470 // ok, we will delete anything
2471 LyXCursor tmpcursor;
2473 // make sure that you do not delete any environments
2474 status(bview, LyXText::NEED_MORE_REFRESH);
2477 if (old_cursor.row()->previous()) {
2478 refresh_row = old_cursor.row()->previous();
2479 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2481 cursor = old_cursor; // that undo can restore the right cursor position
2482 Paragraph * endpar = old_cursor.par()->next();
2483 if (endpar && endpar->getDepth()) {
2484 while (endpar && endpar->getDepth()) {
2485 endpar = endpar->next();
2488 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2492 removeRow(old_cursor.row());
2493 if (ownerParagraph() == old_cursor.par()) {
2494 ownerParagraph(ownerParagraph()->next());
2497 delete old_cursor.par();
2499 /* Breakagain the next par. Needed because of
2500 * the parindent that can occur or dissappear.
2501 * The next row can change its height, if
2502 * there is another layout before */
2503 if (refresh_row->next()) {
2504 breakAgain(bview, refresh_row->next());
2505 updateCounters(bview, refresh_row);
2507 setHeightOfRow(bview, refresh_row);
2509 refresh_row = old_cursor.row()->next();
2510 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2513 cursor = old_cursor; // that undo can restore the right cursor position
2514 Paragraph * endpar = old_cursor.par()->next();
2515 if (endpar && endpar->getDepth()) {
2516 while (endpar && endpar->getDepth()) {
2517 endpar = endpar->next();
2520 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2524 removeRow(old_cursor.row());
2526 if (ownerParagraph() == old_cursor.par()) {
2527 ownerParagraph(ownerParagraph()->next());
2530 delete old_cursor.par();
2532 /* Breakagain the next par. Needed because of
2533 the parindent that can occur or dissappear.
2534 The next row can change its height, if
2535 there is another layout before */
2537 breakAgain(bview, refresh_row);
2538 updateCounters(bview, refresh_row->previous());
2543 setCursorIntern(bview, cursor.par(), cursor.pos());
2545 if (selection.cursor.par() == old_cursor.par()
2546 && selection.cursor.pos() == old_cursor.pos()) {
2547 // correct selection
2548 selection.cursor = cursor;
2552 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2553 redoParagraphs(bview, old_cursor,
2554 old_cursor.par()->next());
2556 setCursorIntern(bview, cursor.par(), cursor.pos());
2557 selection.cursor = cursor;
2564 void LyXText::toggleAppendix(BufferView * bview)
2566 Paragraph * par = cursor.par();
2567 bool start = !par->params().startOfAppendix();
2569 // ensure that we have only one start_of_appendix in this document
2570 Paragraph * tmp = ownerParagraph();
2571 for (; tmp; tmp = tmp->next()) {
2572 tmp->params().startOfAppendix(false);
2575 par->params().startOfAppendix(start);
2577 // we can set the refreshing parameters now
2578 status(bview, LyXText::NEED_MORE_REFRESH);
2580 refresh_row = 0; // not needed for full update
2581 updateCounters(bview, 0);
2582 setCursor(bview, cursor.par(), cursor.pos());
2586 Paragraph * LyXText::ownerParagraph() const
2589 return inset_owner->paragraph();
2591 return bv_owner->buffer()->paragraph;
2595 void LyXText::ownerParagraph(Paragraph * p) const
2598 inset_owner->paragraph(p);
2600 bv_owner->buffer()->paragraph = p;
2605 void LyXText::ownerParagraph(int id, Paragraph * p) const
2607 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2608 if (op && op->inInset()) {
2609 static_cast<InsetText *>(op->inInset())->paragraph(p);
2616 LyXText::text_status LyXText::status() const
2622 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2624 // well as much as I know && binds more then || so the above and the
2625 // below are identical (this for your known use of parentesis!)
2626 // Now some explanation:
2627 // We should only go up with refreshing code so this means that if
2628 // we have a MORE refresh we should never set it to LITTLE if we still
2629 // didn't handle it (and then it will be UNCHANGED. Now as long as
2630 // we stay inside one LyXText this may work but we need to tell the
2631 // outermost LyXText that it should REALLY draw us if there is some
2632 // change in a Inset::LyXText. So you see that when we are inside a
2633 // inset's LyXText we give the LITTLE to the outermost LyXText to
2634 // tell'em that it should redraw the actual row (where the inset
2635 // resides! Capito?!
2637 if ((status_ != NEED_MORE_REFRESH)
2638 || (status_ == NEED_MORE_REFRESH
2639 && st != NEED_VERY_LITTLE_REFRESH))
2642 if (inset_owner && st != UNCHANGED) {
2643 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2644 if (!bview->text->refresh_row) {
2645 bview->text->refresh_row = bview->text->cursor.row();
2646 bview->text->refresh_y = bview->text->cursor.y() -
2647 bview->text->cursor.row()->baseline();