1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
42 #include "FloatList.h"
44 #include "ParagraphParameters.h"
45 #include "support/LAssert.h"
55 LyXText::LyXText(BufferView * bv)
56 : number_of_rows(0), height(0), width(0), first(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
64 LyXText::LyXText(InsetText * inset)
65 : number_of_rows(0), height(0), width(0), first(0),
66 bv_owner(0), inset_owner(inset), the_locking_inset(0),
67 need_break_row(0), refresh_y(0), refresh_row(0),
68 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
72 void LyXText::init(BufferView * bview, bool reinit)
75 // Delete all rows, this does not touch the paragraphs!
76 Row * tmprow = firstrow;
78 tmprow = firstrow->next();
82 lastrow = refresh_row = need_break_row = 0;
83 width = height = copylayouttype = 0;
84 number_of_rows = first = refresh_y = 0;
85 status_ = LyXText::UNCHANGED;
89 Paragraph * par = ownerParagraph();
90 current_font = getFont(bview->buffer(), par, 0);
92 insertParagraph(bview, par, lastrow);
95 setCursorIntern(bview, firstrow->par(), 0);
96 selection.cursor = cursor;
102 // Delete all rows, this does not touch the paragraphs!
103 Row * tmprow = firstrow;
105 tmprow = firstrow->next();
114 LyXFont const realizeFont(LyXFont const & font,
118 LyXFont tmpfont(font);
119 Paragraph::depth_type par_depth = par->getDepth();
121 // Resolve against environment font information
122 while (par && par_depth && !tmpfont.resolved()) {
123 par = par->outerHook();
125 #ifndef INHERIT_LANGUAGE
126 tmpfont.realize(textclasslist.
127 Style(buf->params.textclass,
128 par->getLayout()).font);
130 tmpfont.realize(textclasslist.
131 Style(buf->params.textclass,
132 par->getLayout()).font,
133 buf->params.language);
135 par_depth = par->getDepth();
139 #ifndef INHERIT_LANGUAGE
140 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
143 buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout const & layout =
165 textclasslist.Style(buf->params.textclass, par->getLayout());
167 Paragraph::depth_type par_depth = par->getDepth();
168 // We specialize the 95% common case:
170 if (layout.labeltype == LABEL_MANUAL
171 && pos < beginningOfMainBody(buf, par)) {
173 LyXFont f = par->getFontSettings(buf->params,
175 #ifndef INHERIT_LANGUAGE
176 return f.realize(layout.reslabelfont);
178 return f.realize(layout.reslabelfont, buf->params.language);
181 LyXFont f = par->getFontSettings(buf->params, pos);
182 #ifndef INHERIT_LANGUAGE
183 return f.realize(layout.resfont);
185 return f.realize(layout.resfont, buf->params.language);
190 // The uncommon case need not be optimized as much
194 if (pos < beginningOfMainBody(buf, par)) {
196 layoutfont = layout.labelfont;
199 layoutfont = layout.font;
202 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
203 #ifndef INHERIT_LANGUAGE
204 tmpfont.realize(layoutfont);
206 tmpfont.realize(layoutfont, buf->params.language);
209 return realizeFont(tmpfont, buf, par);
213 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
215 LyXLayout const & layout =
216 textclasslist.Style(buf->params.textclass, par->getLayout());
218 Paragraph::depth_type par_depth = par->getDepth();
221 return layout.resfont;
224 return realizeFont(layout.font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout const & layout =
231 textclasslist.Style(buf->params.textclass, par->getLayout());
233 Paragraph::depth_type par_depth = par->getDepth();
236 return layout.reslabelfont;
239 return realizeFont(layout.labelfont, buf, par);
243 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
244 pos_type pos, LyXFont const & fnt,
247 Buffer const * buf = bv->buffer();
248 LyXFont font = getFont(buf, par, pos);
249 font.update(fnt, buf->params.language, toggleall);
250 // Let the insets convert their font
251 if (par->isInset(pos)) {
252 Inset * inset = par->getInset(pos);
253 if (isEditableInset(inset)) {
254 UpdatableInset * uinset =
255 static_cast<UpdatableInset *>(inset);
256 uinset->setFont(bv, fnt, toggleall, true);
260 LyXLayout const & layout =
261 textclasslist.Style(buf->params.textclass,
264 // Get concrete layout font to reduce against
267 if (pos < beginningOfMainBody(buf, par))
268 layoutfont = layout.labelfont;
270 layoutfont = layout.font;
272 // Realize against environment font information
273 if (par->getDepth()){
274 Paragraph * tp = par;
275 while (!layoutfont.resolved() && tp && tp->getDepth()) {
276 tp = tp->outerHook();
278 #ifndef INHERIT_LANGUAGE
279 layoutfont.realize(textclasslist.
280 Style(buf->params.textclass,
281 tp->getLayout()).font);
283 layoutfont.realize(textclasslist.
284 Style(buf->params.textclass,
285 tp->getLayout()).font,
286 buf->params.language);
291 #ifndef INHERIT_LANGUAGE
292 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
295 buf->params.language);
298 // Now, reduce font against full layout font
299 font.reduce(layoutfont);
301 par->setFont(pos, font);
305 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
306 pos_type pos, LyXFont const & fnt)
310 LyXLayout const & layout =
311 textclasslist.Style(buf->params.textclass,
314 // Get concrete layout font to reduce against
317 if (pos < beginningOfMainBody(buf, par))
318 layoutfont = layout.labelfont;
320 layoutfont = layout.font;
322 // Realize against environment font information
323 if (par->getDepth()){
324 Paragraph * tp = par;
325 while (!layoutfont.resolved() && tp && tp->getDepth()) {
326 tp = tp->outerHook();
328 #ifndef INHERIT_LANGUAGE
329 layoutfont.realize(textclasslist.
330 Style(buf->params.textclass,
331 tp->getLayout()).font);
333 layoutfont.realize(textclasslist.
334 Style(buf->params.textclass,
335 tp->getLayout()).font,
336 buf->params.language);
341 #ifndef INHERIT_LANGUAGE
342 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
345 buf->params.language);
348 // Now, reduce font against full layout font
349 font.reduce(layoutfont);
351 par->setFont(pos, font);
355 // inserts a new row behind the specified row, increments
356 // the touched counters
357 void LyXText::insertRow(Row * row, Paragraph * par,
360 Row * tmprow = new Row;
363 tmprow->next(firstrow);
366 tmprow->previous(row);
367 tmprow->next(row->next());
372 tmprow->next()->previous(tmprow);
374 if (tmprow->previous())
375 tmprow->previous()->next(tmprow);
387 // removes the row and reset the touched counters
388 void LyXText::removeRow(Row * row) const
391 row->next()->previous(row->previous());
392 if (!row->previous()) {
393 firstrow = row->next();
395 row->previous()->next(row->next());
398 lastrow = row->previous();
400 height -= row->height(); // the text becomes smaller
403 --number_of_rows; // one row less
407 // remove all following rows of the paragraph of the specified row.
408 void LyXText::removeParagraph(Row * row) const
410 Paragraph * tmppar = row->par();
414 while (row && row->par() == tmppar) {
415 tmprow = row->next();
422 // insert the specified paragraph behind the specified row
423 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
426 insertRow(row, par, 0); /* insert a new row, starting
429 setCounter(bview->buffer(), par); // set the counters
431 // and now append the whole paragraph behind the new row
434 appendParagraph(bview, firstrow);
436 row->next()->height(0);
437 appendParagraph(bview, row->next());
442 Inset * LyXText::getInset() const
445 if (cursor.pos() == 0 && cursor.par()->bibkey) {
446 inset = cursor.par()->bibkey;
447 } else if (cursor.pos() < cursor.par()->size()
448 && cursor.par()->isInset(cursor.pos())) {
449 inset = cursor.par()->getInset(cursor.pos());
455 void LyXText::toggleInset(BufferView * bview)
457 Inset * inset = getInset();
458 if (!isEditableInset(inset))
460 //bview->owner()->message(inset->editMessage());
462 // do we want to keep this?? (JMarc)
463 if (!isHighlyEditableInset(inset))
464 setCursorParUndo(bview);
466 if (inset->isOpen()) {
472 inset->open(bview, !inset->isOpen());
477 /* used in setlayout */
478 // Asger is not sure we want to do this...
479 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
482 LyXLayout const & layout =
483 textclasslist.Style(buf->params.textclass, par->getLayout());
486 for (pos_type pos = 0; pos < par->size(); ++pos) {
487 if (pos < beginningOfMainBody(buf, par))
488 layoutfont = layout.labelfont;
490 layoutfont = layout.font;
492 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
493 tmpfont.reduce(layoutfont);
494 par->setFont(pos, tmpfont);
499 Paragraph * LyXText::setLayout(BufferView * bview,
500 LyXCursor & cur, LyXCursor & sstart_cur,
501 LyXCursor & send_cur,
502 lyx::layout_type layout)
504 Paragraph * endpar = send_cur.par()->next();
505 Paragraph * undoendpar = endpar;
507 if (endpar && endpar->getDepth()) {
508 while (endpar && endpar->getDepth()) {
509 endpar = endpar->next();
513 endpar = endpar->next(); // because of parindents etc.
516 setUndo(bview, Undo::EDIT,
517 sstart_cur.par(), undoendpar);
519 // ok we have a selection. This is always between sstart_cur
520 // and sel_end cursor
523 LyXLayout const & lyxlayout =
524 textclasslist.Style(bview->buffer()->params.textclass, layout);
526 while (cur.par() != send_cur.par()) {
527 cur.par()->setLayout(layout);
528 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
529 Paragraph * fppar = cur.par();
530 fppar->params().spaceTop(lyxlayout.fill_top ?
531 VSpace(VSpace::VFILL)
532 : VSpace(VSpace::NONE));
533 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
534 VSpace(VSpace::VFILL)
535 : VSpace(VSpace::NONE));
536 if (lyxlayout.margintype == MARGIN_MANUAL)
537 cur.par()->setLabelWidthString(lyxlayout.labelstring());
538 if (lyxlayout.labeltype != LABEL_BIBLIO
540 delete fppar->bibkey;
543 cur.par(cur.par()->next());
545 cur.par()->setLayout(layout);
546 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
547 Paragraph * fppar = cur.par();
548 fppar->params().spaceTop(lyxlayout.fill_top ?
549 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
550 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
551 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
552 if (lyxlayout.margintype == MARGIN_MANUAL)
553 cur.par()->setLabelWidthString(lyxlayout.labelstring());
554 if (lyxlayout.labeltype != LABEL_BIBLIO
556 delete fppar->bibkey;
563 // set layout over selection and make a total rebreak of those paragraphs
564 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
566 LyXCursor tmpcursor = cursor; /* store the current cursor */
568 // if there is no selection just set the layout
569 // of the current paragraph */
570 if (!selection.set()) {
571 selection.start = cursor; // dummy selection
572 selection.end = cursor;
574 Paragraph * endpar = setLayout(bview, cursor, selection.start,
575 selection.end, layout);
576 redoParagraphs(bview, selection.start, endpar);
578 // we have to reset the selection, because the
579 // geometry could have changed
580 setCursor(bview, selection.start.par(),
581 selection.start.pos(), false);
582 selection.cursor = cursor;
583 setCursor(bview, selection.end.par(), selection.end.pos(), false);
584 updateCounters(bview, cursor.row());
587 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
591 // increment depth over selection and
592 // make a total rebreak of those paragraphs
593 void LyXText::incDepth(BufferView * bview)
595 // If there is no selection, just use the current paragraph
596 if (!selection.set()) {
597 selection.start = cursor; // dummy selection
598 selection.end = cursor;
601 // We end at the next paragraph with depth 0
602 Paragraph * endpar = selection.end.par()->next();
604 Paragraph * undoendpar = endpar;
606 if (endpar && endpar->getDepth()) {
607 while (endpar && endpar->getDepth()) {
608 endpar = endpar->next();
612 endpar = endpar->next(); // because of parindents etc.
615 setUndo(bview, Undo::EDIT,
616 selection.start.par(), undoendpar);
618 LyXCursor tmpcursor = cursor; // store the current cursor
620 // ok we have a selection. This is always between sel_start_cursor
621 // and sel_end cursor
622 cursor = selection.start;
624 bool anything_changed = false;
627 // NOTE: you can't change the depth of a bibliography entry
629 textclasslist.Style(bview->buffer()->params.textclass,
630 cursor.par()->getLayout()
631 ).labeltype != LABEL_BIBLIO) {
632 Paragraph * prev = cursor.par()->previous();
635 && (prev->getDepth() - cursor.par()->getDepth() > 0
636 || (prev->getDepth() == cursor.par()->getDepth()
637 && textclasslist.Style(bview->buffer()->params.textclass,
638 prev->getLayout()).isEnvironment()))) {
639 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
640 anything_changed = true;
643 if (cursor.par() == selection.end.par())
645 cursor.par(cursor.par()->next());
648 // if nothing changed set all depth to 0
649 if (!anything_changed) {
650 cursor = selection.start;
651 while (cursor.par() != selection.end.par()) {
652 cursor.par()->params().depth(0);
653 cursor.par(cursor.par()->next());
655 cursor.par()->params().depth(0);
658 redoParagraphs(bview, selection.start, endpar);
660 // we have to reset the selection, because the
661 // geometry could have changed
662 setCursor(bview, selection.start.par(), selection.start.pos());
663 selection.cursor = cursor;
664 setCursor(bview, selection.end.par(), selection.end.pos());
665 updateCounters(bview, cursor.row());
668 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
672 // decrement depth over selection and
673 // make a total rebreak of those paragraphs
674 void LyXText::decDepth(BufferView * bview)
676 // if there is no selection just set the layout
677 // of the current paragraph
678 if (!selection.set()) {
679 selection.start = cursor; // dummy selection
680 selection.end = cursor;
682 Paragraph * endpar = selection.end.par()->next();
683 Paragraph * undoendpar = endpar;
685 if (endpar && endpar->getDepth()) {
686 while (endpar && endpar->getDepth()) {
687 endpar = endpar->next();
691 endpar = endpar->next(); // because of parindents etc.
694 setUndo(bview, Undo::EDIT,
695 selection.start.par(), undoendpar);
697 LyXCursor tmpcursor = cursor; // store the current cursor
699 // ok we have a selection. This is always between sel_start_cursor
700 // and sel_end cursor
701 cursor = selection.start;
704 if (cursor.par()->params().depth()) {
705 cursor.par()->params()
706 .depth(cursor.par()->params().depth() - 1);
708 if (cursor.par() == selection.end.par()) {
711 cursor.par(cursor.par()->next());
714 redoParagraphs(bview, selection.start, endpar);
716 // we have to reset the selection, because the
717 // geometry could have changed
718 setCursor(bview, selection.start.par(),
719 selection.start.pos());
720 selection.cursor = cursor;
721 setCursor(bview, selection.end.par(), selection.end.pos());
722 updateCounters(bview, cursor.row());
725 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
729 // set font over selection and make a total rebreak of those paragraphs
730 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
732 // if there is no selection just set the current_font
733 if (!selection.set()) {
734 // Determine basis font
736 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
738 layoutfont = getLabelFont(bview->buffer(),
741 layoutfont = getLayoutFont(bview->buffer(),
744 // Update current font
745 real_current_font.update(font,
746 bview->buffer()->params.language,
749 // Reduce to implicit settings
750 current_font = real_current_font;
751 current_font.reduce(layoutfont);
752 // And resolve it completely
753 #ifndef INHERIT_LANGUAGE
754 real_current_font.realize(layoutfont);
756 real_current_font.realize(layoutfont,
757 bview->buffer()->params.language);
762 LyXCursor tmpcursor = cursor; // store the current cursor
764 // ok we have a selection. This is always between sel_start_cursor
765 // and sel_end cursor
767 setUndo(bview, Undo::EDIT,
768 selection.start.par(), selection.end.par()->next());
770 cursor = selection.start;
771 while (cursor.par() != selection.end.par() ||
772 (cursor.pos() < selection.end.pos()))
774 if (cursor.pos() < cursor.par()->size()) {
775 // an open footnote should behave
777 setCharFont(bview, cursor.par(), cursor.pos(),
779 cursor.pos(cursor.pos() + 1);
782 cursor.par(cursor.par()->next());
787 redoParagraphs(bview, selection.start, selection.end.par()->next());
789 // we have to reset the selection, because the
790 // geometry could have changed
791 setCursor(bview, selection.start.par(), selection.start.pos());
792 selection.cursor = cursor;
793 setCursor(bview, selection.end.par(), selection.end.pos());
796 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
797 tmpcursor.boundary());
801 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
803 Row * tmprow = cur.row();
804 int y = cur.y() - tmprow->baseline();
806 setHeightOfRow(bview, tmprow);
808 while (tmprow->previous()
809 && tmprow->previous()->par() == tmprow->par()) {
810 tmprow = tmprow->previous();
811 y -= tmprow->height();
812 setHeightOfRow(bview, tmprow);
815 // we can set the refreshing parameters now
816 status(bview, LyXText::NEED_MORE_REFRESH);
818 refresh_row = tmprow;
819 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
823 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
825 Row * tmprow = cur.row();
827 int y = cur.y() - tmprow->baseline();
828 setHeightOfRow(bview, tmprow);
830 while (tmprow->previous()
831 && tmprow->previous()->par() == tmprow->par()) {
832 tmprow = tmprow->previous();
833 y -= tmprow->height();
836 // we can set the refreshing parameters now
837 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
839 refresh_row = tmprow;
841 status(bview, LyXText::NEED_MORE_REFRESH);
842 setCursor(bview, cur.par(), cur.pos());
846 // deletes and inserts again all paragaphs between the cursor
847 // and the specified par
848 // This function is needed after SetLayout and SetFont etc.
849 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
850 Paragraph const * endpar) const
853 Paragraph * tmppar = 0;
854 Paragraph * first_phys_par = 0;
856 Row * tmprow = cur.row();
858 int y = cur.y() - tmprow->baseline();
860 if (!tmprow->previous()) {
861 // a trick/hack for UNDO
862 // Can somebody please tell me _why_ this solves
864 first_phys_par = firstParagraph();
866 first_phys_par = tmprow->par();
867 while (tmprow->previous()
868 && tmprow->previous()->par() == first_phys_par)
870 tmprow = tmprow->previous();
871 y -= tmprow->height();
875 // we can set the refreshing parameters now
876 status(bview, LyXText::NEED_MORE_REFRESH);
878 refresh_row = tmprow->previous(); /* the real refresh row will
879 be deleted, so I store
883 tmppar = tmprow->next()->par();
886 while (tmppar != endpar) {
887 removeRow(tmprow->next());
889 tmppar = tmprow->next()->par();
894 // remove the first one
895 tmprow2 = tmprow; /* this is because tmprow->previous()
897 tmprow = tmprow->previous();
900 tmppar = first_phys_par;
904 insertParagraph(bview, tmppar, tmprow);
908 while (tmprow->next()
909 && tmprow->next()->par() == tmppar) {
910 tmprow = tmprow->next();
912 tmppar = tmppar->next();
914 } while (tmppar && tmppar != endpar);
916 // this is because of layout changes
918 refresh_y -= refresh_row->height();
919 setHeightOfRow(bview, refresh_row);
921 refresh_row = firstrow;
923 setHeightOfRow(bview, refresh_row);
926 if (tmprow && tmprow->next())
927 setHeightOfRow(bview, tmprow->next());
931 bool LyXText::fullRebreak(BufferView * bview)
937 if (need_break_row) {
938 breakAgain(bview, need_break_row);
946 // important for the screen
949 /* the cursor set functions have a special mechanism. When they
950 * realize, that you left an empty paragraph, they will delete it.
951 * They also delete the corresponding row */
953 // need the selection cursor:
954 void LyXText::setSelection(BufferView * bview)
956 bool const lsel = selection.set();
958 if (!selection.set()) {
959 last_sel_cursor = selection.cursor;
960 selection.start = selection.cursor;
961 selection.end = selection.cursor;
966 // first the toggling area
967 if (cursor.y() < last_sel_cursor.y()
968 || (cursor.y() == last_sel_cursor.y()
969 && cursor.x() < last_sel_cursor.x())) {
970 toggle_end_cursor = last_sel_cursor;
971 toggle_cursor = cursor;
973 toggle_end_cursor = cursor;
974 toggle_cursor = last_sel_cursor;
977 last_sel_cursor = cursor;
979 // and now the whole selection
981 if (selection.cursor.par() == cursor.par())
982 if (selection.cursor.pos() < cursor.pos()) {
983 selection.end = cursor;
984 selection.start = selection.cursor;
986 selection.end = selection.cursor;
987 selection.start = cursor;
989 else if (selection.cursor.y() < cursor.y() ||
990 (selection.cursor.y() == cursor.y()
991 && selection.cursor.x() < cursor.x())) {
992 selection.end = cursor;
993 selection.start = selection.cursor;
996 selection.end = selection.cursor;
997 selection.start = cursor;
1000 // a selection with no contents is not a selection
1001 if (selection.start.par() == selection.end.par() &&
1002 selection.start.pos() == selection.end.pos())
1003 selection.set(false);
1005 if (inset_owner && (selection.set() || lsel))
1006 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1010 string const LyXText::selectionAsString(Buffer const * buffer,
1013 if (!selection.set()) return string();
1016 // Special handling if the whole selection is within one paragraph
1017 if (selection.start.par() == selection.end.par()) {
1018 result += selection.start.par()->asString(buffer,
1019 selection.start.pos(),
1020 selection.end.pos(),
1025 // The selection spans more than one paragraph
1027 // First paragraph in selection
1028 result += selection.start.par()->asString(buffer,
1029 selection.start.pos(),
1030 selection.start.par()->size(),
1034 // The paragraphs in between (if any)
1035 LyXCursor tmpcur(selection.start);
1036 tmpcur.par(tmpcur.par()->next());
1037 while (tmpcur.par() != selection.end.par()) {
1038 result += tmpcur.par()->asString(buffer, 0,
1039 tmpcur.par()->size(),
1041 tmpcur.par(tmpcur.par()->next());
1044 // Last paragraph in selection
1045 result += selection.end.par()->asString(buffer, 0,
1046 selection.end.pos(), label);
1052 void LyXText::clearSelection() const
1054 selection.set(false);
1055 selection.mark(false);
1056 selection.end = selection.start = selection.cursor = cursor;
1060 void LyXText::cursorHome(BufferView * bview) const
1062 setCursor(bview, cursor.par(), cursor.row()->pos());
1066 void LyXText::cursorEnd(BufferView * bview) const
1068 if (!cursor.row()->next()
1069 || cursor.row()->next()->par() != cursor.row()->par()) {
1070 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1072 if (cursor.par()->size() &&
1073 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1074 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1075 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1077 setCursor(bview,cursor.par(),
1078 rowLast(cursor.row()) + 1);
1084 void LyXText::cursorTop(BufferView * bview) const
1086 while (cursor.par()->previous())
1087 cursor.par(cursor.par()->previous());
1088 setCursor(bview, cursor.par(), 0);
1092 void LyXText::cursorBottom(BufferView * bview) const
1094 while (cursor.par()->next())
1095 cursor.par(cursor.par()->next());
1096 setCursor(bview, cursor.par(), cursor.par()->size());
1100 void LyXText::toggleFree(BufferView * bview,
1101 LyXFont const & font, bool toggleall)
1103 // If the mask is completely neutral, tell user
1104 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1105 // Could only happen with user style
1106 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1110 // Try implicit word selection
1111 // If there is a change in the language the implicit word selection
1113 LyXCursor resetCursor = cursor;
1114 bool implicitSelection = (font.language() == ignore_language
1115 && font.number() == LyXFont::IGNORE)
1116 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1119 setFont(bview, font, toggleall);
1121 // Implicit selections are cleared afterwards
1122 //and cursor is set to the original position.
1123 if (implicitSelection) {
1125 cursor = resetCursor;
1126 setCursor(bview, cursor.par(), cursor.pos());
1127 selection.cursor = cursor;
1130 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1134 string LyXText::getStringToIndex(BufferView * bview)
1138 // Try implicit word selection
1139 // If there is a change in the language the implicit word selection
1141 LyXCursor resetCursor = cursor;
1142 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1144 if (!selection.set()) {
1145 bview->owner()->message(_("Nothing to index!"));
1148 if (selection.start.par() != selection.end.par()) {
1149 bview->owner()->message(_("Cannot index more than one paragraph!"));
1153 idxstring = selectionAsString(bview->buffer(), false);
1155 // Implicit selections are cleared afterwards
1156 //and cursor is set to the original position.
1157 if (implicitSelection) {
1159 cursor = resetCursor;
1160 setCursor(bview, cursor.par(), cursor.pos());
1161 selection.cursor = cursor;
1167 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1168 Paragraph const * par) const
1170 if (textclasslist.Style(buf->params.textclass,
1171 par->getLayout()).labeltype != LABEL_MANUAL)
1174 return par->beginningOfMainBody();
1178 /* the DTP switches for paragraphs. LyX will store them in the
1179 * first physicla paragraph. When a paragraph is broken, the top settings
1180 * rest, the bottom settings are given to the new one. So I can make shure,
1181 * they do not duplicate themself and you cannnot make dirty things with
1184 void LyXText::setParagraph(BufferView * bview,
1185 bool line_top, bool line_bottom,
1186 bool pagebreak_top, bool pagebreak_bottom,
1187 VSpace const & space_top,
1188 VSpace const & space_bottom,
1189 Spacing const & spacing,
1191 string labelwidthstring,
1194 LyXCursor tmpcursor = cursor;
1195 if (!selection.set()) {
1196 selection.start = cursor;
1197 selection.end = cursor;
1200 // make sure that the depth behind the selection are restored, too
1201 Paragraph * endpar = selection.end.par()->next();
1202 Paragraph * undoendpar = endpar;
1204 if (endpar && endpar->getDepth()) {
1205 while (endpar && endpar->getDepth()) {
1206 endpar = endpar->next();
1207 undoendpar = endpar;
1211 // because of parindents etc.
1212 endpar = endpar->next();
1215 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1218 Paragraph * tmppar = selection.end.par();
1219 while (tmppar != selection.start.par()->previous()) {
1220 setCursor(bview, tmppar, 0);
1221 status(bview, LyXText::NEED_MORE_REFRESH);
1222 refresh_row = cursor.row();
1223 refresh_y = cursor.y() - cursor.row()->baseline();
1224 cursor.par()->params().lineTop(line_top);
1225 cursor.par()->params().lineBottom(line_bottom);
1226 cursor.par()->params().pagebreakTop(pagebreak_top);
1227 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1228 cursor.par()->params().spaceTop(space_top);
1229 cursor.par()->params().spaceBottom(space_bottom);
1230 cursor.par()->params().spacing(spacing);
1231 // does the layout allow the new alignment?
1232 if (align == LYX_ALIGN_LAYOUT)
1233 align = textclasslist
1234 .Style(bview->buffer()->params.textclass,
1235 cursor.par()->getLayout()).align;
1236 if (align & textclasslist
1237 .Style(bview->buffer()->params.textclass,
1238 cursor.par()->getLayout()).alignpossible) {
1239 if (align == textclasslist
1240 .Style(bview->buffer()->params.textclass,
1241 cursor.par()->getLayout()).align)
1242 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1244 cursor.par()->params().align(align);
1246 cursor.par()->setLabelWidthString(labelwidthstring);
1247 cursor.par()->params().noindent(noindent);
1248 tmppar = cursor.par()->previous();
1251 redoParagraphs(bview, selection.start, endpar);
1254 setCursor(bview, selection.start.par(), selection.start.pos());
1255 selection.cursor = cursor;
1256 setCursor(bview, selection.end.par(), selection.end.pos());
1257 setSelection(bview);
1258 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1260 bview->updateInset(inset_owner, true);
1264 char loweralphaCounter(int n)
1266 if (n < 1 || n > 26)
1276 char alphaCounter(int n)
1278 if (n < 1 || n > 26)
1286 char hebrewCounter(int n)
1288 static const char hebrew[22] = {
1289 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1290 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1291 '÷', 'ø', 'ù', 'ú'
1293 if (n < 1 || n > 22)
1301 string const romanCounter(int n)
1303 static char const * roman[20] = {
1304 "i", "ii", "iii", "iv", "v",
1305 "vi", "vii", "viii", "ix", "x",
1306 "xi", "xii", "xiii", "xiv", "xv",
1307 "xvi", "xvii", "xviii", "xix", "xx"
1309 if (n < 1 || n > 20)
1318 // set the counter of a paragraph. This includes the labels
1319 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1321 LyXLayout const & layout =
1322 textclasslist.Style(buf->params.textclass,
1325 LyXTextClass const & textclass =
1326 textclasslist.TextClass(buf->params.textclass);
1328 // copy the prev-counters to this one,
1329 // unless this is the first paragraph
1330 if (par->previous()) {
1331 for (int i = 0; i < 10; ++i) {
1332 par->setCounter(i, par->previous()->getFirstCounter(i));
1334 par->params().appendix(par->previous()->params().appendix());
1335 if (!par->params().appendix() && par->params().startOfAppendix()) {
1336 par->params().appendix(true);
1337 for (int i = 0; i < 10; ++i) {
1338 par->setCounter(i, 0);
1341 par->enumdepth = par->previous()->enumdepth;
1342 par->itemdepth = par->previous()->itemdepth;
1344 for (int i = 0; i < 10; ++i) {
1345 par->setCounter(i, 0);
1347 par->params().appendix(par->params().startOfAppendix());
1352 /* Maybe we have to increment the enumeration depth.
1353 * BUT, enumeration in a footnote is considered in isolation from its
1354 * surrounding paragraph so don't increment if this is the
1355 * first line of the footnote
1356 * AND, bibliographies can't have their depth changed ie. they
1357 * are always of depth 0
1360 && par->previous()->getDepth() < par->getDepth()
1361 && textclasslist.Style(buf->params.textclass,
1362 par->previous()->getLayout()
1363 ).labeltype == LABEL_COUNTER_ENUMI
1364 && par->enumdepth < 3
1365 && layout.labeltype != LABEL_BIBLIO) {
1369 // Maybe we have to decrement the enumeration depth, see note above
1371 && par->previous()->getDepth() > par->getDepth()
1372 && layout.labeltype != LABEL_BIBLIO) {
1373 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1374 par->setCounter(6 + par->enumdepth,
1375 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1376 /* reset the counters.
1377 * A depth change is like a breaking layout
1379 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1380 par->setCounter(i, 0);
1383 if (!par->params().labelString().empty()) {
1384 par->params().labelString(string());
1387 if (layout.margintype == MARGIN_MANUAL) {
1388 if (par->params().labelWidthString().empty()) {
1389 par->setLabelWidthString(layout.labelstring());
1392 par->setLabelWidthString(string());
1395 // is it a layout that has an automatic label?
1396 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1398 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1399 if (i >= 0 && i<= buf->params.secnumdepth) {
1400 par->incCounter(i); // increment the counter
1402 // Is there a label? Useful for Chapter layout
1403 if (!par->params().appendix()) {
1404 if (!layout.labelstring().empty())
1405 par->params().labelString(layout.labelstring());
1407 par->params().labelString(string());
1409 if (!layout.labelstring_appendix().empty())
1410 par->params().labelString(layout.labelstring_appendix());
1412 par->params().labelString(string());
1417 if (!par->params().appendix()) {
1418 switch (2 * LABEL_COUNTER_CHAPTER -
1419 textclass.maxcounter() + i) {
1420 case LABEL_COUNTER_CHAPTER:
1421 s << par->getCounter(i);
1423 case LABEL_COUNTER_SECTION:
1424 s << par->getCounter(i - 1) << '.'
1425 << par->getCounter(i);
1427 case LABEL_COUNTER_SUBSECTION:
1428 s << par->getCounter(i - 2) << '.'
1429 << par->getCounter(i - 1) << '.'
1430 << par->getCounter(i);
1432 case LABEL_COUNTER_SUBSUBSECTION:
1433 s << par->getCounter(i - 3) << '.'
1434 << par->getCounter(i - 2) << '.'
1435 << par->getCounter(i - 1) << '.'
1436 << par->getCounter(i);
1439 case LABEL_COUNTER_PARAGRAPH:
1440 s << par->getCounter(i - 4) << '.'
1441 << par->getCounter(i - 3) << '.'
1442 << par->getCounter(i - 2) << '.'
1443 << par->getCounter(i - 1) << '.'
1444 << par->getCounter(i);
1446 case LABEL_COUNTER_SUBPARAGRAPH:
1447 s << par->getCounter(i - 5) << '.'
1448 << par->getCounter(i - 4) << '.'
1449 << par->getCounter(i - 3) << '.'
1450 << par->getCounter(i - 2) << '.'
1451 << par->getCounter(i - 1) << '.'
1452 << par->getCounter(i);
1456 // Can this ever be reached? And in the
1457 // case it is, how can this be correct?
1459 s << par->getCounter(i) << '.';
1462 } else { // appendix
1463 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1464 case LABEL_COUNTER_CHAPTER:
1465 if (par->isRightToLeftPar(buf->params))
1466 s << hebrewCounter(par->getCounter(i));
1468 s << alphaCounter(par->getCounter(i));
1470 case LABEL_COUNTER_SECTION:
1471 if (par->isRightToLeftPar(buf->params))
1472 s << hebrewCounter(par->getCounter(i - 1));
1474 s << alphaCounter(par->getCounter(i - 1));
1477 << par->getCounter(i);
1480 case LABEL_COUNTER_SUBSECTION:
1481 if (par->isRightToLeftPar(buf->params))
1482 s << hebrewCounter(par->getCounter(i - 2));
1484 s << alphaCounter(par->getCounter(i - 2));
1487 << par->getCounter(i-1) << '.'
1488 << par->getCounter(i);
1491 case LABEL_COUNTER_SUBSUBSECTION:
1492 if (par->isRightToLeftPar(buf->params))
1493 s << hebrewCounter(par->getCounter(i-3));
1495 s << alphaCounter(par->getCounter(i-3));
1498 << par->getCounter(i-2) << '.'
1499 << par->getCounter(i-1) << '.'
1500 << par->getCounter(i);
1503 case LABEL_COUNTER_PARAGRAPH:
1504 if (par->isRightToLeftPar(buf->params))
1505 s << hebrewCounter(par->getCounter(i-4));
1507 s << alphaCounter(par->getCounter(i-4));
1510 << par->getCounter(i-3) << '.'
1511 << par->getCounter(i-2) << '.'
1512 << par->getCounter(i-1) << '.'
1513 << par->getCounter(i);
1516 case LABEL_COUNTER_SUBPARAGRAPH:
1517 if (par->isRightToLeftPar(buf->params))
1518 s << hebrewCounter(par->getCounter(i-5));
1520 s << alphaCounter(par->getCounter(i-5));
1523 << par->getCounter(i-4) << '.'
1524 << par->getCounter(i-3) << '.'
1525 << par->getCounter(i-2) << '.'
1526 << par->getCounter(i-1) << '.'
1527 << par->getCounter(i);
1531 // Can this ever be reached? And in the
1532 // case it is, how can this be correct?
1534 s << par->getCounter(i) << '.';
1540 par->params().labelString(par->params().labelString() +s.str().c_str());
1541 // We really want to remove the c_str as soon as
1544 for (i++; i < 10; ++i) {
1545 // reset the following counters
1546 par->setCounter(i, 0);
1548 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1549 for (i++; i < 10; ++i) {
1550 // reset the following counters
1551 par->setCounter(i, 0);
1553 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1554 par->incCounter(i + par->enumdepth);
1555 int number = par->getCounter(i + par->enumdepth);
1559 switch (par->enumdepth) {
1561 if (par->isRightToLeftPar(buf->params))
1563 << hebrewCounter(number)
1567 << loweralphaCounter(number)
1571 if (par->isRightToLeftPar(buf->params))
1572 s << '.' << romanCounter(number);
1574 s << romanCounter(number) << '.';
1577 if (par->isRightToLeftPar(buf->params))
1579 << alphaCounter(number);
1581 s << alphaCounter(number)
1585 if (par->isRightToLeftPar(buf->params))
1592 par->params().labelString(s.str().c_str());
1594 for (i += par->enumdepth + 1; i < 10; ++i) {
1595 // reset the following counters
1596 par->setCounter(i, 0);
1600 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1601 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1603 int number = par->getCounter(i);
1605 InsetCommandParams p( "bibitem" );
1606 par->bibkey = new InsetBibKey(p);
1608 par->bibkey->setCounter(number);
1609 par->params().labelString(layout.labelstring());
1611 // In biblio should't be following counters but...
1613 string s = layout.labelstring();
1615 // the caption hack:
1616 if (layout.labeltype == LABEL_SENSITIVE) {
1617 bool isOK (par->inInset() && par->inInset()->owner() &&
1618 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1621 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1623 = floatList.getType(tmp->type());
1624 // We should get the correct number here too.
1625 s = fl.name() + " #:";
1627 /* par->SetLayout(0);
1628 s = layout->labelstring; */
1629 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1630 ? " :úåòîùî øñç" : "Senseless: ";
1633 par->params().labelString(s);
1635 /* reset the enumeration counter. They are always resetted
1636 * when there is any other layout between */
1637 for (int i = 6 + par->enumdepth; i < 10; ++i)
1638 par->setCounter(i, 0);
1643 // Updates all counters BEHIND the row. Changed paragraphs
1644 // with a dynamic left margin will be rebroken.
1645 void LyXText::updateCounters(BufferView * bview, Row * row) const
1653 par = row->par()->next();
1657 while (row->par() != par)
1660 setCounter(bview->buffer(), par);
1662 // now check for the headline layouts. remember that they
1663 // have a dynamic left margin
1664 if ((textclasslist.Style(bview->buffer()->params.textclass,
1665 par->layout).margintype == MARGIN_DYNAMIC
1666 || textclasslist.Style(bview->buffer()->params.textclass,
1667 par->layout).labeltype == LABEL_SENSITIVE)) {
1669 // Rebreak the paragraph
1670 removeParagraph(row);
1671 appendParagraph(bview, row);
1678 void LyXText::insertInset(BufferView * bview, Inset * inset)
1680 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1682 setUndo(bview, Undo::INSERT,
1683 cursor.par(), cursor.par()->next());
1684 cursor.par()->insertInset(cursor.pos(), inset);
1685 // Just to rebreak and refresh correctly.
1686 // The character will not be inserted a second time
1687 insertChar(bview, Paragraph::META_INSET);
1689 // If we enter a highly editable inset the cursor should be to before
1690 // the inset. This couldn't happen before as Undo was not handled inside
1691 // inset now after the Undo LyX tries to call inset->Edit(...) again
1692 // and cannot do this as the cursor is behind the inset and GetInset
1693 // does not return the inset!
1694 if (isHighlyEditableInset(inset)) {
1695 cursorLeft(bview, true);
1701 void LyXText::copyEnvironmentType()
1703 copylayouttype = cursor.par()->getLayout();
1707 void LyXText::pasteEnvironmentType(BufferView * bview)
1709 setLayout(bview, copylayouttype);
1713 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1715 // Stuff what we got on the clipboard. Even if there is no selection.
1717 // There is a problem with having the stuffing here in that the
1718 // larger the selection the slower LyX will get. This can be
1719 // solved by running the line below only when the selection has
1720 // finished. The solution used currently just works, to make it
1721 // faster we need to be more clever and probably also have more
1722 // calls to stuffClipboard. (Lgb)
1723 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1725 // This doesn't make sense, if there is no selection
1726 if (!selection.set())
1729 // OK, we have a selection. This is always between selection.start
1730 // and selection.end
1732 // make sure that the depth behind the selection are restored, too
1733 Paragraph * endpar = selection.end.par()->next();
1734 Paragraph * undoendpar = endpar;
1736 if (endpar && endpar->getDepth()) {
1737 while (endpar && endpar->getDepth()) {
1738 endpar = endpar->next();
1739 undoendpar = endpar;
1741 } else if (endpar) {
1742 endpar = endpar->next(); // because of parindents etc.
1745 setUndo(bview, Undo::DELETE,
1746 selection.start.par(), undoendpar);
1748 // there are two cases: cut only within one paragraph or
1749 // more than one paragraph
1750 if (selection.start.par() == selection.end.par()) {
1751 // only within one paragraph
1752 endpar = selection.end.par();
1753 int pos = selection.end.pos();
1754 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1755 selection.start.pos(), pos,
1756 bview->buffer()->params.textclass,
1758 selection.end.pos(pos);
1760 endpar = selection.end.par();
1761 int pos = selection.end.pos();
1762 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1763 selection.start.pos(), pos,
1764 bview->buffer()->params.textclass,
1767 selection.end.par(endpar);
1768 selection.end.pos(pos);
1769 cursor.pos(selection.end.pos());
1771 endpar = endpar->next();
1773 // sometimes necessary
1775 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1777 redoParagraphs(bview, selection.start, endpar);
1779 // cutSelection can invalidate the cursor so we need to set
1781 cursor = selection.start;
1783 // need a valid cursor. (Lgb)
1786 setCursor(bview, cursor.par(), cursor.pos());
1787 selection.cursor = cursor;
1788 updateCounters(bview, cursor.row());
1792 void LyXText::copySelection(BufferView * bview)
1794 // Stuff what we got on the clipboard. Even if there is no selection.
1796 // There is a problem with having the stuffing here in that the
1797 // larger the selection the slower LyX will get. This can be
1798 // solved by running the line below only when the selection has
1799 // finished. The solution used currently just works, to make it
1800 // faster we need to be more clever and probably also have more
1801 // calls to stuffClipboard. (Lgb)
1802 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1804 // this doesnt make sense, if there is no selection
1805 if (!selection.set())
1808 // ok we have a selection. This is always between selection.start
1809 // and sel_end cursor
1811 // copy behind a space if there is one
1812 while (selection.start.par()->size() > selection.start.pos()
1813 && selection.start.par()->isLineSeparator(selection.start.pos())
1814 && (selection.start.par() != selection.end.par()
1815 || selection.start.pos() < selection.end.pos()))
1816 selection.start.pos(selection.start.pos() + 1);
1818 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1819 selection.start.pos(), selection.end.pos(),
1820 bview->buffer()->params.textclass);
1824 void LyXText::pasteSelection(BufferView * bview)
1826 // this does not make sense, if there is nothing to paste
1827 if (!CutAndPaste::checkPastePossible(cursor.par()))
1830 setUndo(bview, Undo::INSERT,
1831 cursor.par(), cursor.par()->next());
1834 Paragraph * actpar = cursor.par();
1835 int pos = cursor.pos();
1837 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1838 bview->buffer()->params.textclass);
1840 redoParagraphs(bview, cursor, endpar);
1842 setCursor(bview, cursor.par(), cursor.pos());
1845 setCursor(bview, actpar, pos);
1846 updateCounters(bview, cursor.row());
1850 // returns a pointer to the very first Paragraph
1851 Paragraph * LyXText::firstParagraph() const
1853 return ownerParagraph();
1857 // sets the selection over the number of characters of string, no check!!
1858 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1863 selection.cursor = cursor;
1864 for (string::size_type i = 0; i < str.length(); ++i)
1866 setSelection(bview);
1870 // simple replacing. The font of the first selected character is used
1871 void LyXText::replaceSelectionWithString(BufferView * bview,
1874 setCursorParUndo(bview);
1877 if (!selection.set()) { // create a dummy selection
1878 selection.end = cursor;
1879 selection.start = cursor;
1882 // Get font setting before we cut
1883 pos_type pos = selection.end.pos();
1884 LyXFont const font = selection.start.par()
1885 ->getFontSettings(bview->buffer()->params,
1886 selection.start.pos());
1888 // Insert the new string
1889 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1890 selection.end.par()->insertChar(pos, (*cit), font);
1894 // Cut the selection
1895 cutSelection(bview, true, false);
1901 // needed to insert the selection
1902 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1904 Paragraph * par = cursor.par();
1905 pos_type pos = cursor.pos();
1906 Paragraph * endpar = cursor.par()->next();
1908 setCursorParUndo(bview);
1910 // only to be sure, should not be neccessary
1913 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1915 redoParagraphs(bview, cursor, endpar);
1916 setCursor(bview, cursor.par(), cursor.pos());
1917 selection.cursor = cursor;
1918 setCursor(bview, par, pos);
1919 setSelection(bview);
1923 // turns double-CR to single CR, others where converted into one
1924 // blank. Then InsertStringAsLines is called
1925 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1927 string linestr(str);
1928 bool newline_inserted = false;
1929 for (string::size_type i = 0; i < linestr.length(); ++i) {
1930 if (linestr[i] == '\n') {
1931 if (newline_inserted) {
1932 // we know that \r will be ignored by
1933 // InsertStringA. Of course, it is a dirty
1934 // trick, but it works...
1935 linestr[i - 1] = '\r';
1939 newline_inserted = true;
1941 } else if (IsPrintable(linestr[i])) {
1942 newline_inserted = false;
1945 insertStringAsLines(bview, linestr);
1949 bool LyXText::gotoNextInset(BufferView * bview,
1950 std::vector<Inset::Code> const & codes,
1951 string const & contents) const
1953 LyXCursor res = cursor;
1956 if (res.pos() < res.par()->size() - 1) {
1957 res.pos(res.pos() + 1);
1959 res.par(res.par()->next());
1963 } while (res.par() &&
1964 !(res.par()->isInset(res.pos())
1965 && (inset = res.par()->getInset(res.pos())) != 0
1966 && find(codes.begin(), codes.end(), inset->lyxCode())
1968 && (contents.empty() ||
1969 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1973 setCursor(bview, res.par(), res.pos(), false);
1980 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1983 LyXCursor tmpcursor;
1987 Row * row = getRow(par, pos, y);
1989 // is there a break one row above
1990 if (row->previous() && row->previous()->par() == row->par()) {
1991 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1992 if (z >= row->pos()) {
1993 // set the dimensions of the row above
1994 y -= row->previous()->height();
1996 refresh_row = row->previous();
1997 status(bview, LyXText::NEED_MORE_REFRESH);
1999 breakAgain(bview, row->previous());
2001 // set the cursor again. Otherwise
2002 // dangling pointers are possible
2003 setCursor(bview, cursor.par(), cursor.pos(),
2004 false, cursor.boundary());
2005 selection.cursor = cursor;
2010 int const tmpheight = row->height();
2011 pos_type const tmplast = rowLast(row);
2015 breakAgain(bview, row);
2016 if (row->height() == tmpheight && rowLast(row) == tmplast)
2017 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2019 status(bview, LyXText::NEED_MORE_REFRESH);
2021 // check the special right address boxes
2022 if (textclasslist.Style(bview->buffer()->params.textclass,
2023 par->getLayout()).margintype
2024 == MARGIN_RIGHT_ADDRESS_BOX)
2032 redoDrawingOfParagraph(bview, tmpcursor);
2035 // set the cursor again. Otherwise dangling pointers are possible
2036 // also set the selection
2038 if (selection.set()) {
2040 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2041 false, selection.cursor.boundary());
2042 selection.cursor = cursor;
2043 setCursorIntern(bview, selection.start.par(),
2044 selection.start.pos(),
2045 false, selection.start.boundary());
2046 selection.start = cursor;
2047 setCursorIntern(bview, selection.end.par(),
2048 selection.end.pos(),
2049 false, selection.end.boundary());
2050 selection.end = cursor;
2051 setCursorIntern(bview, last_sel_cursor.par(),
2052 last_sel_cursor.pos(),
2053 false, last_sel_cursor.boundary());
2054 last_sel_cursor = cursor;
2057 setCursorIntern(bview, cursor.par(), cursor.pos(),
2058 false, cursor.boundary());
2062 // returns false if inset wasn't found
2063 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2065 // first check the current paragraph
2066 int pos = cursor.par()->getPositionOfInset(inset);
2068 checkParagraph(bview, cursor.par(), pos);
2072 // check every paragraph
2074 Paragraph * par = firstParagraph();
2076 pos = par->getPositionOfInset(inset);
2078 checkParagraph(bview, par, pos);
2088 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2090 bool setfont, bool boundary) const
2092 LyXCursor old_cursor = cursor;
2093 setCursorIntern(bview, par, pos, setfont, boundary);
2094 deleteEmptyParagraphMechanism(bview, old_cursor);
2098 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2099 pos_type pos, bool boundary) const
2103 cur.boundary(boundary);
2105 // get the cursor y position in text
2107 Row * row = getRow(par, pos, y);
2108 // y is now the beginning of the cursor row
2109 y += row->baseline();
2110 // y is now the cursor baseline
2113 // now get the cursors x position
2115 float fill_separator;
2117 float fill_label_hfill;
2118 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2120 pos_type cursor_vpos = 0;
2121 pos_type last = rowLastPrintable(row);
2123 if (pos > last + 1) // This shouldn't happen.
2125 else if (pos < row->pos())
2128 if (last < row->pos())
2129 cursor_vpos = row->pos();
2130 else if (pos > last && !boundary)
2131 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2132 ? row->pos() : last + 1;
2133 else if (pos > row->pos() &&
2134 (pos > last || boundary))
2135 /// Place cursor after char at (logical) position pos - 1
2136 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2137 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2139 /// Place cursor before char at (logical) position pos
2140 cursor_vpos = (bidi_level(pos) % 2 == 0)
2141 ? log2vis(pos) : log2vis(pos) + 1;
2143 pos_type main_body =
2144 beginningOfMainBody(bview->buffer(), row->par());
2145 if ((main_body > 0) &&
2146 ((main_body-1 > last) ||
2147 !row->par()->isLineSeparator(main_body-1)))
2150 for (pos_type vpos = row->pos();
2151 vpos < cursor_vpos; ++vpos) {
2152 pos = vis2log(vpos);
2153 if (main_body > 0 && pos == main_body - 1) {
2154 x += fill_label_hfill +
2155 lyxfont::width(textclasslist.Style(
2156 bview->buffer()->params.textclass,
2157 row->par()->getLayout())
2159 getLabelFont(bview->buffer(), row->par()));
2160 if (row->par()->isLineSeparator(main_body-1))
2161 x -= singleWidth(bview, row->par(),main_body-1);
2163 if (hfillExpansion(bview->buffer(), row, pos)) {
2164 x += singleWidth(bview, row->par(), pos);
2165 if (pos >= main_body)
2168 x += fill_label_hfill;
2169 } else if (row->par()->isSeparator(pos)) {
2170 x += singleWidth(bview, row->par(), pos);
2171 if (pos >= main_body)
2172 x += fill_separator;
2174 x += singleWidth(bview, row->par(), pos);
2183 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2185 bool setfont, bool boundary) const
2187 InsetText * it = static_cast<InsetText *>(par->inInset());
2189 if (it != inset_owner) {
2190 lyxerr << "InsetText is " << it << endl;
2191 lyxerr << "inset_owner is " << inset_owner << endl;
2192 #warning I believe this code is wrong. (Lgb)
2193 #warning Jürgen, have a look at this. (Lgb)
2194 #warning Hmmm, I guess you are right but we
2195 #warning should verify when this is needed
2196 // Jürgen, would you like to have a look?
2197 // I guess we need to move the outer cursor
2198 // and open and lock the inset (bla bla bla)
2199 // stuff I don't know... so can you have a look?
2201 // I moved the lyxerr stuff in here so we can see if
2202 // this is actually really needed and where!
2204 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2210 setCursor(bview, cursor, par, pos, boundary);
2212 setCurrentFont(bview);
2216 void LyXText::setCurrentFont(BufferView * bview) const
2218 pos_type pos = cursor.pos();
2219 if (cursor.boundary() && pos > 0)
2223 if (pos == cursor.par()->size())
2225 else // potentional bug... BUG (Lgb)
2226 if (cursor.par()->isSeparator(pos)) {
2227 if (pos > cursor.row()->pos() &&
2228 bidi_level(pos) % 2 ==
2229 bidi_level(pos - 1) % 2)
2231 else if (pos + 1 < cursor.par()->size())
2237 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2238 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2240 if (cursor.pos() == cursor.par()->size() &&
2241 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2242 !cursor.boundary()) {
2243 Language const * lang =
2244 cursor.par()->getParLanguage(bview->buffer()->params);
2245 current_font.setLanguage(lang);
2246 current_font.setNumber(LyXFont::OFF);
2247 real_current_font.setLanguage(lang);
2248 real_current_font.setNumber(LyXFont::OFF);
2253 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2255 LyXCursor old_cursor = cursor;
2257 setCursorFromCoordinates(bview, cursor, x, y);
2258 setCurrentFont(bview);
2259 deleteEmptyParagraphMechanism(bview, old_cursor);
2263 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2266 // Get the row first.
2268 Row * row = getRowNearY(y);
2270 pos_type const column = getColumnNearX(bview, row, x, bound);
2271 cur.par(row->par());
2272 cur.pos(row->pos() + column);
2274 cur.y(y + row->baseline());
2276 cur.boundary(bound);
2280 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2282 if (cursor.pos() > 0) {
2283 bool boundary = cursor.boundary();
2284 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2285 if (!internal && !boundary &&
2286 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2287 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2288 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2289 Paragraph * par = cursor.par()->previous();
2290 setCursor(bview, par, par->size());
2295 void LyXText::cursorRight(BufferView * bview, bool internal) const
2297 if (!internal && cursor.boundary() &&
2298 !cursor.par()->isNewline(cursor.pos()))
2299 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2300 else if (cursor.pos() < cursor.par()->size()) {
2301 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2303 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2304 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2305 } else if (cursor.par()->next())
2306 setCursor(bview, cursor.par()->next(), 0);
2310 void LyXText::cursorUp(BufferView * bview) const
2312 setCursorFromCoordinates(bview, cursor.x_fix(),
2313 cursor.y() - cursor.row()->baseline() - 1);
2317 void LyXText::cursorDown(BufferView * bview) const
2319 setCursorFromCoordinates(bview, cursor.x_fix(),
2320 cursor.y() - cursor.row()->baseline()
2321 + cursor.row()->height() + 1);
2325 void LyXText::cursorUpParagraph(BufferView * bview) const
2327 if (cursor.pos() > 0) {
2328 setCursor(bview, cursor.par(), 0);
2330 else if (cursor.par()->previous()) {
2331 setCursor(bview, cursor.par()->previous(), 0);
2336 void LyXText::cursorDownParagraph(BufferView * bview) const
2338 if (cursor.par()->next()) {
2339 setCursor(bview, cursor.par()->next(), 0);
2341 setCursor(bview, cursor.par(), cursor.par()->size());
2345 // fix the cursor `cur' after a characters has been deleted at `where'
2346 // position. Called by deleteEmptyParagraphMechanism
2347 void LyXText::fixCursorAfterDelete(BufferView * bview,
2349 LyXCursor const & where) const
2351 // if cursor is not in the paragraph where the delete occured,
2353 if (cur.par() != where.par())
2356 // if cursor position is after the place where the delete occured,
2358 if (cur.pos() > where.pos())
2359 cur.pos(cur.pos()-1);
2361 // recompute row et al. for this cursor
2362 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2366 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2367 LyXCursor const & old_cursor) const
2369 // Would be wrong to delete anything if we have a selection.
2370 if (selection.set()) return;
2372 // We allow all kinds of "mumbo-jumbo" when freespacing.
2373 if (textclasslist.Style(bview->buffer()->params.textclass,
2374 old_cursor.par()->getLayout()).free_spacing)
2377 bool deleted = false;
2379 /* Ok I'll put some comments here about what is missing.
2380 I have fixed BackSpace (and thus Delete) to not delete
2381 double-spaces automagically. I have also changed Cut,
2382 Copy and Paste to hopefully do some sensible things.
2383 There are still some small problems that can lead to
2384 double spaces stored in the document file or space at
2385 the beginning of paragraphs. This happens if you have
2386 the cursor betwenn to spaces and then save. Or if you
2387 cut and paste and the selection have a space at the
2388 beginning and then save right after the paste. I am
2389 sure none of these are very hard to fix, but I will
2390 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2391 that I can get some feedback. (Lgb)
2394 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2395 // delete the LineSeparator.
2398 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2399 // delete the LineSeparator.
2402 // If the pos around the old_cursor were spaces, delete one of them.
2403 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2404 // Only if the cursor has really moved
2406 if (old_cursor.pos() > 0
2407 && old_cursor.pos() < old_cursor.par()->size()
2408 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2409 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2410 old_cursor.par()->erase(old_cursor.pos() - 1);
2411 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2413 #ifdef WITH_WARNINGS
2414 #warning This will not work anymore when we have multiple views of the same buffer
2415 // In this case, we will have to correct also the cursors held by
2416 // other bufferviews. It will probably be easier to do that in a more
2417 // automated way in LyXCursor code. (JMarc 26/09/2001)
2419 // correct all cursors held by the LyXText
2420 fixCursorAfterDelete(bview, cursor, old_cursor);
2421 fixCursorAfterDelete(bview, selection.cursor,
2423 fixCursorAfterDelete(bview, selection.start,
2425 fixCursorAfterDelete(bview, selection.end, old_cursor);
2426 fixCursorAfterDelete(bview, last_sel_cursor,
2428 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2429 fixCursorAfterDelete(bview, toggle_end_cursor,
2435 // Do not delete empty paragraphs with keepempty set.
2436 if ((textclasslist.Style(bview->buffer()->params.textclass,
2437 old_cursor.par()->getLayout())).keepempty)
2440 // only do our magic if we changed paragraph
2441 if (old_cursor.par() == cursor.par())
2444 if ((old_cursor.par()->size() == 0
2445 || (old_cursor.par()->size() == 1
2446 && old_cursor.par()->isLineSeparator(0)))) {
2447 // ok, we will delete anything
2448 LyXCursor tmpcursor;
2450 // make sure that you do not delete any environments
2451 status(bview, LyXText::NEED_MORE_REFRESH);
2454 if (old_cursor.row()->previous()) {
2455 refresh_row = old_cursor.row()->previous();
2456 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2458 cursor = old_cursor; // that undo can restore the right cursor position
2459 Paragraph * endpar = old_cursor.par()->next();
2460 if (endpar && endpar->getDepth()) {
2461 while (endpar && endpar->getDepth()) {
2462 endpar = endpar->next();
2465 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2469 removeRow(old_cursor.row());
2470 if (ownerParagraph() == old_cursor.par()) {
2471 ownerParagraph(ownerParagraph()->next());
2474 delete old_cursor.par();
2476 /* Breakagain the next par. Needed because of
2477 * the parindent that can occur or dissappear.
2478 * The next row can change its height, if
2479 * there is another layout before */
2480 if (refresh_row->next()) {
2481 breakAgain(bview, refresh_row->next());
2482 updateCounters(bview, refresh_row);
2484 setHeightOfRow(bview, refresh_row);
2486 refresh_row = old_cursor.row()->next();
2487 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2490 cursor = old_cursor; // that undo can restore the right cursor position
2491 Paragraph * endpar = old_cursor.par()->next();
2492 if (endpar && endpar->getDepth()) {
2493 while (endpar && endpar->getDepth()) {
2494 endpar = endpar->next();
2497 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2501 removeRow(old_cursor.row());
2503 if (ownerParagraph() == old_cursor.par()) {
2504 ownerParagraph(ownerParagraph()->next());
2507 delete old_cursor.par();
2509 /* Breakagain the next par. Needed because of
2510 the parindent that can occur or dissappear.
2511 The next row can change its height, if
2512 there is another layout before */
2514 breakAgain(bview, refresh_row);
2515 updateCounters(bview, refresh_row->previous());
2520 setCursorIntern(bview, cursor.par(), cursor.pos());
2522 if (selection.cursor.par() == old_cursor.par()
2523 && selection.cursor.pos() == selection.cursor.pos()) {
2524 // correct selection
2525 selection.cursor = cursor;
2529 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2530 redoParagraphs(bview, old_cursor,
2531 old_cursor.par()->next());
2533 setCursorIntern(bview, cursor.par(), cursor.pos());
2534 selection.cursor = cursor;
2540 void LyXText::toggleAppendix(BufferView * bview)
2542 Paragraph * par = cursor.par();
2543 bool start = !par->params().startOfAppendix();
2545 // ensure that we have only one start_of_appendix in this document
2546 Paragraph * tmp = firstParagraph();
2547 for (; tmp; tmp = tmp->next()) {
2548 tmp->params().startOfAppendix(false);
2551 par->params().startOfAppendix(start);
2553 // we can set the refreshing parameters now
2554 status(bview, LyXText::NEED_MORE_REFRESH);
2556 refresh_row = 0; // not needed for full update
2557 updateCounters(bview, 0);
2558 setCursor(bview, cursor.par(), cursor.pos());
2562 Paragraph * LyXText::ownerParagraph() const
2565 return inset_owner->paragraph();
2567 return bv_owner->buffer()->paragraph;
2571 void LyXText::ownerParagraph(Paragraph * p) const
2574 inset_owner->paragraph(p);
2576 bv_owner->buffer()->paragraph = p;
2581 void LyXText::ownerParagraph(int id, Paragraph * p) const
2583 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2584 if (op && op->inInset()) {
2585 static_cast<InsetText *>(op->inInset())->paragraph(p);
2588 inset_owner->paragraph(p);
2590 bv_owner->buffer()->paragraph = p;
2596 LyXText::text_status LyXText::status() const
2602 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2604 // well as much as I know && binds more then || so the above and the
2605 // below are identical (this for your known use of parentesis!)
2606 // Now some explanation:
2607 // We should only go up with refreshing code so this means that if
2608 // we have a MORE refresh we should never set it to LITTLE if we still
2609 // didn't handle it (and then it will be UNCHANGED. Now as long as
2610 // we stay inside one LyXText this may work but we need to tell the
2611 // outermost LyXText that it should REALLY draw us if there is some
2612 // change in a Inset::LyXText. So you see that when we are inside a
2613 // inset's LyXText we give the LITTLE to the outermost LyXText to
2614 // tell'em that it should redraw the actual row (where the inset
2615 // resides! Capito?!
2617 if ((status_ != NEED_MORE_REFRESH)
2618 || (status_ == NEED_MORE_REFRESH
2619 && st != NEED_VERY_LITTLE_REFRESH))
2622 if (inset_owner && st != UNCHANGED) {
2623 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2624 if (!bview->text->refresh_row) {
2625 bview->text->refresh_row = bview->text->cursor.row();
2626 bview->text->refresh_y = bview->text->cursor.y() -
2627 bview->text->cursor.row()->baseline();