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
393 row->next()->previous(row->previous());
394 if (!row->previous()) {
395 firstrow = row->next();
396 // lyx::Assert(firstrow);
398 row->previous()->next(row->next());
401 lastrow = row->previous();
403 height -= row->height(); // the text becomes smaller
406 --number_of_rows; // one row less
410 // remove all following rows of the paragraph of the specified row.
411 void LyXText::removeParagraph(Row * row) const
413 Paragraph * tmppar = row->par();
417 while (row && row->par() == tmppar) {
418 tmprow = row->next();
425 // insert the specified paragraph behind the specified row
426 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
429 insertRow(row, par, 0); /* insert a new row, starting
432 setCounter(bview->buffer(), par); // set the counters
434 // and now append the whole paragraph behind the new row
437 appendParagraph(bview, firstrow);
439 row->next()->height(0);
440 appendParagraph(bview, row->next());
445 Inset * LyXText::getInset() const
448 if (cursor.pos() == 0 && cursor.par()->bibkey) {
449 inset = cursor.par()->bibkey;
450 } else if (cursor.pos() < cursor.par()->size()
451 && cursor.par()->isInset(cursor.pos())) {
452 inset = cursor.par()->getInset(cursor.pos());
458 void LyXText::toggleInset(BufferView * bview)
460 Inset * inset = getInset();
461 if (!isEditableInset(inset))
463 //bview->owner()->message(inset->editMessage());
465 // do we want to keep this?? (JMarc)
466 if (!isHighlyEditableInset(inset))
467 setCursorParUndo(bview);
469 if (inset->isOpen()) {
475 inset->open(bview, !inset->isOpen());
480 /* used in setlayout */
481 // Asger is not sure we want to do this...
482 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
485 LyXLayout const & layout =
486 textclasslist.Style(buf->params.textclass, par->getLayout());
489 for (pos_type pos = 0; pos < par->size(); ++pos) {
490 if (pos < beginningOfMainBody(buf, par))
491 layoutfont = layout.labelfont;
493 layoutfont = layout.font;
495 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
496 tmpfont.reduce(layoutfont);
497 par->setFont(pos, tmpfont);
502 Paragraph * LyXText::setLayout(BufferView * bview,
503 LyXCursor & cur, LyXCursor & sstart_cur,
504 LyXCursor & send_cur,
505 lyx::layout_type layout)
507 Paragraph * endpar = send_cur.par()->next();
508 Paragraph * undoendpar = endpar;
510 if (endpar && endpar->getDepth()) {
511 while (endpar && endpar->getDepth()) {
512 endpar = endpar->next();
516 endpar = endpar->next(); // because of parindents etc.
519 setUndo(bview, Undo::EDIT,
520 sstart_cur.par(), undoendpar);
522 // ok we have a selection. This is always between sstart_cur
523 // and sel_end cursor
526 LyXLayout const & lyxlayout =
527 textclasslist.Style(bview->buffer()->params.textclass, layout);
529 while (cur.par() != send_cur.par()) {
530 cur.par()->setLayout(layout);
531 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
532 Paragraph * fppar = cur.par();
533 fppar->params().spaceTop(lyxlayout.fill_top ?
534 VSpace(VSpace::VFILL)
535 : VSpace(VSpace::NONE));
536 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
537 VSpace(VSpace::VFILL)
538 : VSpace(VSpace::NONE));
539 if (lyxlayout.margintype == MARGIN_MANUAL)
540 cur.par()->setLabelWidthString(lyxlayout.labelstring());
541 if (lyxlayout.labeltype != LABEL_BIBLIO
543 delete fppar->bibkey;
546 cur.par(cur.par()->next());
548 cur.par()->setLayout(layout);
549 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
550 Paragraph * fppar = cur.par();
551 fppar->params().spaceTop(lyxlayout.fill_top ?
552 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
553 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
554 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
555 if (lyxlayout.margintype == MARGIN_MANUAL)
556 cur.par()->setLabelWidthString(lyxlayout.labelstring());
557 if (lyxlayout.labeltype != LABEL_BIBLIO
559 delete fppar->bibkey;
566 // set layout over selection and make a total rebreak of those paragraphs
567 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
569 LyXCursor tmpcursor = cursor; /* store the current cursor */
571 // if there is no selection just set the layout
572 // of the current paragraph */
573 if (!selection.set()) {
574 selection.start = cursor; // dummy selection
575 selection.end = cursor;
577 Paragraph * endpar = setLayout(bview, cursor, selection.start,
578 selection.end, layout);
579 redoParagraphs(bview, selection.start, endpar);
581 // we have to reset the selection, because the
582 // geometry could have changed
583 setCursor(bview, selection.start.par(),
584 selection.start.pos(), false);
585 selection.cursor = cursor;
586 setCursor(bview, selection.end.par(), selection.end.pos(), false);
587 updateCounters(bview, cursor.row());
590 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
594 // increment depth over selection and
595 // make a total rebreak of those paragraphs
596 void LyXText::incDepth(BufferView * bview)
598 // If there is no selection, just use the current paragraph
599 if (!selection.set()) {
600 selection.start = cursor; // dummy selection
601 selection.end = cursor;
604 // We end at the next paragraph with depth 0
605 Paragraph * endpar = selection.end.par()->next();
607 Paragraph * undoendpar = endpar;
609 if (endpar && endpar->getDepth()) {
610 while (endpar && endpar->getDepth()) {
611 endpar = endpar->next();
615 endpar = endpar->next(); // because of parindents etc.
618 setUndo(bview, Undo::EDIT,
619 selection.start.par(), undoendpar);
621 LyXCursor tmpcursor = cursor; // store the current cursor
623 // ok we have a selection. This is always between sel_start_cursor
624 // and sel_end cursor
625 cursor = selection.start;
627 bool anything_changed = false;
630 // NOTE: you can't change the depth of a bibliography entry
632 textclasslist.Style(bview->buffer()->params.textclass,
633 cursor.par()->getLayout()
634 ).labeltype != LABEL_BIBLIO) {
635 Paragraph * prev = cursor.par()->previous();
638 && (prev->getDepth() - cursor.par()->getDepth() > 0
639 || (prev->getDepth() == cursor.par()->getDepth()
640 && textclasslist.Style(bview->buffer()->params.textclass,
641 prev->getLayout()).isEnvironment()))) {
642 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
643 anything_changed = true;
646 if (cursor.par() == selection.end.par())
648 cursor.par(cursor.par()->next());
651 // if nothing changed set all depth to 0
652 if (!anything_changed) {
653 cursor = selection.start;
654 while (cursor.par() != selection.end.par()) {
655 cursor.par()->params().depth(0);
656 cursor.par(cursor.par()->next());
658 cursor.par()->params().depth(0);
661 redoParagraphs(bview, selection.start, endpar);
663 // we have to reset the selection, because the
664 // geometry could have changed
665 setCursor(bview, selection.start.par(), selection.start.pos());
666 selection.cursor = cursor;
667 setCursor(bview, selection.end.par(), selection.end.pos());
668 updateCounters(bview, cursor.row());
671 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
675 // decrement depth over selection and
676 // make a total rebreak of those paragraphs
677 void LyXText::decDepth(BufferView * bview)
679 // if there is no selection just set the layout
680 // of the current paragraph
681 if (!selection.set()) {
682 selection.start = cursor; // dummy selection
683 selection.end = cursor;
685 Paragraph * endpar = selection.end.par()->next();
686 Paragraph * undoendpar = endpar;
688 if (endpar && endpar->getDepth()) {
689 while (endpar && endpar->getDepth()) {
690 endpar = endpar->next();
694 endpar = endpar->next(); // because of parindents etc.
697 setUndo(bview, Undo::EDIT,
698 selection.start.par(), undoendpar);
700 LyXCursor tmpcursor = cursor; // store the current cursor
702 // ok we have a selection. This is always between sel_start_cursor
703 // and sel_end cursor
704 cursor = selection.start;
707 if (cursor.par()->params().depth()) {
708 cursor.par()->params()
709 .depth(cursor.par()->params().depth() - 1);
711 if (cursor.par() == selection.end.par()) {
714 cursor.par(cursor.par()->next());
717 redoParagraphs(bview, selection.start, endpar);
719 // we have to reset the selection, because the
720 // geometry could have changed
721 setCursor(bview, selection.start.par(),
722 selection.start.pos());
723 selection.cursor = cursor;
724 setCursor(bview, selection.end.par(), selection.end.pos());
725 updateCounters(bview, cursor.row());
728 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
732 // set font over selection and make a total rebreak of those paragraphs
733 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
735 // if there is no selection just set the current_font
736 if (!selection.set()) {
737 // Determine basis font
739 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
741 layoutfont = getLabelFont(bview->buffer(),
744 layoutfont = getLayoutFont(bview->buffer(),
747 // Update current font
748 real_current_font.update(font,
749 bview->buffer()->params.language,
752 // Reduce to implicit settings
753 current_font = real_current_font;
754 current_font.reduce(layoutfont);
755 // And resolve it completely
756 #ifndef INHERIT_LANGUAGE
757 real_current_font.realize(layoutfont);
759 real_current_font.realize(layoutfont,
760 bview->buffer()->params.language);
765 LyXCursor tmpcursor = cursor; // store the current cursor
767 // ok we have a selection. This is always between sel_start_cursor
768 // and sel_end cursor
770 setUndo(bview, Undo::EDIT,
771 selection.start.par(), selection.end.par()->next());
773 cursor = selection.start;
774 while (cursor.par() != selection.end.par() ||
775 (cursor.pos() < selection.end.pos()))
777 if (cursor.pos() < cursor.par()->size()) {
778 // an open footnote should behave
780 setCharFont(bview, cursor.par(), cursor.pos(),
782 cursor.pos(cursor.pos() + 1);
785 cursor.par(cursor.par()->next());
790 redoParagraphs(bview, selection.start, selection.end.par()->next());
792 // we have to reset the selection, because the
793 // geometry could have changed, but we keep
794 // it for user convenience
795 setCursor(bview, selection.start.par(), selection.start.pos());
796 selection.cursor = cursor;
797 setCursor(bview, selection.end.par(), selection.end.pos());
799 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
800 tmpcursor.boundary());
804 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
806 Row * tmprow = cur.row();
807 int y = cur.y() - tmprow->baseline();
809 setHeightOfRow(bview, tmprow);
811 while (tmprow->previous()
812 && tmprow->previous()->par() == tmprow->par()) {
813 tmprow = tmprow->previous();
814 y -= tmprow->height();
815 setHeightOfRow(bview, tmprow);
818 // we can set the refreshing parameters now
819 status(bview, LyXText::NEED_MORE_REFRESH);
821 refresh_row = tmprow;
822 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
826 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
828 Row * tmprow = cur.row();
830 int y = cur.y() - tmprow->baseline();
831 setHeightOfRow(bview, tmprow);
833 while (tmprow->previous()
834 && tmprow->previous()->par() == tmprow->par()) {
835 tmprow = tmprow->previous();
836 y -= tmprow->height();
839 // we can set the refreshing parameters now
840 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
842 refresh_row = tmprow;
844 status(bview, LyXText::NEED_MORE_REFRESH);
845 setCursor(bview, cur.par(), cur.pos());
849 // deletes and inserts again all paragaphs between the cursor
850 // and the specified par
851 // This function is needed after SetLayout and SetFont etc.
852 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
853 Paragraph const * endpar) const
856 Paragraph * tmppar = 0;
857 Paragraph * first_phys_par = 0;
859 Row * tmprow = cur.row();
861 int y = cur.y() - tmprow->baseline();
863 if (!tmprow->previous()) {
864 // a trick/hack for UNDO
865 // This is needed because in an UNDO/REDO we could have changed
866 // the firstParagrah() so the paragraph inside the row is NOT
867 // my really first par anymore. Got it Lars ;) (Jug 20011206)
868 first_phys_par = firstParagraph();
870 first_phys_par = tmprow->par();
871 while (tmprow->previous()
872 && tmprow->previous()->par() == first_phys_par)
874 tmprow = tmprow->previous();
875 y -= tmprow->height();
879 // we can set the refreshing parameters now
880 status(bview, LyXText::NEED_MORE_REFRESH);
882 refresh_row = tmprow->previous(); /* the real refresh row will
883 be deleted, so I store
887 tmppar = tmprow->next()->par();
890 while (tmprow->next() && tmppar != endpar) {
891 removeRow(tmprow->next());
892 if (tmprow->next()) {
893 tmppar = tmprow->next()->par();
899 // remove the first one
900 tmprow2 = tmprow; /* this is because tmprow->previous()
902 tmprow = tmprow->previous();
905 tmppar = first_phys_par;
909 insertParagraph(bview, tmppar, tmprow);
913 while (tmprow->next()
914 && tmprow->next()->par() == tmppar) {
915 tmprow = tmprow->next();
917 tmppar = tmppar->next();
919 } while (tmppar && tmppar != endpar);
921 // this is because of layout changes
923 refresh_y -= refresh_row->height();
924 setHeightOfRow(bview, refresh_row);
926 refresh_row = firstrow;
928 setHeightOfRow(bview, refresh_row);
931 if (tmprow && tmprow->next())
932 setHeightOfRow(bview, tmprow->next());
936 bool LyXText::fullRebreak(BufferView * bview)
942 if (need_break_row) {
943 breakAgain(bview, need_break_row);
951 // important for the screen
954 /* the cursor set functions have a special mechanism. When they
955 * realize, that you left an empty paragraph, they will delete it.
956 * They also delete the corresponding row */
958 // need the selection cursor:
959 void LyXText::setSelection(BufferView * bview)
961 bool const lsel = selection.set();
963 if (!selection.set()) {
964 last_sel_cursor = selection.cursor;
965 selection.start = selection.cursor;
966 selection.end = selection.cursor;
971 // first the toggling area
972 if (cursor.y() < last_sel_cursor.y()
973 || (cursor.y() == last_sel_cursor.y()
974 && cursor.x() < last_sel_cursor.x())) {
975 toggle_end_cursor = last_sel_cursor;
976 toggle_cursor = cursor;
978 toggle_end_cursor = cursor;
979 toggle_cursor = last_sel_cursor;
982 last_sel_cursor = cursor;
984 // and now the whole selection
986 if (selection.cursor.par() == cursor.par())
987 if (selection.cursor.pos() < cursor.pos()) {
988 selection.end = cursor;
989 selection.start = selection.cursor;
991 selection.end = selection.cursor;
992 selection.start = cursor;
994 else if (selection.cursor.y() < cursor.y() ||
995 (selection.cursor.y() == cursor.y()
996 && selection.cursor.x() < cursor.x())) {
997 selection.end = cursor;
998 selection.start = selection.cursor;
1001 selection.end = selection.cursor;
1002 selection.start = cursor;
1005 // a selection with no contents is not a selection
1006 if (selection.start.par() == selection.end.par() &&
1007 selection.start.pos() == selection.end.pos())
1008 selection.set(false);
1010 if (inset_owner && (selection.set() || lsel))
1011 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1015 string const LyXText::selectionAsString(Buffer const * buffer,
1018 if (!selection.set()) return string();
1021 // Special handling if the whole selection is within one paragraph
1022 if (selection.start.par() == selection.end.par()) {
1023 result += selection.start.par()->asString(buffer,
1024 selection.start.pos(),
1025 selection.end.pos(),
1030 // The selection spans more than one paragraph
1032 // First paragraph in selection
1033 result += selection.start.par()->asString(buffer,
1034 selection.start.pos(),
1035 selection.start.par()->size(),
1039 // The paragraphs in between (if any)
1040 LyXCursor tmpcur(selection.start);
1041 tmpcur.par(tmpcur.par()->next());
1042 while (tmpcur.par() != selection.end.par()) {
1043 result += tmpcur.par()->asString(buffer, 0,
1044 tmpcur.par()->size(),
1046 tmpcur.par(tmpcur.par()->next());
1049 // Last paragraph in selection
1050 result += selection.end.par()->asString(buffer, 0,
1051 selection.end.pos(), label);
1057 void LyXText::clearSelection() const
1059 selection.set(false);
1060 selection.mark(false);
1061 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1065 void LyXText::cursorHome(BufferView * bview) const
1067 setCursor(bview, cursor.par(), cursor.row()->pos());
1071 void LyXText::cursorEnd(BufferView * bview) const
1073 if (!cursor.row()->next()
1074 || cursor.row()->next()->par() != cursor.row()->par()) {
1075 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1077 if (cursor.par()->size() &&
1078 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1079 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1080 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1082 setCursor(bview,cursor.par(),
1083 rowLast(cursor.row()) + 1);
1089 void LyXText::cursorTop(BufferView * bview) const
1091 while (cursor.par()->previous())
1092 cursor.par(cursor.par()->previous());
1093 setCursor(bview, cursor.par(), 0);
1097 void LyXText::cursorBottom(BufferView * bview) const
1099 while (cursor.par()->next())
1100 cursor.par(cursor.par()->next());
1101 setCursor(bview, cursor.par(), cursor.par()->size());
1105 void LyXText::toggleFree(BufferView * bview,
1106 LyXFont const & font, bool toggleall)
1108 // If the mask is completely neutral, tell user
1109 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1110 // Could only happen with user style
1111 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1115 // Try implicit word selection
1116 // If there is a change in the language the implicit word selection
1118 LyXCursor resetCursor = cursor;
1119 bool implicitSelection = (font.language() == ignore_language
1120 && font.number() == LyXFont::IGNORE)
1121 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1124 setFont(bview, font, toggleall);
1126 // Implicit selections are cleared afterwards
1127 //and cursor is set to the original position.
1128 if (implicitSelection) {
1130 cursor = resetCursor;
1131 setCursor(bview, cursor.par(), cursor.pos());
1132 selection.cursor = cursor;
1135 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1139 string LyXText::getStringToIndex(BufferView * bview)
1143 // Try implicit word selection
1144 // If there is a change in the language the implicit word selection
1146 LyXCursor resetCursor = cursor;
1147 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1149 if (!selection.set()) {
1150 bview->owner()->message(_("Nothing to index!"));
1153 if (selection.start.par() != selection.end.par()) {
1154 bview->owner()->message(_("Cannot index more than one paragraph!"));
1158 idxstring = selectionAsString(bview->buffer(), false);
1160 // Implicit selections are cleared afterwards
1161 //and cursor is set to the original position.
1162 if (implicitSelection) {
1164 cursor = resetCursor;
1165 setCursor(bview, cursor.par(), cursor.pos());
1166 selection.cursor = cursor;
1172 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1173 Paragraph const * par) const
1175 if (textclasslist.Style(buf->params.textclass,
1176 par->getLayout()).labeltype != LABEL_MANUAL)
1179 return par->beginningOfMainBody();
1183 /* the DTP switches for paragraphs. LyX will store them in the
1184 * first physicla paragraph. When a paragraph is broken, the top settings
1185 * rest, the bottom settings are given to the new one. So I can make shure,
1186 * they do not duplicate themself and you cannnot make dirty things with
1189 void LyXText::setParagraph(BufferView * bview,
1190 bool line_top, bool line_bottom,
1191 bool pagebreak_top, bool pagebreak_bottom,
1192 VSpace const & space_top,
1193 VSpace const & space_bottom,
1194 Spacing const & spacing,
1196 string labelwidthstring,
1199 LyXCursor tmpcursor = cursor;
1200 if (!selection.set()) {
1201 selection.start = cursor;
1202 selection.end = cursor;
1205 // make sure that the depth behind the selection are restored, too
1206 Paragraph * endpar = selection.end.par()->next();
1207 Paragraph * undoendpar = endpar;
1209 if (endpar && endpar->getDepth()) {
1210 while (endpar && endpar->getDepth()) {
1211 endpar = endpar->next();
1212 undoendpar = endpar;
1216 // because of parindents etc.
1217 endpar = endpar->next();
1220 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1223 Paragraph * tmppar = selection.end.par();
1224 while (tmppar != selection.start.par()->previous()) {
1225 setCursor(bview, tmppar, 0);
1226 status(bview, LyXText::NEED_MORE_REFRESH);
1227 refresh_row = cursor.row();
1228 refresh_y = cursor.y() - cursor.row()->baseline();
1229 cursor.par()->params().lineTop(line_top);
1230 cursor.par()->params().lineBottom(line_bottom);
1231 cursor.par()->params().pagebreakTop(pagebreak_top);
1232 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1233 cursor.par()->params().spaceTop(space_top);
1234 cursor.par()->params().spaceBottom(space_bottom);
1235 cursor.par()->params().spacing(spacing);
1236 // does the layout allow the new alignment?
1237 if (align == LYX_ALIGN_LAYOUT)
1238 align = textclasslist
1239 .Style(bview->buffer()->params.textclass,
1240 cursor.par()->getLayout()).align;
1241 if (align & textclasslist
1242 .Style(bview->buffer()->params.textclass,
1243 cursor.par()->getLayout()).alignpossible) {
1244 if (align == textclasslist
1245 .Style(bview->buffer()->params.textclass,
1246 cursor.par()->getLayout()).align)
1247 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1249 cursor.par()->params().align(align);
1251 cursor.par()->setLabelWidthString(labelwidthstring);
1252 cursor.par()->params().noindent(noindent);
1253 tmppar = cursor.par()->previous();
1256 redoParagraphs(bview, selection.start, endpar);
1259 setCursor(bview, selection.start.par(), selection.start.pos());
1260 selection.cursor = cursor;
1261 setCursor(bview, selection.end.par(), selection.end.pos());
1262 setSelection(bview);
1263 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1265 bview->updateInset(inset_owner, true);
1269 char loweralphaCounter(int n)
1271 if (n < 1 || n > 26)
1281 char alphaCounter(int n)
1283 if (n < 1 || n > 26)
1291 char hebrewCounter(int n)
1293 static const char hebrew[22] = {
1294 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1295 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1296 '÷', 'ø', 'ù', 'ú'
1298 if (n < 1 || n > 22)
1306 string const romanCounter(int n)
1308 static char const * roman[20] = {
1309 "i", "ii", "iii", "iv", "v",
1310 "vi", "vii", "viii", "ix", "x",
1311 "xi", "xii", "xiii", "xiv", "xv",
1312 "xvi", "xvii", "xviii", "xix", "xx"
1314 if (n < 1 || n > 20)
1323 // set the counter of a paragraph. This includes the labels
1324 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1326 LyXLayout const & layout =
1327 textclasslist.Style(buf->params.textclass,
1330 LyXTextClass const & textclass =
1331 textclasslist.TextClass(buf->params.textclass);
1333 // copy the prev-counters to this one,
1334 // unless this is the first paragraph
1335 if (par->previous()) {
1336 for (int i = 0; i < 10; ++i) {
1337 par->setCounter(i, par->previous()->getFirstCounter(i));
1339 par->params().appendix(par->previous()->params().appendix());
1340 if (!par->params().appendix() && par->params().startOfAppendix()) {
1341 par->params().appendix(true);
1342 for (int i = 0; i < 10; ++i) {
1343 par->setCounter(i, 0);
1346 par->enumdepth = par->previous()->enumdepth;
1347 par->itemdepth = par->previous()->itemdepth;
1349 for (int i = 0; i < 10; ++i) {
1350 par->setCounter(i, 0);
1352 par->params().appendix(par->params().startOfAppendix());
1357 /* Maybe we have to increment the enumeration depth.
1358 * BUT, enumeration in a footnote is considered in isolation from its
1359 * surrounding paragraph so don't increment if this is the
1360 * first line of the footnote
1361 * AND, bibliographies can't have their depth changed ie. they
1362 * are always of depth 0
1365 && par->previous()->getDepth() < par->getDepth()
1366 && textclasslist.Style(buf->params.textclass,
1367 par->previous()->getLayout()
1368 ).labeltype == LABEL_COUNTER_ENUMI
1369 && par->enumdepth < 3
1370 && layout.labeltype != LABEL_BIBLIO) {
1374 // Maybe we have to decrement the enumeration depth, see note above
1376 && par->previous()->getDepth() > par->getDepth()
1377 && layout.labeltype != LABEL_BIBLIO) {
1378 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1379 par->setCounter(6 + par->enumdepth,
1380 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1381 /* reset the counters.
1382 * A depth change is like a breaking layout
1384 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1385 par->setCounter(i, 0);
1388 if (!par->params().labelString().empty()) {
1389 par->params().labelString(string());
1392 if (layout.margintype == MARGIN_MANUAL) {
1393 if (par->params().labelWidthString().empty()) {
1394 par->setLabelWidthString(layout.labelstring());
1397 par->setLabelWidthString(string());
1400 // is it a layout that has an automatic label?
1401 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1403 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1404 if (i >= 0 && i<= buf->params.secnumdepth) {
1405 par->incCounter(i); // increment the counter
1407 // Is there a label? Useful for Chapter layout
1408 if (!par->params().appendix()) {
1409 if (!layout.labelstring().empty())
1410 par->params().labelString(layout.labelstring());
1412 par->params().labelString(string());
1414 if (!layout.labelstring_appendix().empty())
1415 par->params().labelString(layout.labelstring_appendix());
1417 par->params().labelString(string());
1422 if (!par->params().appendix()) {
1423 switch (2 * LABEL_COUNTER_CHAPTER -
1424 textclass.maxcounter() + i) {
1425 case LABEL_COUNTER_CHAPTER:
1426 s << par->getCounter(i);
1428 case LABEL_COUNTER_SECTION:
1429 s << par->getCounter(i - 1) << '.'
1430 << par->getCounter(i);
1432 case LABEL_COUNTER_SUBSECTION:
1433 s << par->getCounter(i - 2) << '.'
1434 << par->getCounter(i - 1) << '.'
1435 << par->getCounter(i);
1437 case LABEL_COUNTER_SUBSUBSECTION:
1438 s << par->getCounter(i - 3) << '.'
1439 << par->getCounter(i - 2) << '.'
1440 << par->getCounter(i - 1) << '.'
1441 << par->getCounter(i);
1444 case LABEL_COUNTER_PARAGRAPH:
1445 s << par->getCounter(i - 4) << '.'
1446 << par->getCounter(i - 3) << '.'
1447 << par->getCounter(i - 2) << '.'
1448 << par->getCounter(i - 1) << '.'
1449 << par->getCounter(i);
1451 case LABEL_COUNTER_SUBPARAGRAPH:
1452 s << par->getCounter(i - 5) << '.'
1453 << par->getCounter(i - 4) << '.'
1454 << par->getCounter(i - 3) << '.'
1455 << par->getCounter(i - 2) << '.'
1456 << par->getCounter(i - 1) << '.'
1457 << par->getCounter(i);
1461 // Can this ever be reached? And in the
1462 // case it is, how can this be correct?
1464 s << par->getCounter(i) << '.';
1467 } else { // appendix
1468 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1469 case LABEL_COUNTER_CHAPTER:
1470 if (par->isRightToLeftPar(buf->params))
1471 s << hebrewCounter(par->getCounter(i));
1473 s << alphaCounter(par->getCounter(i));
1475 case LABEL_COUNTER_SECTION:
1476 if (par->isRightToLeftPar(buf->params))
1477 s << hebrewCounter(par->getCounter(i - 1));
1479 s << alphaCounter(par->getCounter(i - 1));
1482 << par->getCounter(i);
1485 case LABEL_COUNTER_SUBSECTION:
1486 if (par->isRightToLeftPar(buf->params))
1487 s << hebrewCounter(par->getCounter(i - 2));
1489 s << alphaCounter(par->getCounter(i - 2));
1492 << par->getCounter(i-1) << '.'
1493 << par->getCounter(i);
1496 case LABEL_COUNTER_SUBSUBSECTION:
1497 if (par->isRightToLeftPar(buf->params))
1498 s << hebrewCounter(par->getCounter(i-3));
1500 s << alphaCounter(par->getCounter(i-3));
1503 << par->getCounter(i-2) << '.'
1504 << par->getCounter(i-1) << '.'
1505 << par->getCounter(i);
1508 case LABEL_COUNTER_PARAGRAPH:
1509 if (par->isRightToLeftPar(buf->params))
1510 s << hebrewCounter(par->getCounter(i-4));
1512 s << alphaCounter(par->getCounter(i-4));
1515 << par->getCounter(i-3) << '.'
1516 << par->getCounter(i-2) << '.'
1517 << par->getCounter(i-1) << '.'
1518 << par->getCounter(i);
1521 case LABEL_COUNTER_SUBPARAGRAPH:
1522 if (par->isRightToLeftPar(buf->params))
1523 s << hebrewCounter(par->getCounter(i-5));
1525 s << alphaCounter(par->getCounter(i-5));
1528 << par->getCounter(i-4) << '.'
1529 << par->getCounter(i-3) << '.'
1530 << par->getCounter(i-2) << '.'
1531 << par->getCounter(i-1) << '.'
1532 << par->getCounter(i);
1536 // Can this ever be reached? And in the
1537 // case it is, how can this be correct?
1539 s << par->getCounter(i) << '.';
1545 par->params().labelString(par->params().labelString() +s.str().c_str());
1546 // We really want to remove the c_str as soon as
1549 for (i++; i < 10; ++i) {
1550 // reset the following counters
1551 par->setCounter(i, 0);
1553 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1554 for (i++; i < 10; ++i) {
1555 // reset the following counters
1556 par->setCounter(i, 0);
1558 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1559 par->incCounter(i + par->enumdepth);
1560 int number = par->getCounter(i + par->enumdepth);
1564 switch (par->enumdepth) {
1566 if (par->isRightToLeftPar(buf->params))
1568 << hebrewCounter(number)
1572 << loweralphaCounter(number)
1576 if (par->isRightToLeftPar(buf->params))
1577 s << '.' << romanCounter(number);
1579 s << romanCounter(number) << '.';
1582 if (par->isRightToLeftPar(buf->params))
1584 << alphaCounter(number);
1586 s << alphaCounter(number)
1590 if (par->isRightToLeftPar(buf->params))
1597 par->params().labelString(s.str().c_str());
1599 for (i += par->enumdepth + 1; i < 10; ++i) {
1600 // reset the following counters
1601 par->setCounter(i, 0);
1605 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1606 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1608 int number = par->getCounter(i);
1610 InsetCommandParams p( "bibitem" );
1611 par->bibkey = new InsetBibKey(p);
1613 par->bibkey->setCounter(number);
1614 par->params().labelString(layout.labelstring());
1616 // In biblio should't be following counters but...
1618 string s = layout.labelstring();
1620 // the caption hack:
1621 if (layout.labeltype == LABEL_SENSITIVE) {
1622 bool isOK (par->inInset() && par->inInset()->owner() &&
1623 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1626 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1628 = floatList.getType(tmp->type());
1629 // We should get the correct number here too.
1630 s = fl.name() + " #:";
1632 /* par->SetLayout(0);
1633 s = layout->labelstring; */
1634 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1635 ? " :úåòîùî øñç" : "Senseless: ";
1638 par->params().labelString(s);
1640 /* reset the enumeration counter. They are always resetted
1641 * when there is any other layout between */
1642 for (int i = 6 + par->enumdepth; i < 10; ++i)
1643 par->setCounter(i, 0);
1648 // Updates all counters BEHIND the row. Changed paragraphs
1649 // with a dynamic left margin will be rebroken.
1650 void LyXText::updateCounters(BufferView * bview, Row * row) const
1658 par = row->par()->next();
1662 while (row->par() != par)
1665 setCounter(bview->buffer(), par);
1667 // now check for the headline layouts. remember that they
1668 // have a dynamic left margin
1669 if ((textclasslist.Style(bview->buffer()->params.textclass,
1670 par->layout).margintype == MARGIN_DYNAMIC
1671 || textclasslist.Style(bview->buffer()->params.textclass,
1672 par->layout).labeltype == LABEL_SENSITIVE)) {
1674 // Rebreak the paragraph
1675 removeParagraph(row);
1676 appendParagraph(bview, row);
1683 void LyXText::insertInset(BufferView * bview, Inset * inset)
1685 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1687 // I don't know if this is necessary here (Jug 20020102)
1688 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1689 cursor.par()->insertInset(cursor.pos(), inset);
1690 // Just to rebreak and refresh correctly.
1691 // The character will not be inserted a second time
1692 insertChar(bview, Paragraph::META_INSET);
1694 // If we enter a highly editable inset the cursor should be to before
1695 // the inset. This couldn't happen before as Undo was not handled inside
1696 // inset now after the Undo LyX tries to call inset->Edit(...) again
1697 // and cannot do this as the cursor is behind the inset and GetInset
1698 // does not return the inset!
1699 if (isHighlyEditableInset(inset)) {
1700 cursorLeft(bview, true);
1706 void LyXText::copyEnvironmentType()
1708 copylayouttype = cursor.par()->getLayout();
1712 void LyXText::pasteEnvironmentType(BufferView * bview)
1714 setLayout(bview, copylayouttype);
1718 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1720 // Stuff what we got on the clipboard. Even if there is no selection.
1722 // There is a problem with having the stuffing here in that the
1723 // larger the selection the slower LyX will get. This can be
1724 // solved by running the line below only when the selection has
1725 // finished. The solution used currently just works, to make it
1726 // faster we need to be more clever and probably also have more
1727 // calls to stuffClipboard. (Lgb)
1728 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1730 // This doesn't make sense, if there is no selection
1731 if (!selection.set())
1734 // OK, we have a selection. This is always between selection.start
1735 // and selection.end
1737 // make sure that the depth behind the selection are restored, too
1738 Paragraph * endpar = selection.end.par()->next();
1739 Paragraph * undoendpar = endpar;
1741 if (endpar && endpar->getDepth()) {
1742 while (endpar && endpar->getDepth()) {
1743 endpar = endpar->next();
1744 undoendpar = endpar;
1746 } else if (endpar) {
1747 endpar = endpar->next(); // because of parindents etc.
1750 setUndo(bview, Undo::DELETE,
1751 selection.start.par(), undoendpar);
1753 // there are two cases: cut only within one paragraph or
1754 // more than one paragraph
1755 if (selection.start.par() == selection.end.par()) {
1756 // only within one paragraph
1757 endpar = selection.end.par();
1758 int pos = selection.end.pos();
1759 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1760 selection.start.pos(), pos,
1761 bview->buffer()->params.textclass,
1763 selection.end.pos(pos);
1765 endpar = selection.end.par();
1766 int pos = selection.end.pos();
1767 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1768 selection.start.pos(), pos,
1769 bview->buffer()->params.textclass,
1772 selection.end.par(endpar);
1773 selection.end.pos(pos);
1774 cursor.pos(selection.end.pos());
1776 endpar = endpar->next();
1778 // sometimes necessary
1780 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1782 redoParagraphs(bview, selection.start, endpar);
1784 // cutSelection can invalidate the cursor so we need to set
1786 cursor = selection.start;
1788 // need a valid cursor. (Lgb)
1791 setCursor(bview, cursor.par(), cursor.pos());
1792 selection.cursor = cursor;
1793 updateCounters(bview, cursor.row());
1797 void LyXText::copySelection(BufferView * bview)
1799 // Stuff what we got on the clipboard. Even if there is no selection.
1801 // There is a problem with having the stuffing here in that the
1802 // larger the selection the slower LyX will get. This can be
1803 // solved by running the line below only when the selection has
1804 // finished. The solution used currently just works, to make it
1805 // faster we need to be more clever and probably also have more
1806 // calls to stuffClipboard. (Lgb)
1807 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1809 // this doesnt make sense, if there is no selection
1810 if (!selection.set())
1813 // ok we have a selection. This is always between selection.start
1814 // and sel_end cursor
1816 // copy behind a space if there is one
1817 while (selection.start.par()->size() > selection.start.pos()
1818 && selection.start.par()->isLineSeparator(selection.start.pos())
1819 && (selection.start.par() != selection.end.par()
1820 || selection.start.pos() < selection.end.pos()))
1821 selection.start.pos(selection.start.pos() + 1);
1823 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1824 selection.start.pos(), selection.end.pos(),
1825 bview->buffer()->params.textclass);
1829 void LyXText::pasteSelection(BufferView * bview)
1831 // this does not make sense, if there is nothing to paste
1832 if (!CutAndPaste::checkPastePossible(cursor.par()))
1835 setUndo(bview, Undo::INSERT,
1836 cursor.par(), cursor.par()->next());
1839 Paragraph * actpar = cursor.par();
1840 int pos = cursor.pos();
1842 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1843 bview->buffer()->params.textclass);
1845 redoParagraphs(bview, cursor, endpar);
1847 setCursor(bview, cursor.par(), cursor.pos());
1850 setCursor(bview, actpar, pos);
1851 updateCounters(bview, cursor.row());
1855 // returns a pointer to the very first Paragraph
1856 Paragraph * LyXText::firstParagraph() const
1858 return ownerParagraph();
1862 // sets the selection over the number of characters of string, no check!!
1863 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1868 selection.cursor = cursor;
1869 for (string::size_type i = 0; i < str.length(); ++i)
1871 setSelection(bview);
1875 // simple replacing. The font of the first selected character is used
1876 void LyXText::replaceSelectionWithString(BufferView * bview,
1879 setCursorParUndo(bview);
1882 if (!selection.set()) { // create a dummy selection
1883 selection.end = cursor;
1884 selection.start = cursor;
1887 // Get font setting before we cut
1888 pos_type pos = selection.end.pos();
1889 LyXFont const font = selection.start.par()
1890 ->getFontSettings(bview->buffer()->params,
1891 selection.start.pos());
1893 // Insert the new string
1894 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1895 selection.end.par()->insertChar(pos, (*cit), font);
1899 // Cut the selection
1900 cutSelection(bview, true, false);
1906 // needed to insert the selection
1907 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1909 Paragraph * par = cursor.par();
1910 pos_type pos = cursor.pos();
1911 Paragraph * endpar = cursor.par()->next();
1913 setCursorParUndo(bview);
1915 // only to be sure, should not be neccessary
1918 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1920 redoParagraphs(bview, cursor, endpar);
1921 setCursor(bview, cursor.par(), cursor.pos());
1922 selection.cursor = cursor;
1923 setCursor(bview, par, pos);
1924 setSelection(bview);
1928 // turns double-CR to single CR, others where converted into one
1929 // blank. Then InsertStringAsLines is called
1930 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1932 string linestr(str);
1933 bool newline_inserted = false;
1934 for (string::size_type i = 0; i < linestr.length(); ++i) {
1935 if (linestr[i] == '\n') {
1936 if (newline_inserted) {
1937 // we know that \r will be ignored by
1938 // InsertStringA. Of course, it is a dirty
1939 // trick, but it works...
1940 linestr[i - 1] = '\r';
1944 newline_inserted = true;
1946 } else if (IsPrintable(linestr[i])) {
1947 newline_inserted = false;
1950 insertStringAsLines(bview, linestr);
1954 bool LyXText::gotoNextInset(BufferView * bview,
1955 std::vector<Inset::Code> const & codes,
1956 string const & contents) const
1958 LyXCursor res = cursor;
1961 if (res.pos() < res.par()->size() - 1) {
1962 res.pos(res.pos() + 1);
1964 res.par(res.par()->next());
1968 } while (res.par() &&
1969 !(res.par()->isInset(res.pos())
1970 && (inset = res.par()->getInset(res.pos())) != 0
1971 && find(codes.begin(), codes.end(), inset->lyxCode())
1973 && (contents.empty() ||
1974 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1978 setCursor(bview, res.par(), res.pos(), false);
1985 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1988 LyXCursor tmpcursor;
1992 Row * row = getRow(par, pos, y);
1994 // is there a break one row above
1995 if (row->previous() && row->previous()->par() == row->par()) {
1996 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1997 if (z >= row->pos()) {
1998 // set the dimensions of the row above
1999 y -= row->previous()->height();
2001 refresh_row = row->previous();
2002 status(bview, LyXText::NEED_MORE_REFRESH);
2004 breakAgain(bview, row->previous());
2006 // set the cursor again. Otherwise
2007 // dangling pointers are possible
2008 setCursor(bview, cursor.par(), cursor.pos(),
2009 false, cursor.boundary());
2010 selection.cursor = cursor;
2015 int const tmpheight = row->height();
2016 pos_type const tmplast = rowLast(row);
2020 breakAgain(bview, row);
2021 if (row->height() == tmpheight && rowLast(row) == tmplast)
2022 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2024 status(bview, LyXText::NEED_MORE_REFRESH);
2026 // check the special right address boxes
2027 if (textclasslist.Style(bview->buffer()->params.textclass,
2028 par->getLayout()).margintype
2029 == MARGIN_RIGHT_ADDRESS_BOX)
2037 redoDrawingOfParagraph(bview, tmpcursor);
2040 // set the cursor again. Otherwise dangling pointers are possible
2041 // also set the selection
2043 if (selection.set()) {
2045 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2046 false, selection.cursor.boundary());
2047 selection.cursor = cursor;
2048 setCursorIntern(bview, selection.start.par(),
2049 selection.start.pos(),
2050 false, selection.start.boundary());
2051 selection.start = cursor;
2052 setCursorIntern(bview, selection.end.par(),
2053 selection.end.pos(),
2054 false, selection.end.boundary());
2055 selection.end = cursor;
2056 setCursorIntern(bview, last_sel_cursor.par(),
2057 last_sel_cursor.pos(),
2058 false, last_sel_cursor.boundary());
2059 last_sel_cursor = cursor;
2062 setCursorIntern(bview, cursor.par(), cursor.pos(),
2063 false, cursor.boundary());
2067 // returns false if inset wasn't found
2068 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2070 // first check the current paragraph
2071 int pos = cursor.par()->getPositionOfInset(inset);
2073 checkParagraph(bview, cursor.par(), pos);
2077 // check every paragraph
2079 Paragraph * par = firstParagraph();
2081 pos = par->getPositionOfInset(inset);
2083 checkParagraph(bview, par, pos);
2093 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2095 bool setfont, bool boundary) const
2097 LyXCursor old_cursor = cursor;
2098 setCursorIntern(bview, par, pos, setfont, boundary);
2099 return deleteEmptyParagraphMechanism(bview, old_cursor);
2103 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2104 pos_type pos, bool boundary) const
2111 cur.boundary(boundary);
2113 // get the cursor y position in text
2115 Row * row = getRow(par, pos, y);
2116 // y is now the beginning of the cursor row
2117 y += row->baseline();
2118 // y is now the cursor baseline
2121 // now get the cursors x position
2123 float fill_separator;
2125 float fill_label_hfill;
2126 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2128 pos_type cursor_vpos = 0;
2129 pos_type last = rowLastPrintable(row);
2131 if (pos > last + 1) {
2132 // This shouldn't happen.
2135 } else if (pos < row->pos()) {
2140 if (last < row->pos())
2141 cursor_vpos = row->pos();
2142 else if (pos > last && !boundary)
2143 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2144 ? row->pos() : last + 1;
2145 else if (pos > row->pos() &&
2146 (pos > last || boundary))
2147 /// Place cursor after char at (logical) position pos - 1
2148 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2149 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2151 /// Place cursor before char at (logical) position pos
2152 cursor_vpos = (bidi_level(pos) % 2 == 0)
2153 ? log2vis(pos) : log2vis(pos) + 1;
2155 pos_type main_body =
2156 beginningOfMainBody(bview->buffer(), row->par());
2157 if ((main_body > 0) &&
2158 ((main_body-1 > last) ||
2159 !row->par()->isLineSeparator(main_body-1)))
2162 for (pos_type vpos = row->pos();
2163 vpos < cursor_vpos; ++vpos) {
2164 pos = vis2log(vpos);
2165 if (main_body > 0 && pos == main_body - 1) {
2166 x += fill_label_hfill +
2167 lyxfont::width(textclasslist.Style(
2168 bview->buffer()->params.textclass,
2169 row->par()->getLayout())
2171 getLabelFont(bview->buffer(), row->par()));
2172 if (row->par()->isLineSeparator(main_body-1))
2173 x -= singleWidth(bview, row->par(),main_body-1);
2175 if (hfillExpansion(bview->buffer(), row, pos)) {
2176 x += singleWidth(bview, row->par(), pos);
2177 if (pos >= main_body)
2180 x += fill_label_hfill;
2181 } else if (row->par()->isSeparator(pos)) {
2182 x += singleWidth(bview, row->par(), pos);
2183 if (pos >= main_body)
2184 x += fill_separator;
2186 x += singleWidth(bview, row->par(), pos);
2195 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2196 pos_type pos, bool setfont, bool boundary) const
2198 InsetText * it = static_cast<InsetText *>(par->inInset());
2200 if (it != inset_owner) {
2201 lyxerr << "InsetText is " << it << endl;
2202 lyxerr << "inset_owner is " << inset_owner << endl;
2203 #warning I believe this code is wrong. (Lgb)
2204 #warning Jürgen, have a look at this. (Lgb)
2205 #warning Hmmm, I guess you are right but we
2206 #warning should verify when this is needed
2207 // Jürgen, would you like to have a look?
2208 // I guess we need to move the outer cursor
2209 // and open and lock the inset (bla bla bla)
2210 // stuff I don't know... so can you have a look?
2212 // I moved the lyxerr stuff in here so we can see if
2213 // this is actually really needed and where!
2215 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2220 setCursor(bview, cursor, par, pos, boundary);
2222 setCurrentFont(bview);
2226 void LyXText::setCurrentFont(BufferView * bview) const
2228 pos_type pos = cursor.pos();
2229 if (cursor.boundary() && pos > 0)
2233 if (pos == cursor.par()->size())
2235 else // potentional bug... BUG (Lgb)
2236 if (cursor.par()->isSeparator(pos)) {
2237 if (pos > cursor.row()->pos() &&
2238 bidi_level(pos) % 2 ==
2239 bidi_level(pos - 1) % 2)
2241 else if (pos + 1 < cursor.par()->size())
2247 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2248 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2250 if (cursor.pos() == cursor.par()->size() &&
2251 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2252 !cursor.boundary()) {
2253 Language const * lang =
2254 cursor.par()->getParLanguage(bview->buffer()->params);
2255 current_font.setLanguage(lang);
2256 current_font.setNumber(LyXFont::OFF);
2257 real_current_font.setLanguage(lang);
2258 real_current_font.setNumber(LyXFont::OFF);
2263 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2265 LyXCursor old_cursor = cursor;
2267 setCursorFromCoordinates(bview, cursor, x, y);
2268 setCurrentFont(bview);
2269 deleteEmptyParagraphMechanism(bview, old_cursor);
2273 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2276 // Get the row first.
2278 Row * row = getRowNearY(y);
2280 pos_type const column = getColumnNearX(bview, row, x, bound);
2281 cur.par(row->par());
2282 cur.pos(row->pos() + column);
2284 cur.y(y + row->baseline());
2286 cur.boundary(bound);
2290 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2292 if (cursor.pos() > 0) {
2293 bool boundary = cursor.boundary();
2294 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2295 if (!internal && !boundary &&
2296 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2297 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2298 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2299 Paragraph * par = cursor.par()->previous();
2300 setCursor(bview, par, par->size());
2305 void LyXText::cursorRight(BufferView * bview, bool internal) const
2307 if (!internal && cursor.boundary() &&
2308 !cursor.par()->isNewline(cursor.pos()))
2309 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2310 else if (cursor.pos() < cursor.par()->size()) {
2311 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2313 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2314 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2315 } else if (cursor.par()->next())
2316 setCursor(bview, cursor.par()->next(), 0);
2320 void LyXText::cursorUp(BufferView * bview) const
2322 setCursorFromCoordinates(bview, cursor.x_fix(),
2323 cursor.y() - cursor.row()->baseline() - 1);
2327 void LyXText::cursorDown(BufferView * bview) const
2329 setCursorFromCoordinates(bview, cursor.x_fix(),
2330 cursor.y() - cursor.row()->baseline()
2331 + cursor.row()->height() + 1);
2335 void LyXText::cursorUpParagraph(BufferView * bview) const
2337 if (cursor.pos() > 0) {
2338 setCursor(bview, cursor.par(), 0);
2340 else if (cursor.par()->previous()) {
2341 setCursor(bview, cursor.par()->previous(), 0);
2346 void LyXText::cursorDownParagraph(BufferView * bview) const
2348 if (cursor.par()->next()) {
2349 setCursor(bview, cursor.par()->next(), 0);
2351 setCursor(bview, cursor.par(), cursor.par()->size());
2355 // fix the cursor `cur' after a characters has been deleted at `where'
2356 // position. Called by deleteEmptyParagraphMechanism
2357 void LyXText::fixCursorAfterDelete(BufferView * bview,
2359 LyXCursor const & where) const
2361 // if cursor is not in the paragraph where the delete occured,
2363 if (cur.par() != where.par())
2366 // if cursor position is after the place where the delete occured,
2368 if (cur.pos() > where.pos())
2369 cur.pos(cur.pos()-1);
2371 // recompute row et al. for this cursor
2372 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2376 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2377 LyXCursor const & old_cursor) const
2379 // Would be wrong to delete anything if we have a selection.
2380 if (selection.set()) return false;
2382 // We allow all kinds of "mumbo-jumbo" when freespacing.
2383 if (textclasslist.Style(bview->buffer()->params.textclass,
2384 old_cursor.par()->getLayout()).free_spacing
2385 || old_cursor.par()->isFreeSpacing())
2390 /* Ok I'll put some comments here about what is missing.
2391 I have fixed BackSpace (and thus Delete) to not delete
2392 double-spaces automagically. I have also changed Cut,
2393 Copy and Paste to hopefully do some sensible things.
2394 There are still some small problems that can lead to
2395 double spaces stored in the document file or space at
2396 the beginning of paragraphs. This happens if you have
2397 the cursor betwenn to spaces and then save. Or if you
2398 cut and paste and the selection have a space at the
2399 beginning and then save right after the paste. I am
2400 sure none of these are very hard to fix, but I will
2401 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2402 that I can get some feedback. (Lgb)
2405 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2406 // delete the LineSeparator.
2409 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2410 // delete the LineSeparator.
2413 // If the pos around the old_cursor were spaces, delete one of them.
2414 if (old_cursor.par() != cursor.par()
2415 || old_cursor.pos() != cursor.pos()) {
2416 // Only if the cursor has really moved
2418 if (old_cursor.pos() > 0
2419 && old_cursor.pos() < old_cursor.par()->size()
2420 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2421 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2422 old_cursor.par()->erase(old_cursor.pos() - 1);
2423 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2425 #ifdef WITH_WARNINGS
2426 #warning This will not work anymore when we have multiple views of the same buffer
2427 // In this case, we will have to correct also the cursors held by
2428 // other bufferviews. It will probably be easier to do that in a more
2429 // automated way in LyXCursor code. (JMarc 26/09/2001)
2431 // correct all cursors held by the LyXText
2432 fixCursorAfterDelete(bview, cursor, old_cursor);
2433 fixCursorAfterDelete(bview, selection.cursor,
2435 fixCursorAfterDelete(bview, selection.start,
2437 fixCursorAfterDelete(bview, selection.end, old_cursor);
2438 fixCursorAfterDelete(bview, last_sel_cursor,
2440 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2441 fixCursorAfterDelete(bview, toggle_end_cursor,
2447 // don't delete anything if this is the ONLY paragraph!
2448 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2451 // Do not delete empty paragraphs with keepempty set.
2452 if ((textclasslist.Style(bview->buffer()->params.textclass,
2453 old_cursor.par()->getLayout())).keepempty)
2456 // only do our magic if we changed paragraph
2457 if (old_cursor.par() == cursor.par())
2460 // record if we have deleted a paragraph
2461 // we can't possibly have deleted a paragraph before this point
2462 bool deleted = false;
2464 if ((old_cursor.par()->size() == 0
2465 || (old_cursor.par()->size() == 1
2466 && old_cursor.par()->isLineSeparator(0)))) {
2467 // ok, we will delete anything
2468 LyXCursor tmpcursor;
2470 // make sure that you do not delete any environments
2471 status(bview, LyXText::NEED_MORE_REFRESH);
2474 if (old_cursor.row()->previous()) {
2475 refresh_row = old_cursor.row()->previous();
2476 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2478 cursor = old_cursor; // that undo can restore the right cursor position
2479 Paragraph * endpar = old_cursor.par()->next();
2480 if (endpar && endpar->getDepth()) {
2481 while (endpar && endpar->getDepth()) {
2482 endpar = endpar->next();
2485 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2489 removeRow(old_cursor.row());
2490 if (ownerParagraph() == old_cursor.par()) {
2491 ownerParagraph(ownerParagraph()->next());
2494 delete old_cursor.par();
2496 /* Breakagain the next par. Needed because of
2497 * the parindent that can occur or dissappear.
2498 * The next row can change its height, if
2499 * there is another layout before */
2500 if (refresh_row->next()) {
2501 breakAgain(bview, refresh_row->next());
2502 updateCounters(bview, refresh_row);
2504 setHeightOfRow(bview, refresh_row);
2506 refresh_row = old_cursor.row()->next();
2507 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2510 cursor = old_cursor; // that undo can restore the right cursor position
2511 Paragraph * endpar = old_cursor.par()->next();
2512 if (endpar && endpar->getDepth()) {
2513 while (endpar && endpar->getDepth()) {
2514 endpar = endpar->next();
2517 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2521 removeRow(old_cursor.row());
2523 if (ownerParagraph() == old_cursor.par()) {
2524 ownerParagraph(ownerParagraph()->next());
2527 delete old_cursor.par();
2529 /* Breakagain the next par. Needed because of
2530 the parindent that can occur or dissappear.
2531 The next row can change its height, if
2532 there is another layout before */
2534 breakAgain(bview, refresh_row);
2535 updateCounters(bview, refresh_row->previous());
2540 setCursorIntern(bview, cursor.par(), cursor.pos());
2542 if (selection.cursor.par() == old_cursor.par()
2543 && selection.cursor.pos() == old_cursor.pos()) {
2544 // correct selection
2545 selection.cursor = cursor;
2549 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2550 redoParagraphs(bview, old_cursor,
2551 old_cursor.par()->next());
2553 setCursorIntern(bview, cursor.par(), cursor.pos());
2554 selection.cursor = cursor;
2561 void LyXText::toggleAppendix(BufferView * bview)
2563 Paragraph * par = cursor.par();
2564 bool start = !par->params().startOfAppendix();
2566 // ensure that we have only one start_of_appendix in this document
2567 Paragraph * tmp = firstParagraph();
2568 for (; tmp; tmp = tmp->next()) {
2569 tmp->params().startOfAppendix(false);
2572 par->params().startOfAppendix(start);
2574 // we can set the refreshing parameters now
2575 status(bview, LyXText::NEED_MORE_REFRESH);
2577 refresh_row = 0; // not needed for full update
2578 updateCounters(bview, 0);
2579 setCursor(bview, cursor.par(), cursor.pos());
2583 Paragraph * LyXText::ownerParagraph() const
2586 return inset_owner->paragraph();
2588 return bv_owner->buffer()->paragraph;
2592 void LyXText::ownerParagraph(Paragraph * p) const
2595 inset_owner->paragraph(p);
2597 bv_owner->buffer()->paragraph = p;
2602 void LyXText::ownerParagraph(int id, Paragraph * p) const
2604 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2605 if (op && op->inInset()) {
2606 static_cast<InsetText *>(op->inInset())->paragraph(p);
2613 LyXText::text_status LyXText::status() const
2619 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2621 // well as much as I know && binds more then || so the above and the
2622 // below are identical (this for your known use of parentesis!)
2623 // Now some explanation:
2624 // We should only go up with refreshing code so this means that if
2625 // we have a MORE refresh we should never set it to LITTLE if we still
2626 // didn't handle it (and then it will be UNCHANGED. Now as long as
2627 // we stay inside one LyXText this may work but we need to tell the
2628 // outermost LyXText that it should REALLY draw us if there is some
2629 // change in a Inset::LyXText. So you see that when we are inside a
2630 // inset's LyXText we give the LITTLE to the outermost LyXText to
2631 // tell'em that it should redraw the actual row (where the inset
2632 // resides! Capito?!
2634 if ((status_ != NEED_MORE_REFRESH)
2635 || (status_ == NEED_MORE_REFRESH
2636 && st != NEED_VERY_LITTLE_REFRESH))
2639 if (inset_owner && st != UNCHANGED) {
2640 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2641 if (!bview->text->refresh_row) {
2642 bview->text->refresh_row = bview->text->cursor.row();
2643 bview->text->refresh_y = bview->text->cursor.y() -
2644 bview->text->cursor.row()->baseline();