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, 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 // 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());
891 if (tmprow->next()) {
892 tmppar = tmprow->next()->par();
898 // remove the first one
899 tmprow2 = tmprow; /* this is because tmprow->previous()
901 tmprow = tmprow->previous();
904 tmppar = first_phys_par;
908 insertParagraph(bview, tmppar, tmprow);
912 while (tmprow->next()
913 && tmprow->next()->par() == tmppar) {
914 tmprow = tmprow->next();
916 tmppar = tmppar->next();
918 } while (tmppar && tmppar != endpar);
920 // this is because of layout changes
922 refresh_y -= refresh_row->height();
923 setHeightOfRow(bview, refresh_row);
925 refresh_row = firstrow;
927 setHeightOfRow(bview, refresh_row);
930 if (tmprow && tmprow->next())
931 setHeightOfRow(bview, tmprow->next());
935 bool LyXText::fullRebreak(BufferView * bview)
941 if (need_break_row) {
942 breakAgain(bview, need_break_row);
950 // important for the screen
953 /* the cursor set functions have a special mechanism. When they
954 * realize, that you left an empty paragraph, they will delete it.
955 * They also delete the corresponding row */
957 // need the selection cursor:
958 void LyXText::setSelection(BufferView * bview)
960 bool const lsel = selection.set();
962 if (!selection.set()) {
963 last_sel_cursor = selection.cursor;
964 selection.start = selection.cursor;
965 selection.end = selection.cursor;
970 // first the toggling area
971 if (cursor.y() < last_sel_cursor.y()
972 || (cursor.y() == last_sel_cursor.y()
973 && cursor.x() < last_sel_cursor.x())) {
974 toggle_end_cursor = last_sel_cursor;
975 toggle_cursor = cursor;
977 toggle_end_cursor = cursor;
978 toggle_cursor = last_sel_cursor;
981 last_sel_cursor = cursor;
983 // and now the whole selection
985 if (selection.cursor.par() == cursor.par())
986 if (selection.cursor.pos() < cursor.pos()) {
987 selection.end = cursor;
988 selection.start = selection.cursor;
990 selection.end = selection.cursor;
991 selection.start = cursor;
993 else if (selection.cursor.y() < cursor.y() ||
994 (selection.cursor.y() == cursor.y()
995 && selection.cursor.x() < cursor.x())) {
996 selection.end = cursor;
997 selection.start = selection.cursor;
1000 selection.end = selection.cursor;
1001 selection.start = cursor;
1004 // a selection with no contents is not a selection
1005 if (selection.start.par() == selection.end.par() &&
1006 selection.start.pos() == selection.end.pos())
1007 selection.set(false);
1009 if (inset_owner && (selection.set() || lsel))
1010 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1014 string const LyXText::selectionAsString(Buffer const * buffer,
1017 if (!selection.set()) return string();
1020 // Special handling if the whole selection is within one paragraph
1021 if (selection.start.par() == selection.end.par()) {
1022 result += selection.start.par()->asString(buffer,
1023 selection.start.pos(),
1024 selection.end.pos(),
1029 // The selection spans more than one paragraph
1031 // First paragraph in selection
1032 result += selection.start.par()->asString(buffer,
1033 selection.start.pos(),
1034 selection.start.par()->size(),
1038 // The paragraphs in between (if any)
1039 LyXCursor tmpcur(selection.start);
1040 tmpcur.par(tmpcur.par()->next());
1041 while (tmpcur.par() != selection.end.par()) {
1042 result += tmpcur.par()->asString(buffer, 0,
1043 tmpcur.par()->size(),
1045 tmpcur.par(tmpcur.par()->next());
1048 // Last paragraph in selection
1049 result += selection.end.par()->asString(buffer, 0,
1050 selection.end.pos(), label);
1056 void LyXText::clearSelection() const
1058 selection.set(false);
1059 selection.mark(false);
1060 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1064 void LyXText::cursorHome(BufferView * bview) const
1066 setCursor(bview, cursor.par(), cursor.row()->pos());
1070 void LyXText::cursorEnd(BufferView * bview) const
1072 if (!cursor.row()->next()
1073 || cursor.row()->next()->par() != cursor.row()->par()) {
1074 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1076 if (cursor.par()->size() &&
1077 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1078 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1079 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1081 setCursor(bview,cursor.par(),
1082 rowLast(cursor.row()) + 1);
1088 void LyXText::cursorTop(BufferView * bview) const
1090 while (cursor.par()->previous())
1091 cursor.par(cursor.par()->previous());
1092 setCursor(bview, cursor.par(), 0);
1096 void LyXText::cursorBottom(BufferView * bview) const
1098 while (cursor.par()->next())
1099 cursor.par(cursor.par()->next());
1100 setCursor(bview, cursor.par(), cursor.par()->size());
1104 void LyXText::toggleFree(BufferView * bview,
1105 LyXFont const & font, bool toggleall)
1107 // If the mask is completely neutral, tell user
1108 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1109 // Could only happen with user style
1110 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1114 // Try implicit word selection
1115 // If there is a change in the language the implicit word selection
1117 LyXCursor resetCursor = cursor;
1118 bool implicitSelection = (font.language() == ignore_language
1119 && font.number() == LyXFont::IGNORE)
1120 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1123 setFont(bview, font, toggleall);
1125 // Implicit selections are cleared afterwards
1126 //and cursor is set to the original position.
1127 if (implicitSelection) {
1129 cursor = resetCursor;
1130 setCursor(bview, cursor.par(), cursor.pos());
1131 selection.cursor = cursor;
1134 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1138 string LyXText::getStringToIndex(BufferView * bview)
1142 // Try implicit word selection
1143 // If there is a change in the language the implicit word selection
1145 LyXCursor resetCursor = cursor;
1146 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1148 if (!selection.set()) {
1149 bview->owner()->message(_("Nothing to index!"));
1152 if (selection.start.par() != selection.end.par()) {
1153 bview->owner()->message(_("Cannot index more than one paragraph!"));
1157 idxstring = selectionAsString(bview->buffer(), false);
1159 // Implicit selections are cleared afterwards
1160 //and cursor is set to the original position.
1161 if (implicitSelection) {
1163 cursor = resetCursor;
1164 setCursor(bview, cursor.par(), cursor.pos());
1165 selection.cursor = cursor;
1171 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1172 Paragraph const * par) const
1174 if (textclasslist.Style(buf->params.textclass,
1175 par->getLayout()).labeltype != LABEL_MANUAL)
1178 return par->beginningOfMainBody();
1182 /* the DTP switches for paragraphs. LyX will store them in the
1183 * first physicla paragraph. When a paragraph is broken, the top settings
1184 * rest, the bottom settings are given to the new one. So I can make shure,
1185 * they do not duplicate themself and you cannnot make dirty things with
1188 void LyXText::setParagraph(BufferView * bview,
1189 bool line_top, bool line_bottom,
1190 bool pagebreak_top, bool pagebreak_bottom,
1191 VSpace const & space_top,
1192 VSpace const & space_bottom,
1193 Spacing const & spacing,
1195 string labelwidthstring,
1198 LyXCursor tmpcursor = cursor;
1199 if (!selection.set()) {
1200 selection.start = cursor;
1201 selection.end = cursor;
1204 // make sure that the depth behind the selection are restored, too
1205 Paragraph * endpar = selection.end.par()->next();
1206 Paragraph * undoendpar = endpar;
1208 if (endpar && endpar->getDepth()) {
1209 while (endpar && endpar->getDepth()) {
1210 endpar = endpar->next();
1211 undoendpar = endpar;
1215 // because of parindents etc.
1216 endpar = endpar->next();
1219 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1222 Paragraph * tmppar = selection.end.par();
1223 while (tmppar != selection.start.par()->previous()) {
1224 setCursor(bview, tmppar, 0);
1225 status(bview, LyXText::NEED_MORE_REFRESH);
1226 refresh_row = cursor.row();
1227 refresh_y = cursor.y() - cursor.row()->baseline();
1228 cursor.par()->params().lineTop(line_top);
1229 cursor.par()->params().lineBottom(line_bottom);
1230 cursor.par()->params().pagebreakTop(pagebreak_top);
1231 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1232 cursor.par()->params().spaceTop(space_top);
1233 cursor.par()->params().spaceBottom(space_bottom);
1234 cursor.par()->params().spacing(spacing);
1235 // does the layout allow the new alignment?
1236 if (align == LYX_ALIGN_LAYOUT)
1237 align = textclasslist
1238 .Style(bview->buffer()->params.textclass,
1239 cursor.par()->getLayout()).align;
1240 if (align & textclasslist
1241 .Style(bview->buffer()->params.textclass,
1242 cursor.par()->getLayout()).alignpossible) {
1243 if (align == textclasslist
1244 .Style(bview->buffer()->params.textclass,
1245 cursor.par()->getLayout()).align)
1246 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1248 cursor.par()->params().align(align);
1250 cursor.par()->setLabelWidthString(labelwidthstring);
1251 cursor.par()->params().noindent(noindent);
1252 tmppar = cursor.par()->previous();
1255 redoParagraphs(bview, selection.start, endpar);
1258 setCursor(bview, selection.start.par(), selection.start.pos());
1259 selection.cursor = cursor;
1260 setCursor(bview, selection.end.par(), selection.end.pos());
1261 setSelection(bview);
1262 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1264 bview->updateInset(inset_owner, true);
1268 char loweralphaCounter(int n)
1270 if (n < 1 || n > 26)
1280 char alphaCounter(int n)
1282 if (n < 1 || n > 26)
1290 char hebrewCounter(int n)
1292 static const char hebrew[22] = {
1293 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1294 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1295 '÷', 'ø', 'ù', 'ú'
1297 if (n < 1 || n > 22)
1305 string const romanCounter(int n)
1307 static char const * roman[20] = {
1308 "i", "ii", "iii", "iv", "v",
1309 "vi", "vii", "viii", "ix", "x",
1310 "xi", "xii", "xiii", "xiv", "xv",
1311 "xvi", "xvii", "xviii", "xix", "xx"
1313 if (n < 1 || n > 20)
1322 // set the counter of a paragraph. This includes the labels
1323 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1325 LyXLayout const & layout =
1326 textclasslist.Style(buf->params.textclass,
1329 LyXTextClass const & textclass =
1330 textclasslist.TextClass(buf->params.textclass);
1332 // copy the prev-counters to this one,
1333 // unless this is the first paragraph
1334 if (par->previous()) {
1335 for (int i = 0; i < 10; ++i) {
1336 par->setCounter(i, par->previous()->getFirstCounter(i));
1338 par->params().appendix(par->previous()->params().appendix());
1339 if (!par->params().appendix() && par->params().startOfAppendix()) {
1340 par->params().appendix(true);
1341 for (int i = 0; i < 10; ++i) {
1342 par->setCounter(i, 0);
1345 par->enumdepth = par->previous()->enumdepth;
1346 par->itemdepth = par->previous()->itemdepth;
1348 for (int i = 0; i < 10; ++i) {
1349 par->setCounter(i, 0);
1351 par->params().appendix(par->params().startOfAppendix());
1356 /* Maybe we have to increment the enumeration depth.
1357 * BUT, enumeration in a footnote is considered in isolation from its
1358 * surrounding paragraph so don't increment if this is the
1359 * first line of the footnote
1360 * AND, bibliographies can't have their depth changed ie. they
1361 * are always of depth 0
1364 && par->previous()->getDepth() < par->getDepth()
1365 && textclasslist.Style(buf->params.textclass,
1366 par->previous()->getLayout()
1367 ).labeltype == LABEL_COUNTER_ENUMI
1368 && par->enumdepth < 3
1369 && layout.labeltype != LABEL_BIBLIO) {
1373 // Maybe we have to decrement the enumeration depth, see note above
1375 && par->previous()->getDepth() > par->getDepth()
1376 && layout.labeltype != LABEL_BIBLIO) {
1377 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1378 par->setCounter(6 + par->enumdepth,
1379 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1380 /* reset the counters.
1381 * A depth change is like a breaking layout
1383 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1384 par->setCounter(i, 0);
1387 if (!par->params().labelString().empty()) {
1388 par->params().labelString(string());
1391 if (layout.margintype == MARGIN_MANUAL) {
1392 if (par->params().labelWidthString().empty()) {
1393 par->setLabelWidthString(layout.labelstring());
1396 par->setLabelWidthString(string());
1399 // is it a layout that has an automatic label?
1400 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1402 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1403 if (i >= 0 && i<= buf->params.secnumdepth) {
1404 par->incCounter(i); // increment the counter
1406 // Is there a label? Useful for Chapter layout
1407 if (!par->params().appendix()) {
1408 if (!layout.labelstring().empty())
1409 par->params().labelString(layout.labelstring());
1411 par->params().labelString(string());
1413 if (!layout.labelstring_appendix().empty())
1414 par->params().labelString(layout.labelstring_appendix());
1416 par->params().labelString(string());
1421 if (!par->params().appendix()) {
1422 switch (2 * LABEL_COUNTER_CHAPTER -
1423 textclass.maxcounter() + i) {
1424 case LABEL_COUNTER_CHAPTER:
1425 s << par->getCounter(i);
1427 case LABEL_COUNTER_SECTION:
1428 s << par->getCounter(i - 1) << '.'
1429 << par->getCounter(i);
1431 case LABEL_COUNTER_SUBSECTION:
1432 s << par->getCounter(i - 2) << '.'
1433 << par->getCounter(i - 1) << '.'
1434 << par->getCounter(i);
1436 case LABEL_COUNTER_SUBSUBSECTION:
1437 s << par->getCounter(i - 3) << '.'
1438 << par->getCounter(i - 2) << '.'
1439 << par->getCounter(i - 1) << '.'
1440 << par->getCounter(i);
1443 case LABEL_COUNTER_PARAGRAPH:
1444 s << par->getCounter(i - 4) << '.'
1445 << par->getCounter(i - 3) << '.'
1446 << par->getCounter(i - 2) << '.'
1447 << par->getCounter(i - 1) << '.'
1448 << par->getCounter(i);
1450 case LABEL_COUNTER_SUBPARAGRAPH:
1451 s << par->getCounter(i - 5) << '.'
1452 << par->getCounter(i - 4) << '.'
1453 << par->getCounter(i - 3) << '.'
1454 << par->getCounter(i - 2) << '.'
1455 << par->getCounter(i - 1) << '.'
1456 << par->getCounter(i);
1460 // Can this ever be reached? And in the
1461 // case it is, how can this be correct?
1463 s << par->getCounter(i) << '.';
1466 } else { // appendix
1467 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1468 case LABEL_COUNTER_CHAPTER:
1469 if (par->isRightToLeftPar(buf->params))
1470 s << hebrewCounter(par->getCounter(i));
1472 s << alphaCounter(par->getCounter(i));
1474 case LABEL_COUNTER_SECTION:
1475 if (par->isRightToLeftPar(buf->params))
1476 s << hebrewCounter(par->getCounter(i - 1));
1478 s << alphaCounter(par->getCounter(i - 1));
1481 << par->getCounter(i);
1484 case LABEL_COUNTER_SUBSECTION:
1485 if (par->isRightToLeftPar(buf->params))
1486 s << hebrewCounter(par->getCounter(i - 2));
1488 s << alphaCounter(par->getCounter(i - 2));
1491 << par->getCounter(i-1) << '.'
1492 << par->getCounter(i);
1495 case LABEL_COUNTER_SUBSUBSECTION:
1496 if (par->isRightToLeftPar(buf->params))
1497 s << hebrewCounter(par->getCounter(i-3));
1499 s << alphaCounter(par->getCounter(i-3));
1502 << par->getCounter(i-2) << '.'
1503 << par->getCounter(i-1) << '.'
1504 << par->getCounter(i);
1507 case LABEL_COUNTER_PARAGRAPH:
1508 if (par->isRightToLeftPar(buf->params))
1509 s << hebrewCounter(par->getCounter(i-4));
1511 s << alphaCounter(par->getCounter(i-4));
1514 << par->getCounter(i-3) << '.'
1515 << par->getCounter(i-2) << '.'
1516 << par->getCounter(i-1) << '.'
1517 << par->getCounter(i);
1520 case LABEL_COUNTER_SUBPARAGRAPH:
1521 if (par->isRightToLeftPar(buf->params))
1522 s << hebrewCounter(par->getCounter(i-5));
1524 s << alphaCounter(par->getCounter(i-5));
1527 << par->getCounter(i-4) << '.'
1528 << par->getCounter(i-3) << '.'
1529 << par->getCounter(i-2) << '.'
1530 << par->getCounter(i-1) << '.'
1531 << par->getCounter(i);
1535 // Can this ever be reached? And in the
1536 // case it is, how can this be correct?
1538 s << par->getCounter(i) << '.';
1544 par->params().labelString(par->params().labelString() +s.str().c_str());
1545 // We really want to remove the c_str as soon as
1548 for (i++; i < 10; ++i) {
1549 // reset the following counters
1550 par->setCounter(i, 0);
1552 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1553 for (i++; i < 10; ++i) {
1554 // reset the following counters
1555 par->setCounter(i, 0);
1557 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1558 par->incCounter(i + par->enumdepth);
1559 int number = par->getCounter(i + par->enumdepth);
1563 switch (par->enumdepth) {
1565 if (par->isRightToLeftPar(buf->params))
1567 << hebrewCounter(number)
1571 << loweralphaCounter(number)
1575 if (par->isRightToLeftPar(buf->params))
1576 s << '.' << romanCounter(number);
1578 s << romanCounter(number) << '.';
1581 if (par->isRightToLeftPar(buf->params))
1583 << alphaCounter(number);
1585 s << alphaCounter(number)
1589 if (par->isRightToLeftPar(buf->params))
1596 par->params().labelString(s.str().c_str());
1598 for (i += par->enumdepth + 1; i < 10; ++i) {
1599 // reset the following counters
1600 par->setCounter(i, 0);
1604 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1605 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1607 int number = par->getCounter(i);
1609 InsetCommandParams p( "bibitem" );
1610 par->bibkey = new InsetBibKey(p);
1612 par->bibkey->setCounter(number);
1613 par->params().labelString(layout.labelstring());
1615 // In biblio should't be following counters but...
1617 string s = layout.labelstring();
1619 // the caption hack:
1620 if (layout.labeltype == LABEL_SENSITIVE) {
1621 bool isOK (par->inInset() && par->inInset()->owner() &&
1622 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1625 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1627 = floatList.getType(tmp->type());
1628 // We should get the correct number here too.
1629 s = fl.name() + " #:";
1631 /* par->SetLayout(0);
1632 s = layout->labelstring; */
1633 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1634 ? " :úåòîùî øñç" : "Senseless: ";
1637 par->params().labelString(s);
1639 /* reset the enumeration counter. They are always resetted
1640 * when there is any other layout between */
1641 for (int i = 6 + par->enumdepth; i < 10; ++i)
1642 par->setCounter(i, 0);
1647 // Updates all counters BEHIND the row. Changed paragraphs
1648 // with a dynamic left margin will be rebroken.
1649 void LyXText::updateCounters(BufferView * bview, Row * row) const
1657 par = row->par()->next();
1661 while (row->par() != par)
1664 setCounter(bview->buffer(), par);
1666 // now check for the headline layouts. remember that they
1667 // have a dynamic left margin
1668 if ((textclasslist.Style(bview->buffer()->params.textclass,
1669 par->layout).margintype == MARGIN_DYNAMIC
1670 || textclasslist.Style(bview->buffer()->params.textclass,
1671 par->layout).labeltype == LABEL_SENSITIVE)) {
1673 // Rebreak the paragraph
1674 removeParagraph(row);
1675 appendParagraph(bview, row);
1682 void LyXText::insertInset(BufferView * bview, Inset * inset)
1684 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1686 setUndo(bview, Undo::INSERT,
1687 cursor.par(), cursor.par()->next());
1688 cursor.par()->insertInset(cursor.pos(), inset);
1689 // Just to rebreak and refresh correctly.
1690 // The character will not be inserted a second time
1691 insertChar(bview, Paragraph::META_INSET);
1693 // If we enter a highly editable inset the cursor should be to before
1694 // the inset. This couldn't happen before as Undo was not handled inside
1695 // inset now after the Undo LyX tries to call inset->Edit(...) again
1696 // and cannot do this as the cursor is behind the inset and GetInset
1697 // does not return the inset!
1698 if (isHighlyEditableInset(inset)) {
1699 cursorLeft(bview, true);
1705 void LyXText::copyEnvironmentType()
1707 copylayouttype = cursor.par()->getLayout();
1711 void LyXText::pasteEnvironmentType(BufferView * bview)
1713 setLayout(bview, copylayouttype);
1717 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1719 // Stuff what we got on the clipboard. Even if there is no selection.
1721 // There is a problem with having the stuffing here in that the
1722 // larger the selection the slower LyX will get. This can be
1723 // solved by running the line below only when the selection has
1724 // finished. The solution used currently just works, to make it
1725 // faster we need to be more clever and probably also have more
1726 // calls to stuffClipboard. (Lgb)
1727 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1729 // This doesn't make sense, if there is no selection
1730 if (!selection.set())
1733 // OK, we have a selection. This is always between selection.start
1734 // and selection.end
1736 // make sure that the depth behind the selection are restored, too
1737 Paragraph * endpar = selection.end.par()->next();
1738 Paragraph * undoendpar = endpar;
1740 if (endpar && endpar->getDepth()) {
1741 while (endpar && endpar->getDepth()) {
1742 endpar = endpar->next();
1743 undoendpar = endpar;
1745 } else if (endpar) {
1746 endpar = endpar->next(); // because of parindents etc.
1749 setUndo(bview, Undo::DELETE,
1750 selection.start.par(), undoendpar);
1752 // there are two cases: cut only within one paragraph or
1753 // more than one paragraph
1754 if (selection.start.par() == selection.end.par()) {
1755 // only within one paragraph
1756 endpar = selection.end.par();
1757 int pos = selection.end.pos();
1758 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1759 selection.start.pos(), pos,
1760 bview->buffer()->params.textclass,
1762 selection.end.pos(pos);
1764 endpar = selection.end.par();
1765 int pos = selection.end.pos();
1766 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1767 selection.start.pos(), pos,
1768 bview->buffer()->params.textclass,
1771 selection.end.par(endpar);
1772 selection.end.pos(pos);
1773 cursor.pos(selection.end.pos());
1775 endpar = endpar->next();
1777 // sometimes necessary
1779 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1781 redoParagraphs(bview, selection.start, endpar);
1783 // cutSelection can invalidate the cursor so we need to set
1785 cursor = selection.start;
1787 // need a valid cursor. (Lgb)
1790 setCursor(bview, cursor.par(), cursor.pos());
1791 selection.cursor = cursor;
1792 updateCounters(bview, cursor.row());
1796 void LyXText::copySelection(BufferView * bview)
1798 // Stuff what we got on the clipboard. Even if there is no selection.
1800 // There is a problem with having the stuffing here in that the
1801 // larger the selection the slower LyX will get. This can be
1802 // solved by running the line below only when the selection has
1803 // finished. The solution used currently just works, to make it
1804 // faster we need to be more clever and probably also have more
1805 // calls to stuffClipboard. (Lgb)
1806 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1808 // this doesnt make sense, if there is no selection
1809 if (!selection.set())
1812 // ok we have a selection. This is always between selection.start
1813 // and sel_end cursor
1815 // copy behind a space if there is one
1816 while (selection.start.par()->size() > selection.start.pos()
1817 && selection.start.par()->isLineSeparator(selection.start.pos())
1818 && (selection.start.par() != selection.end.par()
1819 || selection.start.pos() < selection.end.pos()))
1820 selection.start.pos(selection.start.pos() + 1);
1822 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1823 selection.start.pos(), selection.end.pos(),
1824 bview->buffer()->params.textclass);
1828 void LyXText::pasteSelection(BufferView * bview)
1830 // this does not make sense, if there is nothing to paste
1831 if (!CutAndPaste::checkPastePossible(cursor.par()))
1834 setUndo(bview, Undo::INSERT,
1835 cursor.par(), cursor.par()->next());
1838 Paragraph * actpar = cursor.par();
1839 int pos = cursor.pos();
1841 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1842 bview->buffer()->params.textclass);
1844 redoParagraphs(bview, cursor, endpar);
1846 setCursor(bview, cursor.par(), cursor.pos());
1849 setCursor(bview, actpar, pos);
1850 updateCounters(bview, cursor.row());
1854 // returns a pointer to the very first Paragraph
1855 Paragraph * LyXText::firstParagraph() const
1857 return ownerParagraph();
1861 // sets the selection over the number of characters of string, no check!!
1862 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1867 selection.cursor = cursor;
1868 for (string::size_type i = 0; i < str.length(); ++i)
1870 setSelection(bview);
1874 // simple replacing. The font of the first selected character is used
1875 void LyXText::replaceSelectionWithString(BufferView * bview,
1878 setCursorParUndo(bview);
1881 if (!selection.set()) { // create a dummy selection
1882 selection.end = cursor;
1883 selection.start = cursor;
1886 // Get font setting before we cut
1887 pos_type pos = selection.end.pos();
1888 LyXFont const font = selection.start.par()
1889 ->getFontSettings(bview->buffer()->params,
1890 selection.start.pos());
1892 // Insert the new string
1893 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1894 selection.end.par()->insertChar(pos, (*cit), font);
1898 // Cut the selection
1899 cutSelection(bview, true, false);
1905 // needed to insert the selection
1906 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1908 Paragraph * par = cursor.par();
1909 pos_type pos = cursor.pos();
1910 Paragraph * endpar = cursor.par()->next();
1912 setCursorParUndo(bview);
1914 // only to be sure, should not be neccessary
1917 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1919 redoParagraphs(bview, cursor, endpar);
1920 setCursor(bview, cursor.par(), cursor.pos());
1921 selection.cursor = cursor;
1922 setCursor(bview, par, pos);
1923 setSelection(bview);
1927 // turns double-CR to single CR, others where converted into one
1928 // blank. Then InsertStringAsLines is called
1929 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1931 string linestr(str);
1932 bool newline_inserted = false;
1933 for (string::size_type i = 0; i < linestr.length(); ++i) {
1934 if (linestr[i] == '\n') {
1935 if (newline_inserted) {
1936 // we know that \r will be ignored by
1937 // InsertStringA. Of course, it is a dirty
1938 // trick, but it works...
1939 linestr[i - 1] = '\r';
1943 newline_inserted = true;
1945 } else if (IsPrintable(linestr[i])) {
1946 newline_inserted = false;
1949 insertStringAsLines(bview, linestr);
1953 bool LyXText::gotoNextInset(BufferView * bview,
1954 std::vector<Inset::Code> const & codes,
1955 string const & contents) const
1957 LyXCursor res = cursor;
1960 if (res.pos() < res.par()->size() - 1) {
1961 res.pos(res.pos() + 1);
1963 res.par(res.par()->next());
1967 } while (res.par() &&
1968 !(res.par()->isInset(res.pos())
1969 && (inset = res.par()->getInset(res.pos())) != 0
1970 && find(codes.begin(), codes.end(), inset->lyxCode())
1972 && (contents.empty() ||
1973 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1977 setCursor(bview, res.par(), res.pos(), false);
1984 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1987 LyXCursor tmpcursor;
1991 Row * row = getRow(par, pos, y);
1993 // is there a break one row above
1994 if (row->previous() && row->previous()->par() == row->par()) {
1995 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1996 if (z >= row->pos()) {
1997 // set the dimensions of the row above
1998 y -= row->previous()->height();
2000 refresh_row = row->previous();
2001 status(bview, LyXText::NEED_MORE_REFRESH);
2003 breakAgain(bview, row->previous());
2005 // set the cursor again. Otherwise
2006 // dangling pointers are possible
2007 setCursor(bview, cursor.par(), cursor.pos(),
2008 false, cursor.boundary());
2009 selection.cursor = cursor;
2014 int const tmpheight = row->height();
2015 pos_type const tmplast = rowLast(row);
2019 breakAgain(bview, row);
2020 if (row->height() == tmpheight && rowLast(row) == tmplast)
2021 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2023 status(bview, LyXText::NEED_MORE_REFRESH);
2025 // check the special right address boxes
2026 if (textclasslist.Style(bview->buffer()->params.textclass,
2027 par->getLayout()).margintype
2028 == MARGIN_RIGHT_ADDRESS_BOX)
2036 redoDrawingOfParagraph(bview, tmpcursor);
2039 // set the cursor again. Otherwise dangling pointers are possible
2040 // also set the selection
2042 if (selection.set()) {
2044 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2045 false, selection.cursor.boundary());
2046 selection.cursor = cursor;
2047 setCursorIntern(bview, selection.start.par(),
2048 selection.start.pos(),
2049 false, selection.start.boundary());
2050 selection.start = cursor;
2051 setCursorIntern(bview, selection.end.par(),
2052 selection.end.pos(),
2053 false, selection.end.boundary());
2054 selection.end = cursor;
2055 setCursorIntern(bview, last_sel_cursor.par(),
2056 last_sel_cursor.pos(),
2057 false, last_sel_cursor.boundary());
2058 last_sel_cursor = cursor;
2061 setCursorIntern(bview, cursor.par(), cursor.pos(),
2062 false, cursor.boundary());
2066 // returns false if inset wasn't found
2067 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2069 // first check the current paragraph
2070 int pos = cursor.par()->getPositionOfInset(inset);
2072 checkParagraph(bview, cursor.par(), pos);
2076 // check every paragraph
2078 Paragraph * par = firstParagraph();
2080 pos = par->getPositionOfInset(inset);
2082 checkParagraph(bview, par, pos);
2092 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2094 bool setfont, bool boundary) const
2096 LyXCursor old_cursor = cursor;
2097 setCursorIntern(bview, par, pos, setfont, boundary);
2098 deleteEmptyParagraphMechanism(bview, old_cursor);
2102 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2103 pos_type pos, bool boundary) const
2107 cur.boundary(boundary);
2109 // get the cursor y position in text
2111 Row * row = getRow(par, pos, y);
2112 // y is now the beginning of the cursor row
2113 y += row->baseline();
2114 // y is now the cursor baseline
2117 // now get the cursors x position
2119 float fill_separator;
2121 float fill_label_hfill;
2122 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2124 pos_type cursor_vpos = 0;
2125 pos_type last = rowLastPrintable(row);
2127 if (pos > last + 1) // This shouldn't happen.
2129 else if (pos < row->pos())
2132 if (last < row->pos())
2133 cursor_vpos = row->pos();
2134 else if (pos > last && !boundary)
2135 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2136 ? row->pos() : last + 1;
2137 else if (pos > row->pos() &&
2138 (pos > last || boundary))
2139 /// Place cursor after char at (logical) position pos - 1
2140 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2141 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2143 /// Place cursor before char at (logical) position pos
2144 cursor_vpos = (bidi_level(pos) % 2 == 0)
2145 ? log2vis(pos) : log2vis(pos) + 1;
2147 pos_type main_body =
2148 beginningOfMainBody(bview->buffer(), row->par());
2149 if ((main_body > 0) &&
2150 ((main_body-1 > last) ||
2151 !row->par()->isLineSeparator(main_body-1)))
2154 for (pos_type vpos = row->pos();
2155 vpos < cursor_vpos; ++vpos) {
2156 pos = vis2log(vpos);
2157 if (main_body > 0 && pos == main_body - 1) {
2158 x += fill_label_hfill +
2159 lyxfont::width(textclasslist.Style(
2160 bview->buffer()->params.textclass,
2161 row->par()->getLayout())
2163 getLabelFont(bview->buffer(), row->par()));
2164 if (row->par()->isLineSeparator(main_body-1))
2165 x -= singleWidth(bview, row->par(),main_body-1);
2167 if (hfillExpansion(bview->buffer(), row, pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2172 x += fill_label_hfill;
2173 } else if (row->par()->isSeparator(pos)) {
2174 x += singleWidth(bview, row->par(), pos);
2175 if (pos >= main_body)
2176 x += fill_separator;
2178 x += singleWidth(bview, row->par(), pos);
2187 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2189 bool setfont, bool boundary) const
2191 InsetText * it = static_cast<InsetText *>(par->inInset());
2193 if (it != inset_owner) {
2194 lyxerr << "InsetText is " << it << endl;
2195 lyxerr << "inset_owner is " << inset_owner << endl;
2196 #warning I believe this code is wrong. (Lgb)
2197 #warning Jürgen, have a look at this. (Lgb)
2198 #warning Hmmm, I guess you are right but we
2199 #warning should verify when this is needed
2200 // Jürgen, would you like to have a look?
2201 // I guess we need to move the outer cursor
2202 // and open and lock the inset (bla bla bla)
2203 // stuff I don't know... so can you have a look?
2205 // I moved the lyxerr stuff in here so we can see if
2206 // this is actually really needed and where!
2208 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2214 setCursor(bview, cursor, par, pos, boundary);
2216 setCurrentFont(bview);
2220 void LyXText::setCurrentFont(BufferView * bview) const
2222 pos_type pos = cursor.pos();
2223 if (cursor.boundary() && pos > 0)
2227 if (pos == cursor.par()->size())
2229 else // potentional bug... BUG (Lgb)
2230 if (cursor.par()->isSeparator(pos)) {
2231 if (pos > cursor.row()->pos() &&
2232 bidi_level(pos) % 2 ==
2233 bidi_level(pos - 1) % 2)
2235 else if (pos + 1 < cursor.par()->size())
2241 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2242 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2244 if (cursor.pos() == cursor.par()->size() &&
2245 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2246 !cursor.boundary()) {
2247 Language const * lang =
2248 cursor.par()->getParLanguage(bview->buffer()->params);
2249 current_font.setLanguage(lang);
2250 current_font.setNumber(LyXFont::OFF);
2251 real_current_font.setLanguage(lang);
2252 real_current_font.setNumber(LyXFont::OFF);
2257 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2259 LyXCursor old_cursor = cursor;
2261 setCursorFromCoordinates(bview, cursor, x, y);
2262 setCurrentFont(bview);
2263 deleteEmptyParagraphMechanism(bview, old_cursor);
2267 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2270 // Get the row first.
2272 Row * row = getRowNearY(y);
2274 pos_type const column = getColumnNearX(bview, row, x, bound);
2275 cur.par(row->par());
2276 cur.pos(row->pos() + column);
2278 cur.y(y + row->baseline());
2280 cur.boundary(bound);
2284 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2286 if (cursor.pos() > 0) {
2287 bool boundary = cursor.boundary();
2288 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2289 if (!internal && !boundary &&
2290 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2291 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2292 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2293 Paragraph * par = cursor.par()->previous();
2294 setCursor(bview, par, par->size());
2299 void LyXText::cursorRight(BufferView * bview, bool internal) const
2301 if (!internal && cursor.boundary() &&
2302 !cursor.par()->isNewline(cursor.pos()))
2303 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2304 else if (cursor.pos() < cursor.par()->size()) {
2305 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2307 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2308 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2309 } else if (cursor.par()->next())
2310 setCursor(bview, cursor.par()->next(), 0);
2314 void LyXText::cursorUp(BufferView * bview) const
2316 setCursorFromCoordinates(bview, cursor.x_fix(),
2317 cursor.y() - cursor.row()->baseline() - 1);
2321 void LyXText::cursorDown(BufferView * bview) const
2323 setCursorFromCoordinates(bview, cursor.x_fix(),
2324 cursor.y() - cursor.row()->baseline()
2325 + cursor.row()->height() + 1);
2329 void LyXText::cursorUpParagraph(BufferView * bview) const
2331 if (cursor.pos() > 0) {
2332 setCursor(bview, cursor.par(), 0);
2334 else if (cursor.par()->previous()) {
2335 setCursor(bview, cursor.par()->previous(), 0);
2340 void LyXText::cursorDownParagraph(BufferView * bview) const
2342 if (cursor.par()->next()) {
2343 setCursor(bview, cursor.par()->next(), 0);
2345 setCursor(bview, cursor.par(), cursor.par()->size());
2349 // fix the cursor `cur' after a characters has been deleted at `where'
2350 // position. Called by deleteEmptyParagraphMechanism
2351 void LyXText::fixCursorAfterDelete(BufferView * bview,
2353 LyXCursor const & where) const
2355 // if cursor is not in the paragraph where the delete occured,
2357 if (cur.par() != where.par())
2360 // if cursor position is after the place where the delete occured,
2362 if (cur.pos() > where.pos())
2363 cur.pos(cur.pos()-1);
2365 // recompute row et al. for this cursor
2366 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2370 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2371 LyXCursor const & old_cursor) const
2373 // Would be wrong to delete anything if we have a selection.
2374 if (selection.set()) return;
2376 // We allow all kinds of "mumbo-jumbo" when freespacing.
2377 if (textclasslist.Style(bview->buffer()->params.textclass,
2378 old_cursor.par()->getLayout()).free_spacing
2379 || old_cursor.par()->isFreeSpacing())
2384 bool deleted = false;
2386 /* Ok I'll put some comments here about what is missing.
2387 I have fixed BackSpace (and thus Delete) to not delete
2388 double-spaces automagically. I have also changed Cut,
2389 Copy and Paste to hopefully do some sensible things.
2390 There are still some small problems that can lead to
2391 double spaces stored in the document file or space at
2392 the beginning of paragraphs. This happens if you have
2393 the cursor betwenn to spaces and then save. Or if you
2394 cut and paste and the selection have a space at the
2395 beginning and then save right after the paste. I am
2396 sure none of these are very hard to fix, but I will
2397 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2398 that I can get some feedback. (Lgb)
2401 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2402 // delete the LineSeparator.
2405 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2406 // delete the LineSeparator.
2409 // If the pos around the old_cursor were spaces, delete one of them.
2410 if (old_cursor.par() != cursor.par()
2411 || old_cursor.pos() != cursor.pos()) {
2412 // Only if the cursor has really moved
2414 if (old_cursor.pos() > 0
2415 && old_cursor.pos() < old_cursor.par()->size()
2416 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2417 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2418 old_cursor.par()->erase(old_cursor.pos() - 1);
2419 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2421 #ifdef WITH_WARNINGS
2422 #warning This will not work anymore when we have multiple views of the same buffer
2423 // In this case, we will have to correct also the cursors held by
2424 // other bufferviews. It will probably be easier to do that in a more
2425 // automated way in LyXCursor code. (JMarc 26/09/2001)
2427 // correct all cursors held by the LyXText
2428 fixCursorAfterDelete(bview, cursor, old_cursor);
2429 fixCursorAfterDelete(bview, selection.cursor,
2431 fixCursorAfterDelete(bview, selection.start,
2433 fixCursorAfterDelete(bview, selection.end, old_cursor);
2434 fixCursorAfterDelete(bview, last_sel_cursor,
2436 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2437 fixCursorAfterDelete(bview, toggle_end_cursor,
2443 // don't delete anything if this is the ONLY paragraph!
2444 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2447 // Do not delete empty paragraphs with keepempty set.
2448 if ((textclasslist.Style(bview->buffer()->params.textclass,
2449 old_cursor.par()->getLayout())).keepempty)
2452 // only do our magic if we changed paragraph
2453 if (old_cursor.par() == cursor.par())
2456 if ((old_cursor.par()->size() == 0
2457 || (old_cursor.par()->size() == 1
2458 && old_cursor.par()->isLineSeparator(0)))) {
2459 // ok, we will delete anything
2460 LyXCursor tmpcursor;
2462 // make sure that you do not delete any environments
2463 status(bview, LyXText::NEED_MORE_REFRESH);
2466 if (old_cursor.row()->previous()) {
2467 refresh_row = old_cursor.row()->previous();
2468 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2470 cursor = old_cursor; // that undo can restore the right cursor position
2471 Paragraph * endpar = old_cursor.par()->next();
2472 if (endpar && endpar->getDepth()) {
2473 while (endpar && endpar->getDepth()) {
2474 endpar = endpar->next();
2477 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2481 removeRow(old_cursor.row());
2482 if (ownerParagraph() == old_cursor.par()) {
2483 ownerParagraph(ownerParagraph()->next());
2486 delete old_cursor.par();
2488 /* Breakagain the next par. Needed because of
2489 * the parindent that can occur or dissappear.
2490 * The next row can change its height, if
2491 * there is another layout before */
2492 if (refresh_row->next()) {
2493 breakAgain(bview, refresh_row->next());
2494 updateCounters(bview, refresh_row);
2496 setHeightOfRow(bview, refresh_row);
2498 refresh_row = old_cursor.row()->next();
2499 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2502 cursor = old_cursor; // that undo can restore the right cursor position
2503 Paragraph * endpar = old_cursor.par()->next();
2504 if (endpar && endpar->getDepth()) {
2505 while (endpar && endpar->getDepth()) {
2506 endpar = endpar->next();
2509 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2513 removeRow(old_cursor.row());
2515 if (ownerParagraph() == old_cursor.par()) {
2516 ownerParagraph(ownerParagraph()->next());
2519 delete old_cursor.par();
2521 /* Breakagain the next par. Needed because of
2522 the parindent that can occur or dissappear.
2523 The next row can change its height, if
2524 there is another layout before */
2526 breakAgain(bview, refresh_row);
2527 updateCounters(bview, refresh_row->previous());
2532 setCursorIntern(bview, cursor.par(), cursor.pos());
2534 if (selection.cursor.par() == old_cursor.par()
2535 && selection.cursor.pos() == selection.cursor.pos()) {
2536 // correct selection
2537 selection.cursor = cursor;
2541 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2542 redoParagraphs(bview, old_cursor,
2543 old_cursor.par()->next());
2545 setCursorIntern(bview, cursor.par(), cursor.pos());
2546 selection.cursor = cursor;
2552 void LyXText::toggleAppendix(BufferView * bview)
2554 Paragraph * par = cursor.par();
2555 bool start = !par->params().startOfAppendix();
2557 // ensure that we have only one start_of_appendix in this document
2558 Paragraph * tmp = firstParagraph();
2559 for (; tmp; tmp = tmp->next()) {
2560 tmp->params().startOfAppendix(false);
2563 par->params().startOfAppendix(start);
2565 // we can set the refreshing parameters now
2566 status(bview, LyXText::NEED_MORE_REFRESH);
2568 refresh_row = 0; // not needed for full update
2569 updateCounters(bview, 0);
2570 setCursor(bview, cursor.par(), cursor.pos());
2574 Paragraph * LyXText::ownerParagraph() const
2577 return inset_owner->paragraph();
2579 return bv_owner->buffer()->paragraph;
2583 void LyXText::ownerParagraph(Paragraph * p) const
2586 inset_owner->paragraph(p);
2588 bv_owner->buffer()->paragraph = p;
2593 void LyXText::ownerParagraph(int id, Paragraph * p) const
2595 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2596 if (op && op->inInset()) {
2597 static_cast<InsetText *>(op->inInset())->paragraph(p);
2604 LyXText::text_status LyXText::status() const
2610 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2612 // well as much as I know && binds more then || so the above and the
2613 // below are identical (this for your known use of parentesis!)
2614 // Now some explanation:
2615 // We should only go up with refreshing code so this means that if
2616 // we have a MORE refresh we should never set it to LITTLE if we still
2617 // didn't handle it (and then it will be UNCHANGED. Now as long as
2618 // we stay inside one LyXText this may work but we need to tell the
2619 // outermost LyXText that it should REALLY draw us if there is some
2620 // change in a Inset::LyXText. So you see that when we are inside a
2621 // inset's LyXText we give the LITTLE to the outermost LyXText to
2622 // tell'em that it should redraw the actual row (where the inset
2623 // resides! Capito?!
2625 if ((status_ != NEED_MORE_REFRESH)
2626 || (status_ == NEED_MORE_REFRESH
2627 && st != NEED_VERY_LITTLE_REFRESH))
2630 if (inset_owner && st != UNCHANGED) {
2631 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2632 if (!bview->text->refresh_row) {
2633 bview->text->refresh_row = bview->text->cursor.row();
2634 bview->text->refresh_y = bview->text->cursor.y() -
2635 bview->text->cursor.row()->baseline();