1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
25 #include "lyx_gui_misc.h"
27 #include "BufferView.h"
29 #include "CutAndPaste.h"
35 #include "FloatList.h"
37 #include "ParagraphParameters.h"
39 #include "insets/inseterror.h"
40 #include "insets/insetbib.h"
41 #include "insets/insetspecialchar.h"
42 #include "insets/insettext.h"
43 #include "insets/insetfloat.h"
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
58 LyXText::LyXText(BufferView * bv)
59 : number_of_rows(0), height(0), width(0), first(0),
60 bv_owner(bv), inset_owner(0), the_locking_inset(0),
61 need_break_row(0), refresh_y(0), refresh_row(0),
62 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
67 LyXText::LyXText(InsetText * inset)
68 : number_of_rows(0), height(0), width(0), first(0),
69 bv_owner(0), inset_owner(inset), the_locking_inset(0),
70 need_break_row(0), refresh_y(0), refresh_row(0),
71 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
75 void LyXText::init(BufferView * bview, bool reinit)
78 // Delete all rows, this does not touch the paragraphs!
79 Row * tmprow = firstrow;
81 tmprow = firstrow->next();
85 lastrow = refresh_row = need_break_row = 0;
86 width = height = copylayouttype = 0;
87 number_of_rows = first = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(bview, par, lastrow);
98 setCursorIntern(bview, firstrow->par(), 0);
99 selection.cursor = cursor;
105 // Delete all rows, this does not touch the paragraphs!
106 Row * tmprow = firstrow;
108 tmprow = firstrow->next();
117 LyXFont const realizeFont(LyXFont const & font,
121 LyXFont tmpfont(font);
122 Paragraph::depth_type par_depth = par->getDepth();
124 // Resolve against environment font information
125 while (par && par_depth && !tmpfont.resolved()) {
126 par = par->outerHook();
128 #ifndef INHERIT_LANGUAGE
129 tmpfont.realize(textclasslist.
130 Style(buf->params.textclass,
131 par->getLayout()).font);
133 tmpfont.realize(textclasslist.
134 Style(buf->params.textclass,
135 par->getLayout()).font,
136 buf->params.language);
138 par_depth = par->getDepth();
142 #ifndef INHERIT_LANGUAGE
143 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
145 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
146 buf->params.language);
155 // Gets the fully instantiated font at a given position in a paragraph
156 // Basically the same routine as Paragraph::getFont() in paragraph.C.
157 // The difference is that this one is used for displaying, and thus we
158 // are allowed to make cosmetic improvements. For instance make footnotes
160 // If position is -1, we get the layout font of the paragraph.
161 // If position is -2, we get the font of the manual label of the paragraph.
162 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
165 lyx::Assert(pos >= 0);
167 LyXLayout const & layout =
168 textclasslist.Style(buf->params.textclass, par->getLayout());
170 Paragraph::depth_type par_depth = par->getDepth();
171 // We specialize the 95% common case:
173 if (layout.labeltype == LABEL_MANUAL
174 && pos < beginningOfMainBody(buf, par)) {
176 LyXFont f = par->getFontSettings(buf->params,
178 #ifndef INHERIT_LANGUAGE
179 return f.realize(layout.reslabelfont);
181 return f.realize(layout.reslabelfont, buf->params.language);
184 LyXFont f = par->getFontSettings(buf->params, pos);
185 #ifndef INHERIT_LANGUAGE
186 return f.realize(layout.resfont);
188 return f.realize(layout.resfont, buf->params.language);
193 // The uncommon case need not be optimized as much
197 if (pos < beginningOfMainBody(buf, par)) {
199 layoutfont = layout.labelfont;
202 layoutfont = layout.font;
205 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
206 #ifndef INHERIT_LANGUAGE
207 tmpfont.realize(layoutfont);
209 tmpfont.realize(layoutfont, buf->params.language);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout const & layout =
219 textclasslist.Style(buf->params.textclass, par->getLayout());
221 Paragraph::depth_type par_depth = par->getDepth();
224 return layout.resfont;
227 return realizeFont(layout.font, buf, par);
231 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
233 LyXLayout const & layout =
234 textclasslist.Style(buf->params.textclass, par->getLayout());
236 Paragraph::depth_type par_depth = par->getDepth();
239 return layout.reslabelfont;
242 return realizeFont(layout.labelfont, buf, par);
246 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
247 pos_type pos, LyXFont const & fnt,
250 Buffer const * buf = bv->buffer();
251 LyXFont font = getFont(buf, par, pos);
252 font.update(fnt, buf->params.language, toggleall);
253 // Let the insets convert their font
254 if (par->isInset(pos)) {
255 Inset * inset = par->getInset(pos);
256 if (isEditableInset(inset)) {
257 UpdatableInset * uinset =
258 static_cast<UpdatableInset *>(inset);
259 uinset->setFont(bv, fnt, toggleall, true);
263 LyXLayout const & layout =
264 textclasslist.Style(buf->params.textclass,
267 // Get concrete layout font to reduce against
270 if (pos < beginningOfMainBody(buf, par))
271 layoutfont = layout.labelfont;
273 layoutfont = layout.font;
275 // Realize against environment font information
276 if (par->getDepth()) {
277 Paragraph * tp = par;
278 while (!layoutfont.resolved() && tp && tp->getDepth()) {
279 tp = tp->outerHook();
281 #ifndef INHERIT_LANGUAGE
282 layoutfont.realize(textclasslist.
283 Style(buf->params.textclass,
284 tp->getLayout()).font);
286 layoutfont.realize(textclasslist.
287 Style(buf->params.textclass,
288 tp->getLayout()).font,
289 buf->params.language);
294 #ifndef INHERIT_LANGUAGE
295 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
297 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
298 buf->params.language);
301 // Now, reduce font against full layout font
302 font.reduce(layoutfont);
304 par->setFont(pos, font);
308 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
309 pos_type pos, LyXFont const & fnt)
313 LyXLayout const & layout =
314 textclasslist.Style(buf->params.textclass,
317 // Get concrete layout font to reduce against
320 if (pos < beginningOfMainBody(buf, par))
321 layoutfont = layout.labelfont;
323 layoutfont = layout.font;
325 // Realize against environment font information
326 if (par->getDepth()) {
327 Paragraph * tp = par;
328 while (!layoutfont.resolved() && tp && tp->getDepth()) {
329 tp = tp->outerHook();
331 #ifndef INHERIT_LANGUAGE
332 layoutfont.realize(textclasslist.
333 Style(buf->params.textclass,
334 tp->getLayout()).font);
336 layoutfont.realize(textclasslist.
337 Style(buf->params.textclass,
338 tp->getLayout()).font,
339 buf->params.language);
344 #ifndef INHERIT_LANGUAGE
345 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
347 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
348 buf->params.language);
351 // Now, reduce font against full layout font
352 font.reduce(layoutfont);
354 par->setFont(pos, font);
358 // inserts a new row behind the specified row, increments
359 // the touched counters
360 void LyXText::insertRow(Row * row, Paragraph * par,
363 Row * tmprow = new Row;
366 tmprow->next(firstrow);
369 tmprow->previous(row);
370 tmprow->next(row->next());
375 tmprow->next()->previous(tmprow);
377 if (tmprow->previous())
378 tmprow->previous()->next(tmprow);
390 // removes the row and reset the touched counters
391 void LyXText::removeRow(Row * row) const
393 Row * row_prev = row->previous();
395 row->next()->previous(row_prev);
397 firstrow = row->next();
398 // lyx::Assert(firstrow);
400 row_prev->next(row->next());
402 if (row == lastrow) {
403 lyx::Assert(!row->next());
406 if (refresh_row == row) {
407 refresh_row = row_prev ? row_prev : row->next();
408 // what about refresh_y, refresh_height
411 height -= row->height(); // the text becomes smaller
414 --number_of_rows; // one row less
418 // remove all following rows of the paragraph of the specified row.
419 void LyXText::removeParagraph(Row * row) const
421 Paragraph * tmppar = row->par();
425 while (row && row->par() == tmppar) {
426 tmprow = row->next();
433 // insert the specified paragraph behind the specified row
434 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
437 insertRow(row, par, 0); /* insert a new row, starting
440 setCounter(bview->buffer(), par); // set the counters
442 // and now append the whole paragraph behind the new row
445 appendParagraph(bview, firstrow);
447 row->next()->height(0);
448 appendParagraph(bview, row->next());
453 Inset * LyXText::getInset() const
456 if (cursor.pos() == 0 && cursor.par()->bibkey) {
457 inset = cursor.par()->bibkey;
458 } else if (cursor.pos() < cursor.par()->size()
459 && cursor.par()->isInset(cursor.pos())) {
460 inset = cursor.par()->getInset(cursor.pos());
466 void LyXText::toggleInset(BufferView * bview)
468 Inset * inset = getInset();
469 // is there an editable inset at cursor position?
470 if (!isEditableInset(inset)) {
471 // No, try to see if we are inside a collapsable inset
472 if (inset_owner && inset_owner->owner()
473 && inset_owner->owner()->isOpen()) {
474 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
475 inset_owner->owner()->close(bview);
479 //bview->owner()->message(inset->editMessage());
481 // do we want to keep this?? (JMarc)
482 if (!isHighlyEditableInset(inset))
483 setCursorParUndo(bview);
485 if (inset->isOpen()) {
491 inset->open(bview, !inset->isOpen());
496 /* used in setlayout */
497 // Asger is not sure we want to do this...
498 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
501 LyXLayout const & layout =
502 textclasslist.Style(buf->params.textclass, par->getLayout());
505 for (pos_type pos = 0; pos < par->size(); ++pos) {
506 if (pos < beginningOfMainBody(buf, par))
507 layoutfont = layout.labelfont;
509 layoutfont = layout.font;
511 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
512 tmpfont.reduce(layoutfont);
513 par->setFont(pos, tmpfont);
518 Paragraph * LyXText::setLayout(BufferView * bview,
519 LyXCursor & cur, LyXCursor & sstart_cur,
520 LyXCursor & send_cur,
521 lyx::layout_type layout)
523 Paragraph * endpar = send_cur.par()->next();
524 Paragraph * undoendpar = endpar;
526 if (endpar && endpar->getDepth()) {
527 while (endpar && endpar->getDepth()) {
528 endpar = endpar->next();
532 endpar = endpar->next(); // because of parindents etc.
535 setUndo(bview, Undo::EDIT,
536 sstart_cur.par(), undoendpar);
538 // ok we have a selection. This is always between sstart_cur
539 // and sel_end cursor
542 LyXLayout const & lyxlayout =
543 textclasslist.Style(bview->buffer()->params.textclass, layout);
545 while (cur.par() != send_cur.par()) {
546 cur.par()->setLayout(layout);
547 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
548 Paragraph * fppar = cur.par();
549 fppar->params().spaceTop(lyxlayout.fill_top ?
550 VSpace(VSpace::VFILL)
551 : VSpace(VSpace::NONE));
552 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
553 VSpace(VSpace::VFILL)
554 : 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;
562 cur.par(cur.par()->next());
564 cur.par()->setLayout(layout);
565 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
566 Paragraph * fppar = cur.par();
567 fppar->params().spaceTop(lyxlayout.fill_top ?
568 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
569 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
570 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
571 if (lyxlayout.margintype == MARGIN_MANUAL)
572 cur.par()->setLabelWidthString(lyxlayout.labelstring());
573 if (lyxlayout.labeltype != LABEL_BIBLIO
575 delete fppar->bibkey;
582 // set layout over selection and make a total rebreak of those paragraphs
583 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
585 LyXCursor tmpcursor = cursor; /* store the current cursor */
587 // if there is no selection just set the layout
588 // of the current paragraph */
589 if (!selection.set()) {
590 selection.start = cursor; // dummy selection
591 selection.end = cursor;
593 Paragraph * endpar = setLayout(bview, cursor, selection.start,
594 selection.end, layout);
595 redoParagraphs(bview, selection.start, endpar);
597 // we have to reset the selection, because the
598 // geometry could have changed
599 setCursor(bview, selection.start.par(),
600 selection.start.pos(), false);
601 selection.cursor = cursor;
602 setCursor(bview, selection.end.par(), selection.end.pos(), false);
603 updateCounters(bview, cursor.row());
606 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
610 // increment depth over selection and
611 // make a total rebreak of those paragraphs
612 void LyXText::incDepth(BufferView * bview)
614 // If there is no selection, just use the current paragraph
615 if (!selection.set()) {
616 selection.start = cursor; // dummy selection
617 selection.end = cursor;
620 // We end at the next paragraph with depth 0
621 Paragraph * endpar = selection.end.par()->next();
623 Paragraph * undoendpar = endpar;
625 if (endpar && endpar->getDepth()) {
626 while (endpar && endpar->getDepth()) {
627 endpar = endpar->next();
631 endpar = endpar->next(); // because of parindents etc.
634 setUndo(bview, Undo::EDIT,
635 selection.start.par(), undoendpar);
637 LyXCursor tmpcursor = cursor; // store the current cursor
639 // ok we have a selection. This is always between sel_start_cursor
640 // and sel_end cursor
641 cursor = selection.start;
643 bool anything_changed = false;
646 // NOTE: you can't change the depth of a bibliography entry
648 textclasslist.Style(bview->buffer()->params.textclass,
649 cursor.par()->getLayout()
650 ).labeltype != LABEL_BIBLIO) {
651 Paragraph * prev = cursor.par()->previous();
654 && (prev->getDepth() - cursor.par()->getDepth() > 0
655 || (prev->getDepth() == cursor.par()->getDepth()
656 && textclasslist.Style(bview->buffer()->params.textclass,
657 prev->getLayout()).isEnvironment()))) {
658 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
659 anything_changed = true;
662 if (cursor.par() == selection.end.par())
664 cursor.par(cursor.par()->next());
667 // if nothing changed set all depth to 0
668 if (!anything_changed) {
669 cursor = selection.start;
670 while (cursor.par() != selection.end.par()) {
671 cursor.par()->params().depth(0);
672 cursor.par(cursor.par()->next());
674 cursor.par()->params().depth(0);
677 redoParagraphs(bview, selection.start, endpar);
679 // we have to reset the selection, because the
680 // geometry could have changed
681 setCursor(bview, selection.start.par(), selection.start.pos());
682 selection.cursor = cursor;
683 setCursor(bview, selection.end.par(), selection.end.pos());
684 updateCounters(bview, cursor.row());
687 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
691 // decrement depth over selection and
692 // make a total rebreak of those paragraphs
693 void LyXText::decDepth(BufferView * bview)
695 // if there is no selection just set the layout
696 // of the current paragraph
697 if (!selection.set()) {
698 selection.start = cursor; // dummy selection
699 selection.end = cursor;
701 Paragraph * endpar = selection.end.par()->next();
702 Paragraph * undoendpar = endpar;
704 if (endpar && endpar->getDepth()) {
705 while (endpar && endpar->getDepth()) {
706 endpar = endpar->next();
710 endpar = endpar->next(); // because of parindents etc.
713 setUndo(bview, Undo::EDIT,
714 selection.start.par(), undoendpar);
716 LyXCursor tmpcursor = cursor; // store the current cursor
718 // ok we have a selection. This is always between sel_start_cursor
719 // and sel_end cursor
720 cursor = selection.start;
723 if (cursor.par()->params().depth()) {
724 cursor.par()->params()
725 .depth(cursor.par()->params().depth() - 1);
727 if (cursor.par() == selection.end.par()) {
730 cursor.par(cursor.par()->next());
733 redoParagraphs(bview, selection.start, endpar);
735 // we have to reset the selection, because the
736 // geometry could have changed
737 setCursor(bview, selection.start.par(),
738 selection.start.pos());
739 selection.cursor = cursor;
740 setCursor(bview, selection.end.par(), selection.end.pos());
741 updateCounters(bview, cursor.row());
744 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
748 // set font over selection and make a total rebreak of those paragraphs
749 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
751 // if there is no selection just set the current_font
752 if (!selection.set()) {
753 // Determine basis font
755 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
757 layoutfont = getLabelFont(bview->buffer(),
760 layoutfont = getLayoutFont(bview->buffer(),
763 // Update current font
764 real_current_font.update(font,
765 bview->buffer()->params.language,
768 // Reduce to implicit settings
769 current_font = real_current_font;
770 current_font.reduce(layoutfont);
771 // And resolve it completely
772 #ifndef INHERIT_LANGUAGE
773 real_current_font.realize(layoutfont);
775 real_current_font.realize(layoutfont,
776 bview->buffer()->params.language);
781 LyXCursor tmpcursor = cursor; // store the current cursor
783 // ok we have a selection. This is always between sel_start_cursor
784 // and sel_end cursor
786 setUndo(bview, Undo::EDIT,
787 selection.start.par(), selection.end.par()->next());
789 cursor = selection.start;
790 while (cursor.par() != selection.end.par() ||
791 (cursor.pos() < selection.end.pos()))
793 if (cursor.pos() < cursor.par()->size()) {
794 // an open footnote should behave
796 setCharFont(bview, cursor.par(), cursor.pos(),
798 cursor.pos(cursor.pos() + 1);
801 cursor.par(cursor.par()->next());
806 redoParagraphs(bview, selection.start, selection.end.par()->next());
808 // we have to reset the selection, because the
809 // geometry could have changed, but we keep
810 // it for user convenience
811 setCursor(bview, selection.start.par(), selection.start.pos());
812 selection.cursor = cursor;
813 setCursor(bview, selection.end.par(), selection.end.pos());
815 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
816 tmpcursor.boundary());
820 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
822 Row * tmprow = cur.row();
823 int y = cur.y() - tmprow->baseline();
825 setHeightOfRow(bview, tmprow);
827 while (tmprow->previous()
828 && tmprow->previous()->par() == tmprow->par()) {
829 tmprow = tmprow->previous();
830 y -= tmprow->height();
831 setHeightOfRow(bview, tmprow);
834 // we can set the refreshing parameters now
835 status(bview, LyXText::NEED_MORE_REFRESH);
837 refresh_row = tmprow;
838 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
842 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
844 Row * tmprow = cur.row();
846 int y = cur.y() - tmprow->baseline();
847 setHeightOfRow(bview, tmprow);
849 while (tmprow->previous()
850 && tmprow->previous()->par() == tmprow->par()) {
851 tmprow = tmprow->previous();
852 y -= tmprow->height();
855 // we can set the refreshing parameters now
856 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
858 refresh_row = tmprow;
860 status(bview, LyXText::NEED_MORE_REFRESH);
861 setCursor(bview, cur.par(), cur.pos());
865 // deletes and inserts again all paragaphs between the cursor
866 // and the specified par
867 // This function is needed after SetLayout and SetFont etc.
868 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
869 Paragraph const * endpar) const
872 Paragraph * tmppar = 0;
873 Paragraph * first_phys_par = 0;
875 Row * tmprow = cur.row();
877 int y = cur.y() - tmprow->baseline();
879 if (!tmprow->previous()) {
880 // a trick/hack for UNDO
881 // This is needed because in an UNDO/REDO we could have changed
882 // the ownerParagrah() so the paragraph inside the row is NOT
883 // my really first par anymore. Got it Lars ;) (Jug 20011206)
884 first_phys_par = ownerParagraph();
886 first_phys_par = tmprow->par();
887 while (tmprow->previous()
888 && tmprow->previous()->par() == first_phys_par)
890 tmprow = tmprow->previous();
891 y -= tmprow->height();
895 // we can set the refreshing parameters now
896 status(bview, LyXText::NEED_MORE_REFRESH);
898 refresh_row = tmprow->previous(); /* the real refresh row will
899 be deleted, so I store
903 tmppar = tmprow->next()->par();
906 while (tmprow->next() && tmppar != endpar) {
907 removeRow(tmprow->next());
908 if (tmprow->next()) {
909 tmppar = tmprow->next()->par();
915 // remove the first one
916 tmprow2 = tmprow; /* this is because tmprow->previous()
918 tmprow = tmprow->previous();
921 tmppar = first_phys_par;
925 insertParagraph(bview, tmppar, tmprow);
929 while (tmprow->next()
930 && tmprow->next()->par() == tmppar) {
931 tmprow = tmprow->next();
933 tmppar = tmppar->next();
935 } while (tmppar && tmppar != endpar);
937 // this is because of layout changes
939 refresh_y -= refresh_row->height();
940 setHeightOfRow(bview, refresh_row);
942 refresh_row = firstrow;
944 setHeightOfRow(bview, refresh_row);
947 if (tmprow && tmprow->next())
948 setHeightOfRow(bview, tmprow->next());
952 bool LyXText::fullRebreak(BufferView * bview)
958 if (need_break_row) {
959 breakAgain(bview, need_break_row);
967 // important for the screen
970 /* the cursor set functions have a special mechanism. When they
971 * realize, that you left an empty paragraph, they will delete it.
972 * They also delete the corresponding row */
974 // need the selection cursor:
975 void LyXText::setSelection(BufferView * bview)
977 bool const lsel = selection.set();
979 if (!selection.set()) {
980 last_sel_cursor = selection.cursor;
981 selection.start = selection.cursor;
982 selection.end = selection.cursor;
987 // first the toggling area
988 if (cursor.y() < last_sel_cursor.y()
989 || (cursor.y() == last_sel_cursor.y()
990 && cursor.x() < last_sel_cursor.x())) {
991 toggle_end_cursor = last_sel_cursor;
992 toggle_cursor = cursor;
994 toggle_end_cursor = cursor;
995 toggle_cursor = last_sel_cursor;
998 last_sel_cursor = cursor;
1000 // and now the whole selection
1002 if (selection.cursor.par() == cursor.par())
1003 if (selection.cursor.pos() < cursor.pos()) {
1004 selection.end = cursor;
1005 selection.start = selection.cursor;
1007 selection.end = selection.cursor;
1008 selection.start = cursor;
1010 else if (selection.cursor.y() < cursor.y() ||
1011 (selection.cursor.y() == cursor.y()
1012 && selection.cursor.x() < cursor.x())) {
1013 selection.end = cursor;
1014 selection.start = selection.cursor;
1017 selection.end = selection.cursor;
1018 selection.start = cursor;
1021 // a selection with no contents is not a selection
1022 if (selection.start.par() == selection.end.par() &&
1023 selection.start.pos() == selection.end.pos())
1024 selection.set(false);
1026 if (inset_owner && (selection.set() || lsel))
1027 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1031 string const LyXText::selectionAsString(Buffer const * buffer,
1034 if (!selection.set()) return string();
1037 // Special handling if the whole selection is within one paragraph
1038 if (selection.start.par() == selection.end.par()) {
1039 result += selection.start.par()->asString(buffer,
1040 selection.start.pos(),
1041 selection.end.pos(),
1046 // The selection spans more than one paragraph
1048 // First paragraph in selection
1049 result += selection.start.par()->asString(buffer,
1050 selection.start.pos(),
1051 selection.start.par()->size(),
1055 // The paragraphs in between (if any)
1056 LyXCursor tmpcur(selection.start);
1057 tmpcur.par(tmpcur.par()->next());
1058 while (tmpcur.par() != selection.end.par()) {
1059 result += tmpcur.par()->asString(buffer, 0,
1060 tmpcur.par()->size(),
1062 tmpcur.par(tmpcur.par()->next());
1065 // Last paragraph in selection
1066 result += selection.end.par()->asString(buffer, 0,
1067 selection.end.pos(), label);
1073 void LyXText::clearSelection() const
1075 selection.set(false);
1076 selection.mark(false);
1077 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1081 void LyXText::cursorHome(BufferView * bview) const
1083 setCursor(bview, cursor.par(), cursor.row()->pos());
1087 void LyXText::cursorEnd(BufferView * bview) const
1089 if (!cursor.row()->next()
1090 || cursor.row()->next()->par() != cursor.row()->par()) {
1091 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1093 if (cursor.par()->size() &&
1094 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1095 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1096 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1098 setCursor(bview,cursor.par(),
1099 rowLast(cursor.row()) + 1);
1105 void LyXText::cursorTop(BufferView * bview) const
1107 while (cursor.par()->previous())
1108 cursor.par(cursor.par()->previous());
1109 setCursor(bview, cursor.par(), 0);
1113 void LyXText::cursorBottom(BufferView * bview) const
1115 while (cursor.par()->next())
1116 cursor.par(cursor.par()->next());
1117 setCursor(bview, cursor.par(), cursor.par()->size());
1121 void LyXText::toggleFree(BufferView * bview,
1122 LyXFont const & font, bool toggleall)
1124 // If the mask is completely neutral, tell user
1125 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1126 // Could only happen with user style
1127 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1131 // Try implicit word selection
1132 // If there is a change in the language the implicit word selection
1134 LyXCursor resetCursor = cursor;
1135 bool implicitSelection = (font.language() == ignore_language
1136 && font.number() == LyXFont::IGNORE)
1137 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1140 setFont(bview, font, toggleall);
1142 // Implicit selections are cleared afterwards
1143 //and cursor is set to the original position.
1144 if (implicitSelection) {
1146 cursor = resetCursor;
1147 setCursor(bview, cursor.par(), cursor.pos());
1148 selection.cursor = cursor;
1151 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1155 string LyXText::getStringToIndex(BufferView * bview)
1159 // Try implicit word selection
1160 // If there is a change in the language the implicit word selection
1162 LyXCursor resetCursor = cursor;
1163 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1165 if (!selection.set()) {
1166 bview->owner()->message(_("Nothing to index!"));
1169 if (selection.start.par() != selection.end.par()) {
1170 bview->owner()->message(_("Cannot index more than one paragraph!"));
1174 idxstring = selectionAsString(bview->buffer(), false);
1176 // Implicit selections are cleared afterwards
1177 //and cursor is set to the original position.
1178 if (implicitSelection) {
1180 cursor = resetCursor;
1181 setCursor(bview, cursor.par(), cursor.pos());
1182 selection.cursor = cursor;
1188 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1189 Paragraph const * par) const
1191 if (textclasslist.Style(buf->params.textclass,
1192 par->getLayout()).labeltype != LABEL_MANUAL)
1195 return par->beginningOfMainBody();
1199 /* the DTP switches for paragraphs. LyX will store them in the
1200 * first physicla paragraph. When a paragraph is broken, the top settings
1201 * rest, the bottom settings are given to the new one. So I can make shure,
1202 * they do not duplicate themself and you cannnot make dirty things with
1205 void LyXText::setParagraph(BufferView * bview,
1206 bool line_top, bool line_bottom,
1207 bool pagebreak_top, bool pagebreak_bottom,
1208 VSpace const & space_top,
1209 VSpace const & space_bottom,
1210 Spacing const & spacing,
1212 string labelwidthstring,
1215 LyXCursor tmpcursor = cursor;
1216 if (!selection.set()) {
1217 selection.start = cursor;
1218 selection.end = cursor;
1221 // make sure that the depth behind the selection are restored, too
1222 Paragraph * endpar = selection.end.par()->next();
1223 Paragraph * undoendpar = endpar;
1225 if (endpar && endpar->getDepth()) {
1226 while (endpar && endpar->getDepth()) {
1227 endpar = endpar->next();
1228 undoendpar = endpar;
1232 // because of parindents etc.
1233 endpar = endpar->next();
1236 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1239 Paragraph * tmppar = selection.end.par();
1240 while (tmppar != selection.start.par()->previous()) {
1241 setCursor(bview, tmppar, 0);
1242 status(bview, LyXText::NEED_MORE_REFRESH);
1243 refresh_row = cursor.row();
1244 refresh_y = cursor.y() - cursor.row()->baseline();
1245 cursor.par()->params().lineTop(line_top);
1246 cursor.par()->params().lineBottom(line_bottom);
1247 cursor.par()->params().pagebreakTop(pagebreak_top);
1248 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1249 cursor.par()->params().spaceTop(space_top);
1250 cursor.par()->params().spaceBottom(space_bottom);
1251 cursor.par()->params().spacing(spacing);
1252 // does the layout allow the new alignment?
1253 if (align == LYX_ALIGN_LAYOUT)
1254 align = textclasslist
1255 .Style(bview->buffer()->params.textclass,
1256 cursor.par()->getLayout()).align;
1257 if (align & textclasslist
1258 .Style(bview->buffer()->params.textclass,
1259 cursor.par()->getLayout()).alignpossible) {
1260 if (align == textclasslist
1261 .Style(bview->buffer()->params.textclass,
1262 cursor.par()->getLayout()).align)
1263 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1265 cursor.par()->params().align(align);
1267 cursor.par()->setLabelWidthString(labelwidthstring);
1268 cursor.par()->params().noindent(noindent);
1269 tmppar = cursor.par()->previous();
1272 redoParagraphs(bview, selection.start, endpar);
1275 setCursor(bview, selection.start.par(), selection.start.pos());
1276 selection.cursor = cursor;
1277 setCursor(bview, selection.end.par(), selection.end.pos());
1278 setSelection(bview);
1279 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1281 bview->updateInset(inset_owner, true);
1285 char loweralphaCounter(int n)
1287 if (n < 1 || n > 26)
1297 char alphaCounter(int n)
1299 if (n < 1 || n > 26)
1307 char hebrewCounter(int n)
1309 static const char hebrew[22] = {
1310 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1311 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1312 '÷', 'ø', 'ù', 'ú'
1314 if (n < 1 || n > 22)
1322 string const romanCounter(int n)
1324 static char const * roman[20] = {
1325 "i", "ii", "iii", "iv", "v",
1326 "vi", "vii", "viii", "ix", "x",
1327 "xi", "xii", "xiii", "xiv", "xv",
1328 "xvi", "xvii", "xviii", "xix", "xx"
1330 if (n < 1 || n > 20)
1339 // set the counter of a paragraph. This includes the labels
1340 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1342 LyXLayout const & layout =
1343 textclasslist.Style(buf->params.textclass,
1346 LyXTextClass const & textclass =
1347 textclasslist.TextClass(buf->params.textclass);
1349 // copy the prev-counters to this one,
1350 // unless this is the first paragraph
1351 if (par->previous()) {
1352 for (int i = 0; i < 10; ++i) {
1353 par->setCounter(i, par->previous()->getFirstCounter(i));
1355 par->params().appendix(par->previous()->params().appendix());
1356 if (!par->params().appendix() && par->params().startOfAppendix()) {
1357 par->params().appendix(true);
1358 for (int i = 0; i < 10; ++i) {
1359 par->setCounter(i, 0);
1362 par->enumdepth = par->previous()->enumdepth;
1363 par->itemdepth = par->previous()->itemdepth;
1365 for (int i = 0; i < 10; ++i) {
1366 par->setCounter(i, 0);
1368 par->params().appendix(par->params().startOfAppendix());
1373 /* Maybe we have to increment the enumeration depth.
1374 * BUT, enumeration in a footnote is considered in isolation from its
1375 * surrounding paragraph so don't increment if this is the
1376 * first line of the footnote
1377 * AND, bibliographies can't have their depth changed ie. they
1378 * are always of depth 0
1381 && par->previous()->getDepth() < par->getDepth()
1382 && textclasslist.Style(buf->params.textclass,
1383 par->previous()->getLayout()
1384 ).labeltype == LABEL_COUNTER_ENUMI
1385 && par->enumdepth < 3
1386 && layout.labeltype != LABEL_BIBLIO) {
1390 // Maybe we have to decrement the enumeration depth, see note above
1392 && par->previous()->getDepth() > par->getDepth()
1393 && layout.labeltype != LABEL_BIBLIO) {
1394 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1395 par->setCounter(6 + par->enumdepth,
1396 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1397 /* reset the counters.
1398 * A depth change is like a breaking layout
1400 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1401 par->setCounter(i, 0);
1404 if (!par->params().labelString().empty()) {
1405 par->params().labelString(string());
1408 if (layout.margintype == MARGIN_MANUAL) {
1409 if (par->params().labelWidthString().empty()) {
1410 par->setLabelWidthString(layout.labelstring());
1413 par->setLabelWidthString(string());
1416 // is it a layout that has an automatic label?
1417 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1419 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1420 if (i >= 0 && i<= buf->params.secnumdepth) {
1421 par->incCounter(i); // increment the counter
1423 // Is there a label? Useful for Chapter layout
1424 if (!par->params().appendix()) {
1425 if (!layout.labelstring().empty())
1426 par->params().labelString(layout.labelstring());
1428 par->params().labelString(string());
1430 if (!layout.labelstring_appendix().empty())
1431 par->params().labelString(layout.labelstring_appendix());
1433 par->params().labelString(string());
1438 if (!par->params().appendix()) {
1439 switch (2 * LABEL_COUNTER_CHAPTER -
1440 textclass.maxcounter() + i) {
1441 case LABEL_COUNTER_CHAPTER:
1442 s << par->getCounter(i);
1444 case LABEL_COUNTER_SECTION:
1445 s << par->getCounter(i - 1) << '.'
1446 << par->getCounter(i);
1448 case LABEL_COUNTER_SUBSECTION:
1449 s << par->getCounter(i - 2) << '.'
1450 << par->getCounter(i - 1) << '.'
1451 << par->getCounter(i);
1453 case LABEL_COUNTER_SUBSUBSECTION:
1454 s << par->getCounter(i - 3) << '.'
1455 << par->getCounter(i - 2) << '.'
1456 << par->getCounter(i - 1) << '.'
1457 << par->getCounter(i);
1460 case LABEL_COUNTER_PARAGRAPH:
1461 s << par->getCounter(i - 4) << '.'
1462 << par->getCounter(i - 3) << '.'
1463 << par->getCounter(i - 2) << '.'
1464 << par->getCounter(i - 1) << '.'
1465 << par->getCounter(i);
1467 case LABEL_COUNTER_SUBPARAGRAPH:
1468 s << par->getCounter(i - 5) << '.'
1469 << par->getCounter(i - 4) << '.'
1470 << par->getCounter(i - 3) << '.'
1471 << par->getCounter(i - 2) << '.'
1472 << par->getCounter(i - 1) << '.'
1473 << par->getCounter(i);
1477 // Can this ever be reached? And in the
1478 // case it is, how can this be correct?
1480 s << par->getCounter(i) << '.';
1483 } else { // appendix
1484 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1485 case LABEL_COUNTER_CHAPTER:
1486 if (par->isRightToLeftPar(buf->params))
1487 s << hebrewCounter(par->getCounter(i));
1489 s << alphaCounter(par->getCounter(i));
1491 case LABEL_COUNTER_SECTION:
1492 if (par->isRightToLeftPar(buf->params))
1493 s << hebrewCounter(par->getCounter(i - 1));
1495 s << alphaCounter(par->getCounter(i - 1));
1498 << par->getCounter(i);
1501 case LABEL_COUNTER_SUBSECTION:
1502 if (par->isRightToLeftPar(buf->params))
1503 s << hebrewCounter(par->getCounter(i - 2));
1505 s << alphaCounter(par->getCounter(i - 2));
1508 << par->getCounter(i-1) << '.'
1509 << par->getCounter(i);
1512 case LABEL_COUNTER_SUBSUBSECTION:
1513 if (par->isRightToLeftPar(buf->params))
1514 s << hebrewCounter(par->getCounter(i-3));
1516 s << alphaCounter(par->getCounter(i-3));
1519 << par->getCounter(i-2) << '.'
1520 << par->getCounter(i-1) << '.'
1521 << par->getCounter(i);
1524 case LABEL_COUNTER_PARAGRAPH:
1525 if (par->isRightToLeftPar(buf->params))
1526 s << hebrewCounter(par->getCounter(i-4));
1528 s << alphaCounter(par->getCounter(i-4));
1531 << par->getCounter(i-3) << '.'
1532 << par->getCounter(i-2) << '.'
1533 << par->getCounter(i-1) << '.'
1534 << par->getCounter(i);
1537 case LABEL_COUNTER_SUBPARAGRAPH:
1538 if (par->isRightToLeftPar(buf->params))
1539 s << hebrewCounter(par->getCounter(i-5));
1541 s << alphaCounter(par->getCounter(i-5));
1544 << par->getCounter(i-4) << '.'
1545 << par->getCounter(i-3) << '.'
1546 << par->getCounter(i-2) << '.'
1547 << par->getCounter(i-1) << '.'
1548 << par->getCounter(i);
1552 // Can this ever be reached? And in the
1553 // case it is, how can this be correct?
1555 s << par->getCounter(i) << '.';
1561 par->params().labelString(par->params().labelString() +s.str().c_str());
1562 // We really want to remove the c_str as soon as
1565 for (i++; i < 10; ++i) {
1566 // reset the following counters
1567 par->setCounter(i, 0);
1569 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1570 for (i++; i < 10; ++i) {
1571 // reset the following counters
1572 par->setCounter(i, 0);
1574 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1575 par->incCounter(i + par->enumdepth);
1576 int number = par->getCounter(i + par->enumdepth);
1580 switch (par->enumdepth) {
1582 if (par->isRightToLeftPar(buf->params))
1584 << hebrewCounter(number)
1588 << loweralphaCounter(number)
1592 if (par->isRightToLeftPar(buf->params))
1593 s << '.' << romanCounter(number);
1595 s << romanCounter(number) << '.';
1598 if (par->isRightToLeftPar(buf->params))
1600 << alphaCounter(number);
1602 s << alphaCounter(number)
1606 if (par->isRightToLeftPar(buf->params))
1613 par->params().labelString(s.str().c_str());
1615 for (i += par->enumdepth + 1; i < 10; ++i) {
1616 // reset the following counters
1617 par->setCounter(i, 0);
1621 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1622 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1624 int number = par->getCounter(i);
1626 InsetCommandParams p("bibitem" );
1627 par->bibkey = new InsetBibKey(p);
1629 par->bibkey->setCounter(number);
1630 par->params().labelString(layout.labelstring());
1632 // In biblio should't be following counters but...
1634 string s = layout.labelstring();
1636 // the caption hack:
1637 if (layout.labeltype == LABEL_SENSITIVE) {
1638 bool isOK (par->inInset() && par->inInset()->owner() &&
1639 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1642 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1644 = floatList.getType(tmp->type());
1645 // We should get the correct number here too.
1646 s = fl.name() + " #:";
1648 /* par->SetLayout(0);
1649 s = layout->labelstring; */
1650 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1651 ? " :úåòîùî øñç" : "Senseless: ";
1654 par->params().labelString(s);
1656 /* reset the enumeration counter. They are always resetted
1657 * when there is any other layout between */
1658 for (int i = 6 + par->enumdepth; i < 10; ++i)
1659 par->setCounter(i, 0);
1664 // Updates all counters BEHIND the row. Changed paragraphs
1665 // with a dynamic left margin will be rebroken.
1666 void LyXText::updateCounters(BufferView * bview, Row * row) const
1674 par = row->par()->next();
1678 while (row->par() != par)
1681 setCounter(bview->buffer(), par);
1683 // now check for the headline layouts. remember that they
1684 // have a dynamic left margin
1685 if ((textclasslist.Style(bview->buffer()->params.textclass,
1686 par->layout).margintype == MARGIN_DYNAMIC
1687 || textclasslist.Style(bview->buffer()->params.textclass,
1688 par->layout).labeltype == LABEL_SENSITIVE)) {
1690 // Rebreak the paragraph
1691 removeParagraph(row);
1692 appendParagraph(bview, row);
1699 void LyXText::insertInset(BufferView * bview, Inset * inset)
1701 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1703 // I don't know if this is necessary here (Jug 20020102)
1704 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1705 cursor.par()->insertInset(cursor.pos(), inset);
1706 // Just to rebreak and refresh correctly.
1707 // The character will not be inserted a second time
1708 insertChar(bview, Paragraph::META_INSET);
1710 // If we enter a highly editable inset the cursor should be to before
1711 // the inset. This couldn't happen before as Undo was not handled inside
1712 // inset now after the Undo LyX tries to call inset->Edit(...) again
1713 // and cannot do this as the cursor is behind the inset and GetInset
1714 // does not return the inset!
1715 if (isHighlyEditableInset(inset)) {
1716 cursorLeft(bview, true);
1722 void LyXText::copyEnvironmentType()
1724 copylayouttype = cursor.par()->getLayout();
1728 void LyXText::pasteEnvironmentType(BufferView * bview)
1730 setLayout(bview, copylayouttype);
1734 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1736 // Stuff what we got on the clipboard. Even if there is no selection.
1738 // There is a problem with having the stuffing here in that the
1739 // larger the selection the slower LyX will get. This can be
1740 // solved by running the line below only when the selection has
1741 // finished. The solution used currently just works, to make it
1742 // faster we need to be more clever and probably also have more
1743 // calls to stuffClipboard. (Lgb)
1744 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1746 // This doesn't make sense, if there is no selection
1747 if (!selection.set())
1750 // OK, we have a selection. This is always between selection.start
1751 // and selection.end
1753 // make sure that the depth behind the selection are restored, too
1754 Paragraph * endpar = selection.end.par()->next();
1755 Paragraph * undoendpar = endpar;
1757 if (endpar && endpar->getDepth()) {
1758 while (endpar && endpar->getDepth()) {
1759 endpar = endpar->next();
1760 undoendpar = endpar;
1762 } else if (endpar) {
1763 endpar = endpar->next(); // because of parindents etc.
1766 setUndo(bview, Undo::DELETE,
1767 selection.start.par(), undoendpar);
1769 // there are two cases: cut only within one paragraph or
1770 // more than one paragraph
1771 if (selection.start.par() == selection.end.par()) {
1772 // only within one paragraph
1773 endpar = selection.end.par();
1774 int pos = selection.end.pos();
1775 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1776 selection.start.pos(), pos,
1777 bview->buffer()->params.textclass,
1779 selection.end.pos(pos);
1781 endpar = selection.end.par();
1782 int pos = selection.end.pos();
1783 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1784 selection.start.pos(), pos,
1785 bview->buffer()->params.textclass,
1788 selection.end.par(endpar);
1789 selection.end.pos(pos);
1790 cursor.pos(selection.end.pos());
1792 endpar = endpar->next();
1794 // sometimes necessary
1796 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1798 redoParagraphs(bview, selection.start, endpar);
1800 // cutSelection can invalidate the cursor so we need to set
1802 cursor = selection.start;
1804 // need a valid cursor. (Lgb)
1807 setCursor(bview, cursor.par(), cursor.pos());
1808 selection.cursor = cursor;
1809 updateCounters(bview, cursor.row());
1813 void LyXText::copySelection(BufferView * bview)
1815 // stuff the selection onto the X clipboard, from an explicit copy request
1816 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1818 // this doesnt make sense, if there is no selection
1819 if (!selection.set())
1822 // ok we have a selection. This is always between selection.start
1823 // and sel_end cursor
1825 // copy behind a space if there is one
1826 while (selection.start.par()->size() > selection.start.pos()
1827 && selection.start.par()->isLineSeparator(selection.start.pos())
1828 && (selection.start.par() != selection.end.par()
1829 || selection.start.pos() < selection.end.pos()))
1830 selection.start.pos(selection.start.pos() + 1);
1832 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1833 selection.start.pos(), selection.end.pos(),
1834 bview->buffer()->params.textclass);
1838 void LyXText::pasteSelection(BufferView * bview)
1840 // this does not make sense, if there is nothing to paste
1841 if (!CutAndPaste::checkPastePossible(cursor.par()))
1844 setUndo(bview, Undo::INSERT,
1845 cursor.par(), cursor.par()->next());
1848 Paragraph * actpar = cursor.par();
1849 int pos = cursor.pos();
1851 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1852 bview->buffer()->params.textclass);
1854 redoParagraphs(bview, cursor, endpar);
1856 setCursor(bview, cursor.par(), cursor.pos());
1859 setCursor(bview, actpar, pos);
1860 updateCounters(bview, cursor.row());
1864 // sets the selection over the number of characters of string, no check!!
1865 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1870 selection.cursor = cursor;
1871 for (string::size_type i = 0; i < str.length(); ++i)
1873 setSelection(bview);
1877 // simple replacing. The font of the first selected character is used
1878 void LyXText::replaceSelectionWithString(BufferView * bview,
1881 setCursorParUndo(bview);
1884 if (!selection.set()) { // create a dummy selection
1885 selection.end = cursor;
1886 selection.start = cursor;
1889 // Get font setting before we cut
1890 pos_type pos = selection.end.pos();
1891 LyXFont const font = selection.start.par()
1892 ->getFontSettings(bview->buffer()->params,
1893 selection.start.pos());
1895 // Insert the new string
1896 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1897 selection.end.par()->insertChar(pos, (*cit), font);
1901 // Cut the selection
1902 cutSelection(bview, true, false);
1908 // needed to insert the selection
1909 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1911 Paragraph * par = cursor.par();
1912 pos_type pos = cursor.pos();
1913 Paragraph * endpar = cursor.par()->next();
1915 setCursorParUndo(bview);
1917 // only to be sure, should not be neccessary
1920 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1922 redoParagraphs(bview, cursor, endpar);
1923 setCursor(bview, cursor.par(), cursor.pos());
1924 selection.cursor = cursor;
1925 setCursor(bview, par, pos);
1926 setSelection(bview);
1930 // turns double-CR to single CR, others where converted into one
1931 // blank. Then InsertStringAsLines is called
1932 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1934 string linestr(str);
1935 bool newline_inserted = false;
1936 for (string::size_type i = 0; i < linestr.length(); ++i) {
1937 if (linestr[i] == '\n') {
1938 if (newline_inserted) {
1939 // we know that \r will be ignored by
1940 // InsertStringA. Of course, it is a dirty
1941 // trick, but it works...
1942 linestr[i - 1] = '\r';
1946 newline_inserted = true;
1948 } else if (IsPrintable(linestr[i])) {
1949 newline_inserted = false;
1952 insertStringAsLines(bview, linestr);
1956 bool LyXText::gotoNextInset(BufferView * bview,
1957 vector<Inset::Code> const & codes,
1958 string const & contents) const
1960 LyXCursor res = cursor;
1963 if (res.pos() < res.par()->size() - 1) {
1964 res.pos(res.pos() + 1);
1966 res.par(res.par()->next());
1970 } while (res.par() &&
1971 !(res.par()->isInset(res.pos())
1972 && (inset = res.par()->getInset(res.pos())) != 0
1973 && find(codes.begin(), codes.end(), inset->lyxCode())
1975 && (contents.empty() ||
1976 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1980 setCursor(bview, res.par(), res.pos(), false);
1987 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1990 LyXCursor tmpcursor;
1994 Row * row = getRow(par, pos, y);
1996 // is there a break one row above
1997 if (row->previous() && row->previous()->par() == row->par()) {
1998 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1999 if (z >= row->pos()) {
2000 // set the dimensions of the row above
2001 y -= row->previous()->height();
2003 refresh_row = row->previous();
2004 status(bview, LyXText::NEED_MORE_REFRESH);
2006 breakAgain(bview, row->previous());
2008 // set the cursor again. Otherwise
2009 // dangling pointers are possible
2010 setCursor(bview, cursor.par(), cursor.pos(),
2011 false, cursor.boundary());
2012 selection.cursor = cursor;
2017 int const tmpheight = row->height();
2018 pos_type const tmplast = rowLast(row);
2022 breakAgain(bview, row);
2023 if (row->height() == tmpheight && rowLast(row) == tmplast)
2024 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2026 status(bview, LyXText::NEED_MORE_REFRESH);
2028 // check the special right address boxes
2029 if (textclasslist.Style(bview->buffer()->params.textclass,
2030 par->getLayout()).margintype
2031 == MARGIN_RIGHT_ADDRESS_BOX)
2039 redoDrawingOfParagraph(bview, tmpcursor);
2042 // set the cursor again. Otherwise dangling pointers are possible
2043 // also set the selection
2045 if (selection.set()) {
2047 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2048 false, selection.cursor.boundary());
2049 selection.cursor = cursor;
2050 setCursorIntern(bview, selection.start.par(),
2051 selection.start.pos(),
2052 false, selection.start.boundary());
2053 selection.start = cursor;
2054 setCursorIntern(bview, selection.end.par(),
2055 selection.end.pos(),
2056 false, selection.end.boundary());
2057 selection.end = cursor;
2058 setCursorIntern(bview, last_sel_cursor.par(),
2059 last_sel_cursor.pos(),
2060 false, last_sel_cursor.boundary());
2061 last_sel_cursor = cursor;
2064 setCursorIntern(bview, cursor.par(), cursor.pos(),
2065 false, cursor.boundary());
2069 // returns false if inset wasn't found
2070 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2072 // first check the current paragraph
2073 int pos = cursor.par()->getPositionOfInset(inset);
2075 checkParagraph(bview, cursor.par(), pos);
2079 // check every paragraph
2081 Paragraph * par = ownerParagraph();
2083 pos = par->getPositionOfInset(inset);
2085 checkParagraph(bview, par, pos);
2095 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2097 bool setfont, bool boundary) const
2099 LyXCursor old_cursor = cursor;
2100 setCursorIntern(bview, par, pos, setfont, boundary);
2101 return deleteEmptyParagraphMechanism(bview, old_cursor);
2105 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2106 pos_type pos, bool boundary) const
2113 cur.boundary(boundary);
2115 // get the cursor y position in text
2117 Row * row = getRow(par, pos, y);
2118 // y is now the beginning of the cursor row
2119 y += row->baseline();
2120 // y is now the cursor baseline
2123 // now get the cursors x position
2125 float fill_separator;
2127 float fill_label_hfill;
2128 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2130 pos_type cursor_vpos = 0;
2131 pos_type last = rowLastPrintable(row);
2133 if (pos > last + 1) {
2134 // This shouldn't happen.
2137 } else if (pos < row->pos()) {
2142 if (last < row->pos())
2143 cursor_vpos = row->pos();
2144 else if (pos > last && !boundary)
2145 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2146 ? row->pos() : last + 1;
2147 else if (pos > row->pos() &&
2148 (pos > last || boundary))
2149 /// Place cursor after char at (logical) position pos - 1
2150 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2151 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2153 /// Place cursor before char at (logical) position pos
2154 cursor_vpos = (bidi_level(pos) % 2 == 0)
2155 ? log2vis(pos) : log2vis(pos) + 1;
2157 pos_type main_body =
2158 beginningOfMainBody(bview->buffer(), row->par());
2159 if ((main_body > 0) &&
2160 ((main_body-1 > last) ||
2161 !row->par()->isLineSeparator(main_body-1)))
2164 for (pos_type vpos = row->pos();
2165 vpos < cursor_vpos; ++vpos) {
2166 pos = vis2log(vpos);
2167 if (main_body > 0 && pos == main_body - 1) {
2168 x += fill_label_hfill +
2169 lyxfont::width(textclasslist.Style(
2170 bview->buffer()->params.textclass,
2171 row->par()->getLayout())
2173 getLabelFont(bview->buffer(), row->par()));
2174 if (row->par()->isLineSeparator(main_body-1))
2175 x -= singleWidth(bview, row->par(),main_body-1);
2177 if (hfillExpansion(bview->buffer(), row, pos)) {
2178 x += singleWidth(bview, row->par(), pos);
2179 if (pos >= main_body)
2182 x += fill_label_hfill;
2183 } else if (row->par()->isSeparator(pos)) {
2184 x += singleWidth(bview, row->par(), pos);
2185 if (pos >= main_body)
2186 x += fill_separator;
2188 x += singleWidth(bview, row->par(), pos);
2197 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2198 pos_type pos, bool setfont, bool boundary) const
2200 InsetText * it = static_cast<InsetText *>(par->inInset());
2202 if (it != inset_owner) {
2203 lyxerr << "InsetText is " << it << endl;
2204 lyxerr << "inset_owner is " << inset_owner << endl;
2205 #warning I believe this code is wrong. (Lgb)
2206 #warning Jürgen, have a look at this. (Lgb)
2207 #warning Hmmm, I guess you are right but we
2208 #warning should verify when this is needed
2209 // Jürgen, would you like to have a look?
2210 // I guess we need to move the outer cursor
2211 // and open and lock the inset (bla bla bla)
2212 // stuff I don't know... so can you have a look?
2214 // I moved the lyxerr stuff in here so we can see if
2215 // this is actually really needed and where!
2217 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2222 setCursor(bview, cursor, par, pos, boundary);
2224 setCurrentFont(bview);
2228 void LyXText::setCurrentFont(BufferView * bview) const
2230 pos_type pos = cursor.pos();
2231 if (cursor.boundary() && pos > 0)
2235 if (pos == cursor.par()->size())
2237 else // potentional bug... BUG (Lgb)
2238 if (cursor.par()->isSeparator(pos)) {
2239 if (pos > cursor.row()->pos() &&
2240 bidi_level(pos) % 2 ==
2241 bidi_level(pos - 1) % 2)
2243 else if (pos + 1 < cursor.par()->size())
2249 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2250 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2252 if (cursor.pos() == cursor.par()->size() &&
2253 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2254 !cursor.boundary()) {
2255 Language const * lang =
2256 cursor.par()->getParLanguage(bview->buffer()->params);
2257 current_font.setLanguage(lang);
2258 current_font.setNumber(LyXFont::OFF);
2259 real_current_font.setLanguage(lang);
2260 real_current_font.setNumber(LyXFont::OFF);
2265 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2267 LyXCursor old_cursor = cursor;
2269 setCursorFromCoordinates(bview, cursor, x, y);
2270 setCurrentFont(bview);
2271 deleteEmptyParagraphMechanism(bview, old_cursor);
2275 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2278 // Get the row first.
2280 Row * row = getRowNearY(y);
2282 pos_type const column = getColumnNearX(bview, row, x, bound);
2283 cur.par(row->par());
2284 cur.pos(row->pos() + column);
2286 cur.y(y + row->baseline());
2288 cur.boundary(bound);
2292 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2294 if (cursor.pos() > 0) {
2295 bool boundary = cursor.boundary();
2296 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2297 if (!internal && !boundary &&
2298 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2299 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2300 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2301 Paragraph * par = cursor.par()->previous();
2302 setCursor(bview, par, par->size());
2307 void LyXText::cursorRight(BufferView * bview, bool internal) const
2309 if (!internal && cursor.boundary() &&
2310 !cursor.par()->isNewline(cursor.pos()))
2311 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2312 else if (cursor.pos() < cursor.par()->size()) {
2313 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2315 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2316 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2317 } else if (cursor.par()->next())
2318 setCursor(bview, cursor.par()->next(), 0);
2322 void LyXText::cursorUp(BufferView * bview) const
2324 setCursorFromCoordinates(bview, cursor.x_fix(),
2325 cursor.y() - cursor.row()->baseline() - 1);
2329 void LyXText::cursorDown(BufferView * bview) const
2331 setCursorFromCoordinates(bview, cursor.x_fix(),
2332 cursor.y() - cursor.row()->baseline()
2333 + cursor.row()->height() + 1);
2337 void LyXText::cursorUpParagraph(BufferView * bview) const
2339 if (cursor.pos() > 0) {
2340 setCursor(bview, cursor.par(), 0);
2342 else if (cursor.par()->previous()) {
2343 setCursor(bview, cursor.par()->previous(), 0);
2348 void LyXText::cursorDownParagraph(BufferView * bview) const
2350 if (cursor.par()->next()) {
2351 setCursor(bview, cursor.par()->next(), 0);
2353 setCursor(bview, cursor.par(), cursor.par()->size());
2357 // fix the cursor `cur' after a characters has been deleted at `where'
2358 // position. Called by deleteEmptyParagraphMechanism
2359 void LyXText::fixCursorAfterDelete(BufferView * bview,
2361 LyXCursor const & where) const
2363 // if cursor is not in the paragraph where the delete occured,
2365 if (cur.par() != where.par())
2368 // if cursor position is after the place where the delete occured,
2370 if (cur.pos() > where.pos())
2371 cur.pos(cur.pos()-1);
2373 // recompute row et al. for this cursor
2374 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2378 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2379 LyXCursor const & old_cursor) const
2381 // Would be wrong to delete anything if we have a selection.
2382 if (selection.set()) return false;
2384 // We allow all kinds of "mumbo-jumbo" when freespacing.
2385 if (textclasslist.Style(bview->buffer()->params.textclass,
2386 old_cursor.par()->getLayout()).free_spacing
2387 || old_cursor.par()->isFreeSpacing())
2392 /* Ok I'll put some comments here about what is missing.
2393 I have fixed BackSpace (and thus Delete) to not delete
2394 double-spaces automagically. I have also changed Cut,
2395 Copy and Paste to hopefully do some sensible things.
2396 There are still some small problems that can lead to
2397 double spaces stored in the document file or space at
2398 the beginning of paragraphs. This happens if you have
2399 the cursor betwenn to spaces and then save. Or if you
2400 cut and paste and the selection have a space at the
2401 beginning and then save right after the paste. I am
2402 sure none of these are very hard to fix, but I will
2403 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2404 that I can get some feedback. (Lgb)
2407 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2408 // delete the LineSeparator.
2411 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2412 // delete the LineSeparator.
2415 // If the pos around the old_cursor were spaces, delete one of them.
2416 if (old_cursor.par() != cursor.par()
2417 || old_cursor.pos() != cursor.pos()) {
2418 // Only if the cursor has really moved
2420 if (old_cursor.pos() > 0
2421 && old_cursor.pos() < old_cursor.par()->size()
2422 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2423 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2424 old_cursor.par()->erase(old_cursor.pos() - 1);
2425 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2427 #ifdef WITH_WARNINGS
2428 #warning This will not work anymore when we have multiple views of the same buffer
2429 // In this case, we will have to correct also the cursors held by
2430 // other bufferviews. It will probably be easier to do that in a more
2431 // automated way in LyXCursor code. (JMarc 26/09/2001)
2433 // correct all cursors held by the LyXText
2434 fixCursorAfterDelete(bview, cursor, old_cursor);
2435 fixCursorAfterDelete(bview, selection.cursor,
2437 fixCursorAfterDelete(bview, selection.start,
2439 fixCursorAfterDelete(bview, selection.end, old_cursor);
2440 fixCursorAfterDelete(bview, last_sel_cursor,
2442 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2443 fixCursorAfterDelete(bview, toggle_end_cursor,
2449 // don't delete anything if this is the ONLY paragraph!
2450 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2453 // Do not delete empty paragraphs with keepempty set.
2454 if ((textclasslist.Style(bview->buffer()->params.textclass,
2455 old_cursor.par()->getLayout())).keepempty)
2458 // only do our magic if we changed paragraph
2459 if (old_cursor.par() == cursor.par())
2462 // record if we have deleted a paragraph
2463 // we can't possibly have deleted a paragraph before this point
2464 bool deleted = false;
2466 if ((old_cursor.par()->size() == 0
2467 || (old_cursor.par()->size() == 1
2468 && old_cursor.par()->isLineSeparator(0)))) {
2469 // ok, we will delete anything
2470 LyXCursor tmpcursor;
2472 // make sure that you do not delete any environments
2473 status(bview, LyXText::NEED_MORE_REFRESH);
2476 if (old_cursor.row()->previous()) {
2477 refresh_row = old_cursor.row()->previous();
2478 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2480 cursor = old_cursor; // that undo can restore the right cursor position
2481 Paragraph * endpar = old_cursor.par()->next();
2482 if (endpar && endpar->getDepth()) {
2483 while (endpar && endpar->getDepth()) {
2484 endpar = endpar->next();
2487 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2491 removeRow(old_cursor.row());
2492 if (ownerParagraph() == old_cursor.par()) {
2493 ownerParagraph(ownerParagraph()->next());
2496 delete old_cursor.par();
2498 /* Breakagain the next par. Needed because of
2499 * the parindent that can occur or dissappear.
2500 * The next row can change its height, if
2501 * there is another layout before */
2502 if (refresh_row->next()) {
2503 breakAgain(bview, refresh_row->next());
2504 updateCounters(bview, refresh_row);
2506 setHeightOfRow(bview, refresh_row);
2508 refresh_row = old_cursor.row()->next();
2509 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2512 cursor = old_cursor; // that undo can restore the right cursor position
2513 Paragraph * endpar = old_cursor.par()->next();
2514 if (endpar && endpar->getDepth()) {
2515 while (endpar && endpar->getDepth()) {
2516 endpar = endpar->next();
2519 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2523 removeRow(old_cursor.row());
2525 if (ownerParagraph() == old_cursor.par()) {
2526 ownerParagraph(ownerParagraph()->next());
2529 delete old_cursor.par();
2531 /* Breakagain the next par. Needed because of
2532 the parindent that can occur or dissappear.
2533 The next row can change its height, if
2534 there is another layout before */
2536 breakAgain(bview, refresh_row);
2537 updateCounters(bview, refresh_row->previous());
2542 setCursorIntern(bview, cursor.par(), cursor.pos());
2544 if (selection.cursor.par() == old_cursor.par()
2545 && selection.cursor.pos() == old_cursor.pos()) {
2546 // correct selection
2547 selection.cursor = cursor;
2551 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2552 redoParagraphs(bview, old_cursor,
2553 old_cursor.par()->next());
2555 setCursorIntern(bview, cursor.par(), cursor.pos());
2556 selection.cursor = cursor;
2563 void LyXText::toggleAppendix(BufferView * bview)
2565 Paragraph * par = cursor.par();
2566 bool start = !par->params().startOfAppendix();
2568 // ensure that we have only one start_of_appendix in this document
2569 Paragraph * tmp = ownerParagraph();
2570 for (; tmp; tmp = tmp->next()) {
2571 tmp->params().startOfAppendix(false);
2574 par->params().startOfAppendix(start);
2576 // we can set the refreshing parameters now
2577 status(bview, LyXText::NEED_MORE_REFRESH);
2579 refresh_row = 0; // not needed for full update
2580 updateCounters(bview, 0);
2581 setCursor(bview, cursor.par(), cursor.pos());
2585 Paragraph * LyXText::ownerParagraph() const
2588 return inset_owner->paragraph();
2590 return bv_owner->buffer()->paragraph;
2594 void LyXText::ownerParagraph(Paragraph * p) const
2597 inset_owner->paragraph(p);
2599 bv_owner->buffer()->paragraph = p;
2604 void LyXText::ownerParagraph(int id, Paragraph * p) const
2606 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2607 if (op && op->inInset()) {
2608 static_cast<InsetText *>(op->inInset())->paragraph(p);
2615 LyXText::text_status LyXText::status() const
2621 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2623 // well as much as I know && binds more then || so the above and the
2624 // below are identical (this for your known use of parentesis!)
2625 // Now some explanation:
2626 // We should only go up with refreshing code so this means that if
2627 // we have a MORE refresh we should never set it to LITTLE if we still
2628 // didn't handle it (and then it will be UNCHANGED. Now as long as
2629 // we stay inside one LyXText this may work but we need to tell the
2630 // outermost LyXText that it should REALLY draw us if there is some
2631 // change in a Inset::LyXText. So you see that when we are inside a
2632 // inset's LyXText we give the LITTLE to the outermost LyXText to
2633 // tell'em that it should redraw the actual row (where the inset
2634 // resides! Capito?!
2636 if ((status_ != NEED_MORE_REFRESH)
2637 || (status_ == NEED_MORE_REFRESH
2638 && st != NEED_VERY_LITTLE_REFRESH))
2641 if (inset_owner && st != UNCHANGED) {
2642 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2643 if (!bview->text->refresh_row) {
2644 bview->text->refresh_row = bview->text->cursor.row();
2645 bview->text->refresh_y = bview->text->cursor.y() -
2646 bview->text->cursor.row()->baseline();