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"
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
794 setCursor(bview, selection.start.par(), selection.start.pos());
795 selection.cursor = cursor;
796 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 // Can somebody please tell me _why_ this solves
867 first_phys_par = firstParagraph();
869 first_phys_par = tmprow->par();
870 while (tmprow->previous()
871 && tmprow->previous()->par() == first_phys_par)
873 tmprow = tmprow->previous();
874 y -= tmprow->height();
878 // we can set the refreshing parameters now
879 status(bview, LyXText::NEED_MORE_REFRESH);
881 refresh_row = tmprow->previous(); /* the real refresh row will
882 be deleted, so I store
886 tmppar = tmprow->next()->par();
889 while (tmppar != endpar) {
890 removeRow(tmprow->next());
892 tmppar = tmprow->next()->par();
897 // remove the first one
898 tmprow2 = tmprow; /* this is because tmprow->previous()
900 tmprow = tmprow->previous();
903 tmppar = first_phys_par;
907 insertParagraph(bview, tmppar, tmprow);
911 while (tmprow->next()
912 && tmprow->next()->par() == tmppar) {
913 tmprow = tmprow->next();
915 tmppar = tmppar->next();
917 } while (tmppar && tmppar != endpar);
919 // this is because of layout changes
921 refresh_y -= refresh_row->height();
922 setHeightOfRow(bview, refresh_row);
924 refresh_row = firstrow;
926 setHeightOfRow(bview, refresh_row);
929 if (tmprow && tmprow->next())
930 setHeightOfRow(bview, tmprow->next());
934 bool LyXText::fullRebreak(BufferView * bview)
940 if (need_break_row) {
941 breakAgain(bview, need_break_row);
949 // important for the screen
952 /* the cursor set functions have a special mechanism. When they
953 * realize, that you left an empty paragraph, they will delete it.
954 * They also delete the corresponding row */
956 // need the selection cursor:
957 void LyXText::setSelection(BufferView * bview)
959 bool const lsel = selection.set();
961 if (!selection.set()) {
962 last_sel_cursor = selection.cursor;
963 selection.start = selection.cursor;
964 selection.end = selection.cursor;
969 // first the toggling area
970 if (cursor.y() < last_sel_cursor.y()
971 || (cursor.y() == last_sel_cursor.y()
972 && cursor.x() < last_sel_cursor.x())) {
973 toggle_end_cursor = last_sel_cursor;
974 toggle_cursor = cursor;
976 toggle_end_cursor = cursor;
977 toggle_cursor = last_sel_cursor;
980 last_sel_cursor = cursor;
982 // and now the whole selection
984 if (selection.cursor.par() == cursor.par())
985 if (selection.cursor.pos() < cursor.pos()) {
986 selection.end = cursor;
987 selection.start = selection.cursor;
989 selection.end = selection.cursor;
990 selection.start = cursor;
992 else if (selection.cursor.y() < cursor.y() ||
993 (selection.cursor.y() == cursor.y()
994 && selection.cursor.x() < cursor.x())) {
995 selection.end = cursor;
996 selection.start = selection.cursor;
999 selection.end = selection.cursor;
1000 selection.start = cursor;
1003 // a selection with no contents is not a selection
1004 if (selection.start.par() == selection.end.par() &&
1005 selection.start.pos() == selection.end.pos())
1006 selection.set(false);
1008 if (inset_owner && (selection.set() || lsel))
1009 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1013 string const LyXText::selectionAsString(Buffer const * buffer,
1016 if (!selection.set()) return string();
1019 // Special handling if the whole selection is within one paragraph
1020 if (selection.start.par() == selection.end.par()) {
1021 result += selection.start.par()->asString(buffer,
1022 selection.start.pos(),
1023 selection.end.pos(),
1028 // The selection spans more than one paragraph
1030 // First paragraph in selection
1031 result += selection.start.par()->asString(buffer,
1032 selection.start.pos(),
1033 selection.start.par()->size(),
1037 // The paragraphs in between (if any)
1038 LyXCursor tmpcur(selection.start);
1039 tmpcur.par(tmpcur.par()->next());
1040 while (tmpcur.par() != selection.end.par()) {
1041 result += tmpcur.par()->asString(buffer, 0,
1042 tmpcur.par()->size(),
1044 tmpcur.par(tmpcur.par()->next());
1047 // Last paragraph in selection
1048 result += selection.end.par()->asString(buffer, 0,
1049 selection.end.pos(), label);
1055 void LyXText::clearSelection() const
1057 selection.set(false);
1058 selection.mark(false);
1059 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1063 void LyXText::cursorHome(BufferView * bview) const
1065 setCursor(bview, cursor.par(), cursor.row()->pos());
1069 void LyXText::cursorEnd(BufferView * bview) const
1071 if (!cursor.row()->next()
1072 || cursor.row()->next()->par() != cursor.row()->par()) {
1073 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1075 if (cursor.par()->size() &&
1076 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1077 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1078 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1080 setCursor(bview,cursor.par(),
1081 rowLast(cursor.row()) + 1);
1087 void LyXText::cursorTop(BufferView * bview) const
1089 while (cursor.par()->previous())
1090 cursor.par(cursor.par()->previous());
1091 setCursor(bview, cursor.par(), 0);
1095 void LyXText::cursorBottom(BufferView * bview) const
1097 while (cursor.par()->next())
1098 cursor.par(cursor.par()->next());
1099 setCursor(bview, cursor.par(), cursor.par()->size());
1103 void LyXText::toggleFree(BufferView * bview,
1104 LyXFont const & font, bool toggleall)
1106 // If the mask is completely neutral, tell user
1107 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1108 // Could only happen with user style
1109 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1113 // Try implicit word selection
1114 // If there is a change in the language the implicit word selection
1116 LyXCursor resetCursor = cursor;
1117 bool implicitSelection = (font.language() == ignore_language
1118 && font.number() == LyXFont::IGNORE)
1119 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1122 setFont(bview, font, toggleall);
1124 // Implicit selections are cleared afterwards
1125 //and cursor is set to the original position.
1126 if (implicitSelection) {
1128 cursor = resetCursor;
1129 setCursor(bview, cursor.par(), cursor.pos());
1130 selection.cursor = cursor;
1133 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1137 string LyXText::getStringToIndex(BufferView * bview)
1141 // Try implicit word selection
1142 // If there is a change in the language the implicit word selection
1144 LyXCursor resetCursor = cursor;
1145 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1147 if (!selection.set()) {
1148 bview->owner()->message(_("Nothing to index!"));
1151 if (selection.start.par() != selection.end.par()) {
1152 bview->owner()->message(_("Cannot index more than one paragraph!"));
1156 idxstring = selectionAsString(bview->buffer(), false);
1158 // Implicit selections are cleared afterwards
1159 //and cursor is set to the original position.
1160 if (implicitSelection) {
1162 cursor = resetCursor;
1163 setCursor(bview, cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1170 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1171 Paragraph const * par) const
1173 if (textclasslist.Style(buf->params.textclass,
1174 par->getLayout()).labeltype != LABEL_MANUAL)
1177 return par->beginningOfMainBody();
1181 /* the DTP switches for paragraphs. LyX will store them in the
1182 * first physicla paragraph. When a paragraph is broken, the top settings
1183 * rest, the bottom settings are given to the new one. So I can make shure,
1184 * they do not duplicate themself and you cannnot make dirty things with
1187 void LyXText::setParagraph(BufferView * bview,
1188 bool line_top, bool line_bottom,
1189 bool pagebreak_top, bool pagebreak_bottom,
1190 VSpace const & space_top,
1191 VSpace const & space_bottom,
1192 Spacing const & spacing,
1194 string labelwidthstring,
1197 LyXCursor tmpcursor = cursor;
1198 if (!selection.set()) {
1199 selection.start = cursor;
1200 selection.end = cursor;
1203 // make sure that the depth behind the selection are restored, too
1204 Paragraph * endpar = selection.end.par()->next();
1205 Paragraph * undoendpar = endpar;
1207 if (endpar && endpar->getDepth()) {
1208 while (endpar && endpar->getDepth()) {
1209 endpar = endpar->next();
1210 undoendpar = endpar;
1214 // because of parindents etc.
1215 endpar = endpar->next();
1218 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1221 Paragraph * tmppar = selection.end.par();
1222 while (tmppar != selection.start.par()->previous()) {
1223 setCursor(bview, tmppar, 0);
1224 status(bview, LyXText::NEED_MORE_REFRESH);
1225 refresh_row = cursor.row();
1226 refresh_y = cursor.y() - cursor.row()->baseline();
1227 cursor.par()->params().lineTop(line_top);
1228 cursor.par()->params().lineBottom(line_bottom);
1229 cursor.par()->params().pagebreakTop(pagebreak_top);
1230 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1231 cursor.par()->params().spaceTop(space_top);
1232 cursor.par()->params().spaceBottom(space_bottom);
1233 cursor.par()->params().spacing(spacing);
1234 // does the layout allow the new alignment?
1235 if (align == LYX_ALIGN_LAYOUT)
1236 align = textclasslist
1237 .Style(bview->buffer()->params.textclass,
1238 cursor.par()->getLayout()).align;
1239 if (align & textclasslist
1240 .Style(bview->buffer()->params.textclass,
1241 cursor.par()->getLayout()).alignpossible) {
1242 if (align == textclasslist
1243 .Style(bview->buffer()->params.textclass,
1244 cursor.par()->getLayout()).align)
1245 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1247 cursor.par()->params().align(align);
1249 cursor.par()->setLabelWidthString(labelwidthstring);
1250 cursor.par()->params().noindent(noindent);
1251 tmppar = cursor.par()->previous();
1254 redoParagraphs(bview, selection.start, endpar);
1257 setCursor(bview, selection.start.par(), selection.start.pos());
1258 selection.cursor = cursor;
1259 setCursor(bview, selection.end.par(), selection.end.pos());
1260 setSelection(bview);
1261 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1263 bview->updateInset(inset_owner, true);
1267 char loweralphaCounter(int n)
1269 if (n < 1 || n > 26)
1279 char alphaCounter(int n)
1281 if (n < 1 || n > 26)
1289 char hebrewCounter(int n)
1291 static const char hebrew[22] = {
1292 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1293 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1294 '÷', 'ø', 'ù', 'ú'
1296 if (n < 1 || n > 22)
1304 string const romanCounter(int n)
1306 static char const * roman[20] = {
1307 "i", "ii", "iii", "iv", "v",
1308 "vi", "vii", "viii", "ix", "x",
1309 "xi", "xii", "xiii", "xiv", "xv",
1310 "xvi", "xvii", "xviii", "xix", "xx"
1312 if (n < 1 || n > 20)
1321 // set the counter of a paragraph. This includes the labels
1322 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1324 LyXLayout const & layout =
1325 textclasslist.Style(buf->params.textclass,
1328 LyXTextClass const & textclass =
1329 textclasslist.TextClass(buf->params.textclass);
1331 // copy the prev-counters to this one,
1332 // unless this is the first paragraph
1333 if (par->previous()) {
1334 for (int i = 0; i < 10; ++i) {
1335 par->setCounter(i, par->previous()->getFirstCounter(i));
1337 par->params().appendix(par->previous()->params().appendix());
1338 if (!par->params().appendix() && par->params().startOfAppendix()) {
1339 par->params().appendix(true);
1340 for (int i = 0; i < 10; ++i) {
1341 par->setCounter(i, 0);
1344 par->enumdepth = par->previous()->enumdepth;
1345 par->itemdepth = par->previous()->itemdepth;
1347 for (int i = 0; i < 10; ++i) {
1348 par->setCounter(i, 0);
1350 par->params().appendix(par->params().startOfAppendix());
1355 /* Maybe we have to increment the enumeration depth.
1356 * BUT, enumeration in a footnote is considered in isolation from its
1357 * surrounding paragraph so don't increment if this is the
1358 * first line of the footnote
1359 * AND, bibliographies can't have their depth changed ie. they
1360 * are always of depth 0
1363 && par->previous()->getDepth() < par->getDepth()
1364 && textclasslist.Style(buf->params.textclass,
1365 par->previous()->getLayout()
1366 ).labeltype == LABEL_COUNTER_ENUMI
1367 && par->enumdepth < 3
1368 && layout.labeltype != LABEL_BIBLIO) {
1372 // Maybe we have to decrement the enumeration depth, see note above
1374 && par->previous()->getDepth() > par->getDepth()
1375 && layout.labeltype != LABEL_BIBLIO) {
1376 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1377 par->setCounter(6 + par->enumdepth,
1378 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1379 /* reset the counters.
1380 * A depth change is like a breaking layout
1382 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1383 par->setCounter(i, 0);
1386 if (!par->params().labelString().empty()) {
1387 par->params().labelString(string());
1390 if (layout.margintype == MARGIN_MANUAL) {
1391 if (par->params().labelWidthString().empty()) {
1392 par->setLabelWidthString(layout.labelstring());
1395 par->setLabelWidthString(string());
1398 // is it a layout that has an automatic label?
1399 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1401 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1402 if (i >= 0 && i<= buf->params.secnumdepth) {
1403 par->incCounter(i); // increment the counter
1405 // Is there a label? Useful for Chapter layout
1406 if (!par->params().appendix()) {
1407 if (!layout.labelstring().empty())
1408 par->params().labelString(layout.labelstring());
1410 par->params().labelString(string());
1412 if (!layout.labelstring_appendix().empty())
1413 par->params().labelString(layout.labelstring_appendix());
1415 par->params().labelString(string());
1420 if (!par->params().appendix()) {
1421 switch (2 * LABEL_COUNTER_CHAPTER -
1422 textclass.maxcounter() + i) {
1423 case LABEL_COUNTER_CHAPTER:
1424 s << par->getCounter(i);
1426 case LABEL_COUNTER_SECTION:
1427 s << par->getCounter(i - 1) << '.'
1428 << par->getCounter(i);
1430 case LABEL_COUNTER_SUBSECTION:
1431 s << par->getCounter(i - 2) << '.'
1432 << par->getCounter(i - 1) << '.'
1433 << par->getCounter(i);
1435 case LABEL_COUNTER_SUBSUBSECTION:
1436 s << par->getCounter(i - 3) << '.'
1437 << par->getCounter(i - 2) << '.'
1438 << par->getCounter(i - 1) << '.'
1439 << par->getCounter(i);
1442 case LABEL_COUNTER_PARAGRAPH:
1443 s << par->getCounter(i - 4) << '.'
1444 << par->getCounter(i - 3) << '.'
1445 << par->getCounter(i - 2) << '.'
1446 << par->getCounter(i - 1) << '.'
1447 << par->getCounter(i);
1449 case LABEL_COUNTER_SUBPARAGRAPH:
1450 s << par->getCounter(i - 5) << '.'
1451 << par->getCounter(i - 4) << '.'
1452 << par->getCounter(i - 3) << '.'
1453 << par->getCounter(i - 2) << '.'
1454 << par->getCounter(i - 1) << '.'
1455 << par->getCounter(i);
1459 // Can this ever be reached? And in the
1460 // case it is, how can this be correct?
1462 s << par->getCounter(i) << '.';
1465 } else { // appendix
1466 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1467 case LABEL_COUNTER_CHAPTER:
1468 if (par->isRightToLeftPar(buf->params))
1469 s << hebrewCounter(par->getCounter(i));
1471 s << alphaCounter(par->getCounter(i));
1473 case LABEL_COUNTER_SECTION:
1474 if (par->isRightToLeftPar(buf->params))
1475 s << hebrewCounter(par->getCounter(i - 1));
1477 s << alphaCounter(par->getCounter(i - 1));
1480 << par->getCounter(i);
1483 case LABEL_COUNTER_SUBSECTION:
1484 if (par->isRightToLeftPar(buf->params))
1485 s << hebrewCounter(par->getCounter(i - 2));
1487 s << alphaCounter(par->getCounter(i - 2));
1490 << par->getCounter(i-1) << '.'
1491 << par->getCounter(i);
1494 case LABEL_COUNTER_SUBSUBSECTION:
1495 if (par->isRightToLeftPar(buf->params))
1496 s << hebrewCounter(par->getCounter(i-3));
1498 s << alphaCounter(par->getCounter(i-3));
1501 << par->getCounter(i-2) << '.'
1502 << par->getCounter(i-1) << '.'
1503 << par->getCounter(i);
1506 case LABEL_COUNTER_PARAGRAPH:
1507 if (par->isRightToLeftPar(buf->params))
1508 s << hebrewCounter(par->getCounter(i-4));
1510 s << alphaCounter(par->getCounter(i-4));
1513 << par->getCounter(i-3) << '.'
1514 << par->getCounter(i-2) << '.'
1515 << par->getCounter(i-1) << '.'
1516 << par->getCounter(i);
1519 case LABEL_COUNTER_SUBPARAGRAPH:
1520 if (par->isRightToLeftPar(buf->params))
1521 s << hebrewCounter(par->getCounter(i-5));
1523 s << alphaCounter(par->getCounter(i-5));
1526 << par->getCounter(i-4) << '.'
1527 << par->getCounter(i-3) << '.'
1528 << par->getCounter(i-2) << '.'
1529 << par->getCounter(i-1) << '.'
1530 << par->getCounter(i);
1534 // Can this ever be reached? And in the
1535 // case it is, how can this be correct?
1537 s << par->getCounter(i) << '.';
1543 par->params().labelString(par->params().labelString() +s.str().c_str());
1544 // We really want to remove the c_str as soon as
1547 for (i++; i < 10; ++i) {
1548 // reset the following counters
1549 par->setCounter(i, 0);
1551 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1552 for (i++; i < 10; ++i) {
1553 // reset the following counters
1554 par->setCounter(i, 0);
1556 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1557 par->incCounter(i + par->enumdepth);
1558 int number = par->getCounter(i + par->enumdepth);
1562 switch (par->enumdepth) {
1564 if (par->isRightToLeftPar(buf->params))
1566 << hebrewCounter(number)
1570 << loweralphaCounter(number)
1574 if (par->isRightToLeftPar(buf->params))
1575 s << '.' << romanCounter(number);
1577 s << romanCounter(number) << '.';
1580 if (par->isRightToLeftPar(buf->params))
1582 << alphaCounter(number);
1584 s << alphaCounter(number)
1588 if (par->isRightToLeftPar(buf->params))
1595 par->params().labelString(s.str().c_str());
1597 for (i += par->enumdepth + 1; i < 10; ++i) {
1598 // reset the following counters
1599 par->setCounter(i, 0);
1603 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1604 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1606 int number = par->getCounter(i);
1608 InsetCommandParams p( "bibitem" );
1609 par->bibkey = new InsetBibKey(p);
1611 par->bibkey->setCounter(number);
1612 par->params().labelString(layout.labelstring());
1614 // In biblio should't be following counters but...
1616 string s = layout.labelstring();
1618 // the caption hack:
1619 if (layout.labeltype == LABEL_SENSITIVE) {
1620 bool isOK (par->inInset() && par->inInset()->owner() &&
1621 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1624 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1626 = floatList.getType(tmp->type());
1627 // We should get the correct number here too.
1628 s = fl.name() + " #:";
1630 /* par->SetLayout(0);
1631 s = layout->labelstring; */
1632 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1633 ? " :úåòîùî øñç" : "Senseless: ";
1636 par->params().labelString(s);
1638 /* reset the enumeration counter. They are always resetted
1639 * when there is any other layout between */
1640 for (int i = 6 + par->enumdepth; i < 10; ++i)
1641 par->setCounter(i, 0);
1646 // Updates all counters BEHIND the row. Changed paragraphs
1647 // with a dynamic left margin will be rebroken.
1648 void LyXText::updateCounters(BufferView * bview, Row * row) const
1656 par = row->par()->next();
1660 while (row->par() != par)
1663 setCounter(bview->buffer(), par);
1665 // now check for the headline layouts. remember that they
1666 // have a dynamic left margin
1667 if ((textclasslist.Style(bview->buffer()->params.textclass,
1668 par->layout).margintype == MARGIN_DYNAMIC
1669 || textclasslist.Style(bview->buffer()->params.textclass,
1670 par->layout).labeltype == LABEL_SENSITIVE)) {
1672 // Rebreak the paragraph
1673 removeParagraph(row);
1674 appendParagraph(bview, row);
1681 void LyXText::insertInset(BufferView * bview, Inset * inset)
1683 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1685 setUndo(bview, Undo::INSERT,
1686 cursor.par(), cursor.par()->next());
1687 cursor.par()->insertInset(cursor.pos(), inset);
1688 // Just to rebreak and refresh correctly.
1689 // The character will not be inserted a second time
1690 insertChar(bview, Paragraph::META_INSET);
1692 // If we enter a highly editable inset the cursor should be to before
1693 // the inset. This couldn't happen before as Undo was not handled inside
1694 // inset now after the Undo LyX tries to call inset->Edit(...) again
1695 // and cannot do this as the cursor is behind the inset and GetInset
1696 // does not return the inset!
1697 if (isHighlyEditableInset(inset)) {
1698 cursorLeft(bview, true);
1704 void LyXText::copyEnvironmentType()
1706 copylayouttype = cursor.par()->getLayout();
1710 void LyXText::pasteEnvironmentType(BufferView * bview)
1712 setLayout(bview, copylayouttype);
1716 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1718 // Stuff what we got on the clipboard. Even if there is no selection.
1720 // There is a problem with having the stuffing here in that the
1721 // larger the selection the slower LyX will get. This can be
1722 // solved by running the line below only when the selection has
1723 // finished. The solution used currently just works, to make it
1724 // faster we need to be more clever and probably also have more
1725 // calls to stuffClipboard. (Lgb)
1726 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1728 // This doesn't make sense, if there is no selection
1729 if (!selection.set())
1732 // OK, we have a selection. This is always between selection.start
1733 // and selection.end
1735 // make sure that the depth behind the selection are restored, too
1736 Paragraph * endpar = selection.end.par()->next();
1737 Paragraph * undoendpar = endpar;
1739 if (endpar && endpar->getDepth()) {
1740 while (endpar && endpar->getDepth()) {
1741 endpar = endpar->next();
1742 undoendpar = endpar;
1744 } else if (endpar) {
1745 endpar = endpar->next(); // because of parindents etc.
1748 setUndo(bview, Undo::DELETE,
1749 selection.start.par(), undoendpar);
1751 // there are two cases: cut only within one paragraph or
1752 // more than one paragraph
1753 if (selection.start.par() == selection.end.par()) {
1754 // only within one paragraph
1755 endpar = selection.end.par();
1756 int pos = selection.end.pos();
1757 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1758 selection.start.pos(), pos,
1759 bview->buffer()->params.textclass,
1761 selection.end.pos(pos);
1763 endpar = selection.end.par();
1764 int pos = selection.end.pos();
1765 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1766 selection.start.pos(), pos,
1767 bview->buffer()->params.textclass,
1770 selection.end.par(endpar);
1771 selection.end.pos(pos);
1772 cursor.pos(selection.end.pos());
1774 endpar = endpar->next();
1776 // sometimes necessary
1778 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1780 redoParagraphs(bview, selection.start, endpar);
1782 // cutSelection can invalidate the cursor so we need to set
1784 cursor = selection.start;
1786 // need a valid cursor. (Lgb)
1789 setCursor(bview, cursor.par(), cursor.pos());
1790 selection.cursor = cursor;
1791 updateCounters(bview, cursor.row());
1795 void LyXText::copySelection(BufferView * bview)
1797 // Stuff what we got on the clipboard. Even if there is no selection.
1799 // There is a problem with having the stuffing here in that the
1800 // larger the selection the slower LyX will get. This can be
1801 // solved by running the line below only when the selection has
1802 // finished. The solution used currently just works, to make it
1803 // faster we need to be more clever and probably also have more
1804 // calls to stuffClipboard. (Lgb)
1805 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1807 // this doesnt make sense, if there is no selection
1808 if (!selection.set())
1811 // ok we have a selection. This is always between selection.start
1812 // and sel_end cursor
1814 // copy behind a space if there is one
1815 while (selection.start.par()->size() > selection.start.pos()
1816 && selection.start.par()->isLineSeparator(selection.start.pos())
1817 && (selection.start.par() != selection.end.par()
1818 || selection.start.pos() < selection.end.pos()))
1819 selection.start.pos(selection.start.pos() + 1);
1821 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1822 selection.start.pos(), selection.end.pos(),
1823 bview->buffer()->params.textclass);
1827 void LyXText::pasteSelection(BufferView * bview)
1829 // this does not make sense, if there is nothing to paste
1830 if (!CutAndPaste::checkPastePossible(cursor.par()))
1833 setUndo(bview, Undo::INSERT,
1834 cursor.par(), cursor.par()->next());
1837 Paragraph * actpar = cursor.par();
1838 int pos = cursor.pos();
1840 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1841 bview->buffer()->params.textclass);
1843 redoParagraphs(bview, cursor, endpar);
1845 setCursor(bview, cursor.par(), cursor.pos());
1848 setCursor(bview, actpar, pos);
1849 updateCounters(bview, cursor.row());
1853 // returns a pointer to the very first Paragraph
1854 Paragraph * LyXText::firstParagraph() const
1856 return ownerParagraph();
1860 // sets the selection over the number of characters of string, no check!!
1861 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1866 selection.cursor = cursor;
1867 for (string::size_type i = 0; i < str.length(); ++i)
1869 setSelection(bview);
1873 // simple replacing. The font of the first selected character is used
1874 void LyXText::replaceSelectionWithString(BufferView * bview,
1877 setCursorParUndo(bview);
1880 if (!selection.set()) { // create a dummy selection
1881 selection.end = cursor;
1882 selection.start = cursor;
1885 // Get font setting before we cut
1886 pos_type pos = selection.end.pos();
1887 LyXFont const font = selection.start.par()
1888 ->getFontSettings(bview->buffer()->params,
1889 selection.start.pos());
1891 // Insert the new string
1892 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1893 selection.end.par()->insertChar(pos, (*cit), font);
1897 // Cut the selection
1898 cutSelection(bview, true, false);
1904 // needed to insert the selection
1905 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1907 Paragraph * par = cursor.par();
1908 pos_type pos = cursor.pos();
1909 Paragraph * endpar = cursor.par()->next();
1911 setCursorParUndo(bview);
1913 // only to be sure, should not be neccessary
1916 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1918 redoParagraphs(bview, cursor, endpar);
1919 setCursor(bview, cursor.par(), cursor.pos());
1920 selection.cursor = cursor;
1921 setCursor(bview, par, pos);
1922 setSelection(bview);
1926 // turns double-CR to single CR, others where converted into one
1927 // blank. Then InsertStringAsLines is called
1928 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1930 string linestr(str);
1931 bool newline_inserted = false;
1932 for (string::size_type i = 0; i < linestr.length(); ++i) {
1933 if (linestr[i] == '\n') {
1934 if (newline_inserted) {
1935 // we know that \r will be ignored by
1936 // InsertStringA. Of course, it is a dirty
1937 // trick, but it works...
1938 linestr[i - 1] = '\r';
1942 newline_inserted = true;
1944 } else if (IsPrintable(linestr[i])) {
1945 newline_inserted = false;
1948 insertStringAsLines(bview, linestr);
1952 bool LyXText::gotoNextInset(BufferView * bview,
1953 std::vector<Inset::Code> const & codes,
1954 string const & contents) const
1956 LyXCursor res = cursor;
1959 if (res.pos() < res.par()->size() - 1) {
1960 res.pos(res.pos() + 1);
1962 res.par(res.par()->next());
1966 } while (res.par() &&
1967 !(res.par()->isInset(res.pos())
1968 && (inset = res.par()->getInset(res.pos())) != 0
1969 && find(codes.begin(), codes.end(), inset->lyxCode())
1971 && (contents.empty() ||
1972 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1976 setCursor(bview, res.par(), res.pos(), false);
1983 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1986 LyXCursor tmpcursor;
1990 Row * row = getRow(par, pos, y);
1992 // is there a break one row above
1993 if (row->previous() && row->previous()->par() == row->par()) {
1994 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1995 if (z >= row->pos()) {
1996 // set the dimensions of the row above
1997 y -= row->previous()->height();
1999 refresh_row = row->previous();
2000 status(bview, LyXText::NEED_MORE_REFRESH);
2002 breakAgain(bview, row->previous());
2004 // set the cursor again. Otherwise
2005 // dangling pointers are possible
2006 setCursor(bview, cursor.par(), cursor.pos(),
2007 false, cursor.boundary());
2008 selection.cursor = cursor;
2013 int const tmpheight = row->height();
2014 pos_type const tmplast = rowLast(row);
2018 breakAgain(bview, row);
2019 if (row->height() == tmpheight && rowLast(row) == tmplast)
2020 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2022 status(bview, LyXText::NEED_MORE_REFRESH);
2024 // check the special right address boxes
2025 if (textclasslist.Style(bview->buffer()->params.textclass,
2026 par->getLayout()).margintype
2027 == MARGIN_RIGHT_ADDRESS_BOX)
2035 redoDrawingOfParagraph(bview, tmpcursor);
2038 // set the cursor again. Otherwise dangling pointers are possible
2039 // also set the selection
2041 if (selection.set()) {
2043 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2044 false, selection.cursor.boundary());
2045 selection.cursor = cursor;
2046 setCursorIntern(bview, selection.start.par(),
2047 selection.start.pos(),
2048 false, selection.start.boundary());
2049 selection.start = cursor;
2050 setCursorIntern(bview, selection.end.par(),
2051 selection.end.pos(),
2052 false, selection.end.boundary());
2053 selection.end = cursor;
2054 setCursorIntern(bview, last_sel_cursor.par(),
2055 last_sel_cursor.pos(),
2056 false, last_sel_cursor.boundary());
2057 last_sel_cursor = cursor;
2060 setCursorIntern(bview, cursor.par(), cursor.pos(),
2061 false, cursor.boundary());
2065 // returns false if inset wasn't found
2066 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2068 // first check the current paragraph
2069 int pos = cursor.par()->getPositionOfInset(inset);
2071 checkParagraph(bview, cursor.par(), pos);
2075 // check every paragraph
2077 Paragraph * par = firstParagraph();
2079 pos = par->getPositionOfInset(inset);
2081 checkParagraph(bview, par, pos);
2091 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2093 bool setfont, bool boundary) const
2095 LyXCursor old_cursor = cursor;
2096 setCursorIntern(bview, par, pos, setfont, boundary);
2097 deleteEmptyParagraphMechanism(bview, old_cursor);
2101 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2102 pos_type pos, bool boundary) const
2106 cur.boundary(boundary);
2108 // get the cursor y position in text
2110 Row * row = getRow(par, pos, y);
2111 // y is now the beginning of the cursor row
2112 y += row->baseline();
2113 // y is now the cursor baseline
2116 // now get the cursors x position
2118 float fill_separator;
2120 float fill_label_hfill;
2121 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2123 pos_type cursor_vpos = 0;
2124 pos_type last = rowLastPrintable(row);
2126 if (pos > last + 1) // This shouldn't happen.
2128 else if (pos < row->pos())
2131 if (last < row->pos())
2132 cursor_vpos = row->pos();
2133 else if (pos > last && !boundary)
2134 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2135 ? row->pos() : last + 1;
2136 else if (pos > row->pos() &&
2137 (pos > last || boundary))
2138 /// Place cursor after char at (logical) position pos - 1
2139 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2140 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2142 /// Place cursor before char at (logical) position pos
2143 cursor_vpos = (bidi_level(pos) % 2 == 0)
2144 ? log2vis(pos) : log2vis(pos) + 1;
2146 pos_type main_body =
2147 beginningOfMainBody(bview->buffer(), row->par());
2148 if ((main_body > 0) &&
2149 ((main_body-1 > last) ||
2150 !row->par()->isLineSeparator(main_body-1)))
2153 for (pos_type vpos = row->pos();
2154 vpos < cursor_vpos; ++vpos) {
2155 pos = vis2log(vpos);
2156 if (main_body > 0 && pos == main_body - 1) {
2157 x += fill_label_hfill +
2158 lyxfont::width(textclasslist.Style(
2159 bview->buffer()->params.textclass,
2160 row->par()->getLayout())
2162 getLabelFont(bview->buffer(), row->par()));
2163 if (row->par()->isLineSeparator(main_body-1))
2164 x -= singleWidth(bview, row->par(),main_body-1);
2166 if (hfillExpansion(bview->buffer(), row, pos)) {
2167 x += singleWidth(bview, row->par(), pos);
2168 if (pos >= main_body)
2171 x += fill_label_hfill;
2172 } else if (row->par()->isSeparator(pos)) {
2173 x += singleWidth(bview, row->par(), pos);
2174 if (pos >= main_body)
2175 x += fill_separator;
2177 x += singleWidth(bview, row->par(), pos);
2186 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2188 bool setfont, bool boundary) const
2190 InsetText * it = static_cast<InsetText *>(par->inInset());
2192 if (it != inset_owner) {
2193 lyxerr << "InsetText is " << it << endl;
2194 lyxerr << "inset_owner is " << inset_owner << endl;
2195 #warning I believe this code is wrong. (Lgb)
2196 #warning Jürgen, have a look at this. (Lgb)
2197 #warning Hmmm, I guess you are right but we
2198 #warning should verify when this is needed
2199 // Jürgen, would you like to have a look?
2200 // I guess we need to move the outer cursor
2201 // and open and lock the inset (bla bla bla)
2202 // stuff I don't know... so can you have a look?
2204 // I moved the lyxerr stuff in here so we can see if
2205 // this is actually really needed and where!
2207 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2213 setCursor(bview, cursor, par, pos, boundary);
2215 setCurrentFont(bview);
2219 void LyXText::setCurrentFont(BufferView * bview) const
2221 pos_type pos = cursor.pos();
2222 if (cursor.boundary() && pos > 0)
2226 if (pos == cursor.par()->size())
2228 else // potentional bug... BUG (Lgb)
2229 if (cursor.par()->isSeparator(pos)) {
2230 if (pos > cursor.row()->pos() &&
2231 bidi_level(pos) % 2 ==
2232 bidi_level(pos - 1) % 2)
2234 else if (pos + 1 < cursor.par()->size())
2240 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2241 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2243 if (cursor.pos() == cursor.par()->size() &&
2244 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2245 !cursor.boundary()) {
2246 Language const * lang =
2247 cursor.par()->getParLanguage(bview->buffer()->params);
2248 current_font.setLanguage(lang);
2249 current_font.setNumber(LyXFont::OFF);
2250 real_current_font.setLanguage(lang);
2251 real_current_font.setNumber(LyXFont::OFF);
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2258 LyXCursor old_cursor = cursor;
2260 setCursorFromCoordinates(bview, cursor, x, y);
2261 setCurrentFont(bview);
2262 deleteEmptyParagraphMechanism(bview, old_cursor);
2266 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2269 // Get the row first.
2271 Row * row = getRowNearY(y);
2273 pos_type const column = getColumnNearX(bview, row, x, bound);
2274 cur.par(row->par());
2275 cur.pos(row->pos() + column);
2277 cur.y(y + row->baseline());
2279 cur.boundary(bound);
2283 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2285 if (cursor.pos() > 0) {
2286 bool boundary = cursor.boundary();
2287 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2288 if (!internal && !boundary &&
2289 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2290 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2291 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2292 Paragraph * par = cursor.par()->previous();
2293 setCursor(bview, par, par->size());
2298 void LyXText::cursorRight(BufferView * bview, bool internal) const
2300 if (!internal && cursor.boundary() &&
2301 !cursor.par()->isNewline(cursor.pos()))
2302 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2303 else if (cursor.pos() < cursor.par()->size()) {
2304 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2306 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2307 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2308 } else if (cursor.par()->next())
2309 setCursor(bview, cursor.par()->next(), 0);
2313 void LyXText::cursorUp(BufferView * bview) const
2315 setCursorFromCoordinates(bview, cursor.x_fix(),
2316 cursor.y() - cursor.row()->baseline() - 1);
2320 void LyXText::cursorDown(BufferView * bview) const
2322 setCursorFromCoordinates(bview, cursor.x_fix(),
2323 cursor.y() - cursor.row()->baseline()
2324 + cursor.row()->height() + 1);
2328 void LyXText::cursorUpParagraph(BufferView * bview) const
2330 if (cursor.pos() > 0) {
2331 setCursor(bview, cursor.par(), 0);
2333 else if (cursor.par()->previous()) {
2334 setCursor(bview, cursor.par()->previous(), 0);
2339 void LyXText::cursorDownParagraph(BufferView * bview) const
2341 if (cursor.par()->next()) {
2342 setCursor(bview, cursor.par()->next(), 0);
2344 setCursor(bview, cursor.par(), cursor.par()->size());
2348 // fix the cursor `cur' after a characters has been deleted at `where'
2349 // position. Called by deleteEmptyParagraphMechanism
2350 void LyXText::fixCursorAfterDelete(BufferView * bview,
2352 LyXCursor const & where) const
2354 // if cursor is not in the paragraph where the delete occured,
2356 if (cur.par() != where.par())
2359 // if cursor position is after the place where the delete occured,
2361 if (cur.pos() > where.pos())
2362 cur.pos(cur.pos()-1);
2364 // recompute row et al. for this cursor
2365 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2369 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2370 LyXCursor const & old_cursor) const
2372 // don't delete anything if this is the ONLY paragraph!
2373 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2376 // Would be wrong to delete anything if we have a selection.
2377 if (selection.set()) return;
2379 // We allow all kinds of "mumbo-jumbo" when freespacing.
2380 if (textclasslist.Style(bview->buffer()->params.textclass,
2381 old_cursor.par()->getLayout()).free_spacing ||
2382 old_cursor.par()->isFreeSpacing())
2387 bool deleted = false;
2389 /* Ok I'll put some comments here about what is missing.
2390 I have fixed BackSpace (and thus Delete) to not delete
2391 double-spaces automagically. I have also changed Cut,
2392 Copy and Paste to hopefully do some sensible things.
2393 There are still some small problems that can lead to
2394 double spaces stored in the document file or space at
2395 the beginning of paragraphs. This happens if you have
2396 the cursor betwenn to spaces and then save. Or if you
2397 cut and paste and the selection have a space at the
2398 beginning and then save right after the paste. I am
2399 sure none of these are very hard to fix, but I will
2400 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2401 that I can get some feedback. (Lgb)
2404 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2405 // delete the LineSeparator.
2408 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2409 // delete the LineSeparator.
2412 // If the pos around the old_cursor were spaces, delete one of them.
2413 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2414 // Only if the cursor has really moved
2416 if (old_cursor.pos() > 0
2417 && old_cursor.pos() < old_cursor.par()->size()
2418 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2419 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2420 old_cursor.par()->erase(old_cursor.pos() - 1);
2421 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2423 #ifdef WITH_WARNINGS
2424 #warning This will not work anymore when we have multiple views of the same buffer
2425 // In this case, we will have to correct also the cursors held by
2426 // other bufferviews. It will probably be easier to do that in a more
2427 // automated way in LyXCursor code. (JMarc 26/09/2001)
2429 // correct all cursors held by the LyXText
2430 fixCursorAfterDelete(bview, cursor, old_cursor);
2431 fixCursorAfterDelete(bview, selection.cursor,
2433 fixCursorAfterDelete(bview, selection.start,
2435 fixCursorAfterDelete(bview, selection.end, old_cursor);
2436 fixCursorAfterDelete(bview, last_sel_cursor,
2438 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2439 fixCursorAfterDelete(bview, toggle_end_cursor,
2445 // Do not delete empty paragraphs with keepempty set.
2446 if ((textclasslist.Style(bview->buffer()->params.textclass,
2447 old_cursor.par()->getLayout())).keepempty)
2450 // only do our magic if we changed paragraph
2451 if (old_cursor.par() == cursor.par())
2454 if ((old_cursor.par()->size() == 0
2455 || (old_cursor.par()->size() == 1
2456 && old_cursor.par()->isLineSeparator(0)))) {
2457 // ok, we will delete anything
2458 LyXCursor tmpcursor;
2460 // make sure that you do not delete any environments
2461 status(bview, LyXText::NEED_MORE_REFRESH);
2464 if (old_cursor.row()->previous()) {
2465 refresh_row = old_cursor.row()->previous();
2466 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2468 cursor = old_cursor; // that undo can restore the right cursor position
2469 Paragraph * endpar = old_cursor.par()->next();
2470 if (endpar && endpar->getDepth()) {
2471 while (endpar && endpar->getDepth()) {
2472 endpar = endpar->next();
2475 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2479 removeRow(old_cursor.row());
2480 if (ownerParagraph() == old_cursor.par()) {
2481 ownerParagraph(ownerParagraph()->next());
2484 delete old_cursor.par();
2486 /* Breakagain the next par. Needed because of
2487 * the parindent that can occur or dissappear.
2488 * The next row can change its height, if
2489 * there is another layout before */
2490 if (refresh_row->next()) {
2491 breakAgain(bview, refresh_row->next());
2492 updateCounters(bview, refresh_row);
2494 setHeightOfRow(bview, refresh_row);
2496 refresh_row = old_cursor.row()->next();
2497 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2500 cursor = old_cursor; // that undo can restore the right cursor position
2501 Paragraph * endpar = old_cursor.par()->next();
2502 if (endpar && endpar->getDepth()) {
2503 while (endpar && endpar->getDepth()) {
2504 endpar = endpar->next();
2507 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2511 removeRow(old_cursor.row());
2513 if (ownerParagraph() == old_cursor.par()) {
2514 ownerParagraph(ownerParagraph()->next());
2517 delete old_cursor.par();
2519 /* Breakagain the next par. Needed because of
2520 the parindent that can occur or dissappear.
2521 The next row can change its height, if
2522 there is another layout before */
2524 breakAgain(bview, refresh_row);
2525 updateCounters(bview, refresh_row->previous());
2530 setCursorIntern(bview, cursor.par(), cursor.pos());
2532 if (selection.cursor.par() == old_cursor.par()
2533 && selection.cursor.pos() == selection.cursor.pos()) {
2534 // correct selection
2535 selection.cursor = cursor;
2539 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2540 redoParagraphs(bview, old_cursor,
2541 old_cursor.par()->next());
2543 setCursorIntern(bview, cursor.par(), cursor.pos());
2544 selection.cursor = cursor;
2550 void LyXText::toggleAppendix(BufferView * bview)
2552 Paragraph * par = cursor.par();
2553 bool start = !par->params().startOfAppendix();
2555 // ensure that we have only one start_of_appendix in this document
2556 Paragraph * tmp = firstParagraph();
2557 for (; tmp; tmp = tmp->next()) {
2558 tmp->params().startOfAppendix(false);
2561 par->params().startOfAppendix(start);
2563 // we can set the refreshing parameters now
2564 status(bview, LyXText::NEED_MORE_REFRESH);
2566 refresh_row = 0; // not needed for full update
2567 updateCounters(bview, 0);
2568 setCursor(bview, cursor.par(), cursor.pos());
2572 Paragraph * LyXText::ownerParagraph() const
2575 return inset_owner->paragraph();
2577 return bv_owner->buffer()->paragraph;
2581 void LyXText::ownerParagraph(Paragraph * p) const
2584 inset_owner->paragraph(p);
2586 bv_owner->buffer()->paragraph = p;
2591 void LyXText::ownerParagraph(int id, Paragraph * p) const
2593 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2594 if (op && op->inInset()) {
2595 static_cast<InsetText *>(op->inInset())->paragraph(p);
2598 inset_owner->paragraph(p);
2600 bv_owner->buffer()->paragraph = p;
2606 LyXText::text_status LyXText::status() const
2612 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2614 // well as much as I know && binds more then || so the above and the
2615 // below are identical (this for your known use of parentesis!)
2616 // Now some explanation:
2617 // We should only go up with refreshing code so this means that if
2618 // we have a MORE refresh we should never set it to LITTLE if we still
2619 // didn't handle it (and then it will be UNCHANGED. Now as long as
2620 // we stay inside one LyXText this may work but we need to tell the
2621 // outermost LyXText that it should REALLY draw us if there is some
2622 // change in a Inset::LyXText. So you see that when we are inside a
2623 // inset's LyXText we give the LITTLE to the outermost LyXText to
2624 // tell'em that it should redraw the actual row (where the inset
2625 // resides! Capito?!
2627 if ((status_ != NEED_MORE_REFRESH)
2628 || (status_ == NEED_MORE_REFRESH
2629 && st != NEED_VERY_LITTLE_REFRESH))
2632 if (inset_owner && st != UNCHANGED) {
2633 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2634 if (!bview->text->refresh_row) {
2635 bview->text->refresh_row = bview->text->cursor.row();
2636 bview->text->refresh_y = bview->text->cursor.y() -
2637 bview->text->cursor.row()->baseline();