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, pos);
178 par->inInset()->getDrawFont(f);
179 #ifndef INHERIT_LANGUAGE
180 return f.realize(layout.reslabelfont);
182 return f.realize(layout.reslabelfont, buf->params.language);
185 LyXFont f = par->getFontSettings(buf->params, pos);
187 par->inInset()->getDrawFont(f);
188 #ifndef INHERIT_LANGUAGE
189 return f.realize(layout.resfont);
191 return f.realize(layout.resfont, buf->params.language);
196 // The uncommon case need not be optimized as much
200 if (pos < beginningOfMainBody(buf, par)) {
202 layoutfont = layout.labelfont;
205 layoutfont = layout.font;
208 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
209 #ifndef INHERIT_LANGUAGE
210 tmpfont.realize(layoutfont);
212 tmpfont.realize(layoutfont, buf->params.language);
215 par->inInset()->getDrawFont(tmpfont);
217 return realizeFont(tmpfont, buf, par);
221 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
223 LyXLayout const & layout =
224 textclasslist.Style(buf->params.textclass, par->getLayout());
226 Paragraph::depth_type par_depth = par->getDepth();
229 return layout.resfont;
232 return realizeFont(layout.font, buf, par);
236 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
238 LyXLayout const & layout =
239 textclasslist.Style(buf->params.textclass, par->getLayout());
241 Paragraph::depth_type par_depth = par->getDepth();
244 return layout.reslabelfont;
247 return realizeFont(layout.labelfont, buf, par);
251 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
252 pos_type pos, LyXFont const & fnt,
255 Buffer const * buf = bv->buffer();
256 LyXFont font = getFont(buf, par, pos);
257 font.update(fnt, buf->params.language, toggleall);
258 // Let the insets convert their font
259 if (par->isInset(pos)) {
260 Inset * inset = par->getInset(pos);
261 if (isEditableInset(inset)) {
262 UpdatableInset * uinset =
263 static_cast<UpdatableInset *>(inset);
264 uinset->setFont(bv, fnt, toggleall, true);
268 LyXLayout const & layout =
269 textclasslist.Style(buf->params.textclass,
272 // Get concrete layout font to reduce against
275 if (pos < beginningOfMainBody(buf, par))
276 layoutfont = layout.labelfont;
278 layoutfont = layout.font;
280 // Realize against environment font information
281 if (par->getDepth()) {
282 Paragraph * tp = par;
283 while (!layoutfont.resolved() && tp && tp->getDepth()) {
284 tp = tp->outerHook();
286 #ifndef INHERIT_LANGUAGE
287 layoutfont.realize(textclasslist.
288 Style(buf->params.textclass,
289 tp->getLayout()).font);
291 layoutfont.realize(textclasslist.
292 Style(buf->params.textclass,
293 tp->getLayout()).font,
294 buf->params.language);
299 #ifndef INHERIT_LANGUAGE
300 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
302 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
303 buf->params.language);
306 // Now, reduce font against full layout font
307 font.reduce(layoutfont);
309 par->setFont(pos, font);
313 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
314 pos_type pos, LyXFont const & fnt)
318 LyXLayout const & layout =
319 textclasslist.Style(buf->params.textclass,
322 // Get concrete layout font to reduce against
325 if (pos < beginningOfMainBody(buf, par))
326 layoutfont = layout.labelfont;
328 layoutfont = layout.font;
330 // Realize against environment font information
331 if (par->getDepth()) {
332 Paragraph * tp = par;
333 while (!layoutfont.resolved() && tp && tp->getDepth()) {
334 tp = tp->outerHook();
336 #ifndef INHERIT_LANGUAGE
337 layoutfont.realize(textclasslist.
338 Style(buf->params.textclass,
339 tp->getLayout()).font);
341 layoutfont.realize(textclasslist.
342 Style(buf->params.textclass,
343 tp->getLayout()).font,
344 buf->params.language);
349 #ifndef INHERIT_LANGUAGE
350 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
352 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
353 buf->params.language);
356 // Now, reduce font against full layout font
357 font.reduce(layoutfont);
359 par->setFont(pos, font);
363 // inserts a new row behind the specified row, increments
364 // the touched counters
365 void LyXText::insertRow(Row * row, Paragraph * par,
368 Row * tmprow = new Row;
371 tmprow->next(firstrow);
374 tmprow->previous(row);
375 tmprow->next(row->next());
380 tmprow->next()->previous(tmprow);
382 if (tmprow->previous())
383 tmprow->previous()->next(tmprow);
395 // removes the row and reset the touched counters
396 void LyXText::removeRow(Row * row) const
398 Row * row_prev = row->previous();
400 row->next()->previous(row_prev);
402 firstrow = row->next();
403 // lyx::Assert(firstrow);
405 row_prev->next(row->next());
407 if (row == lastrow) {
408 lyx::Assert(!row->next());
411 if (refresh_row == row) {
412 refresh_row = row_prev ? row_prev : row->next();
413 // what about refresh_y, refresh_height
416 height -= row->height(); // the text becomes smaller
419 --number_of_rows; // one row less
423 // remove all following rows of the paragraph of the specified row.
424 void LyXText::removeParagraph(Row * row) const
426 Paragraph * tmppar = row->par();
430 while (row && row->par() == tmppar) {
431 tmprow = row->next();
438 // insert the specified paragraph behind the specified row
439 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
442 insertRow(row, par, 0); /* insert a new row, starting
445 setCounter(bview->buffer(), par); // set the counters
447 // and now append the whole paragraph behind the new row
450 appendParagraph(bview, firstrow);
452 row->next()->height(0);
453 appendParagraph(bview, row->next());
458 Inset * LyXText::getInset() const
461 if (cursor.pos() == 0 && cursor.par()->bibkey) {
462 inset = cursor.par()->bibkey;
463 } else if (cursor.pos() < cursor.par()->size()
464 && cursor.par()->isInset(cursor.pos())) {
465 inset = cursor.par()->getInset(cursor.pos());
471 void LyXText::toggleInset(BufferView * bview)
473 Inset * inset = getInset();
474 // is there an editable inset at cursor position?
475 if (!isEditableInset(inset)) {
476 // No, try to see if we are inside a collapsable inset
477 if (inset_owner && inset_owner->owner()
478 && inset_owner->owner()->isOpen()) {
479 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
480 inset_owner->owner()->close(bview);
484 //bview->owner()->message(inset->editMessage());
486 // do we want to keep this?? (JMarc)
487 if (!isHighlyEditableInset(inset))
488 setCursorParUndo(bview);
490 if (inset->isOpen()) {
496 inset->open(bview, !inset->isOpen());
501 /* used in setlayout */
502 // Asger is not sure we want to do this...
503 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
506 LyXLayout const & layout =
507 textclasslist.Style(buf->params.textclass, par->getLayout());
510 for (pos_type pos = 0; pos < par->size(); ++pos) {
511 if (pos < beginningOfMainBody(buf, par))
512 layoutfont = layout.labelfont;
514 layoutfont = layout.font;
516 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
517 tmpfont.reduce(layoutfont);
518 par->setFont(pos, tmpfont);
523 Paragraph * LyXText::setLayout(BufferView * bview,
524 LyXCursor & cur, LyXCursor & sstart_cur,
525 LyXCursor & send_cur,
526 lyx::layout_type layout)
528 Paragraph * endpar = send_cur.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 sstart_cur.par(), undoendpar);
543 // ok we have a selection. This is always between sstart_cur
544 // and sel_end cursor
547 LyXLayout const & lyxlayout =
548 textclasslist.Style(bview->buffer()->params.textclass, layout);
551 cur.par()->setLayout(layout);
552 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
553 Paragraph * fppar = cur.par();
554 fppar->params().spaceTop(lyxlayout.fill_top ?
555 VSpace(VSpace::VFILL)
556 : VSpace(VSpace::NONE));
557 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
558 VSpace(VSpace::VFILL)
559 : VSpace(VSpace::NONE));
560 if (lyxlayout.margintype == MARGIN_MANUAL)
561 cur.par()->setLabelWidthString(lyxlayout.labelstring());
562 if (lyxlayout.labeltype != LABEL_BIBLIO
564 delete fppar->bibkey;
567 if (cur.par() != send_cur.par())
568 cur.par(cur.par()->next());
569 } while (cur.par() != send_cur.par());
575 // set layout over selection and make a total rebreak of those paragraphs
576 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
578 LyXCursor tmpcursor = cursor; /* store the current cursor */
580 // if there is no selection just set the layout
581 // of the current paragraph */
582 if (!selection.set()) {
583 selection.start = cursor; // dummy selection
584 selection.end = cursor;
586 Paragraph * endpar = setLayout(bview, cursor, selection.start,
587 selection.end, layout);
588 redoParagraphs(bview, selection.start, endpar);
590 // we have to reset the selection, because the
591 // geometry could have changed
592 setCursor(bview, selection.start.par(),
593 selection.start.pos(), false);
594 selection.cursor = cursor;
595 setCursor(bview, selection.end.par(), selection.end.pos(), false);
596 updateCounters(bview, cursor.row());
599 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
603 // increment depth over selection and
604 // make a total rebreak of those paragraphs
605 void LyXText::incDepth(BufferView * bview)
607 // If there is no selection, just use the current paragraph
608 if (!selection.set()) {
609 selection.start = cursor; // dummy selection
610 selection.end = cursor;
613 // We end at the next paragraph with depth 0
614 Paragraph * endpar = selection.end.par()->next();
616 Paragraph * undoendpar = endpar;
618 if (endpar && endpar->getDepth()) {
619 while (endpar && endpar->getDepth()) {
620 endpar = endpar->next();
624 endpar = endpar->next(); // because of parindents etc.
627 setUndo(bview, Undo::EDIT,
628 selection.start.par(), undoendpar);
630 LyXCursor tmpcursor = cursor; // store the current cursor
632 // ok we have a selection. This is always between sel_start_cursor
633 // and sel_end cursor
634 cursor = selection.start;
636 bool anything_changed = false;
639 // NOTE: you can't change the depth of a bibliography entry
640 if (textclasslist.Style(bview->buffer()->params.textclass,
641 cursor.par()->getLayout()).labeltype != LABEL_BIBLIO) {
642 Paragraph * prev = cursor.par()->previous();
645 if (cursor.par()->getDepth()
646 < prev->getMaxDepthAfter(bview->buffer())){
647 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
648 anything_changed = true;
652 if (cursor.par() == selection.end.par())
654 cursor.par(cursor.par()->next());
657 // if nothing changed set all depth to 0
658 if (!anything_changed) {
659 cursor = selection.start;
660 while (cursor.par() != selection.end.par()) {
661 cursor.par()->params().depth(0);
662 cursor.par(cursor.par()->next());
664 cursor.par()->params().depth(0);
667 redoParagraphs(bview, selection.start, endpar);
669 // we have to reset the selection, because the
670 // geometry could have changed
671 setCursor(bview, selection.start.par(), selection.start.pos());
672 selection.cursor = cursor;
673 setCursor(bview, selection.end.par(), selection.end.pos());
674 updateCounters(bview, cursor.row());
677 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
681 // decrement depth over selection and
682 // make a total rebreak of those paragraphs
683 void LyXText::decDepth(BufferView * bview)
685 // if there is no selection just set the layout
686 // of the current paragraph
687 if (!selection.set()) {
688 selection.start = cursor; // dummy selection
689 selection.end = cursor;
691 Paragraph * endpar = selection.end.par()->next();
692 Paragraph * undoendpar = endpar;
694 if (endpar && endpar->getDepth()) {
695 while (endpar && endpar->getDepth()) {
696 endpar = endpar->next();
700 endpar = endpar->next(); // because of parindents etc.
703 setUndo(bview, Undo::EDIT,
704 selection.start.par(), undoendpar);
706 LyXCursor tmpcursor = cursor; // store the current cursor
708 // ok we have a selection. This is always between sel_start_cursor
709 // and sel_end cursor
710 cursor = selection.start;
713 if (cursor.par()->params().depth()) {
714 cursor.par()->params()
715 .depth(cursor.par()->params().depth() - 1);
717 if (cursor.par() == selection.end.par()) {
720 cursor.par(cursor.par()->next());
723 redoParagraphs(bview, selection.start, endpar);
725 // we have to reset the selection, because the
726 // geometry could have changed
727 setCursor(bview, selection.start.par(),
728 selection.start.pos());
729 selection.cursor = cursor;
730 setCursor(bview, selection.end.par(), selection.end.pos());
731 updateCounters(bview, cursor.row());
734 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
738 // set font over selection and make a total rebreak of those paragraphs
739 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
741 // if there is no selection just set the current_font
742 if (!selection.set()) {
743 // Determine basis font
745 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
747 layoutfont = getLabelFont(bview->buffer(),
750 layoutfont = getLayoutFont(bview->buffer(),
753 // Update current font
754 real_current_font.update(font,
755 bview->buffer()->params.language,
758 // Reduce to implicit settings
759 current_font = real_current_font;
760 current_font.reduce(layoutfont);
761 // And resolve it completely
762 #ifndef INHERIT_LANGUAGE
763 real_current_font.realize(layoutfont);
765 real_current_font.realize(layoutfont,
766 bview->buffer()->params.language);
771 LyXCursor tmpcursor = cursor; // store the current cursor
773 // ok we have a selection. This is always between sel_start_cursor
774 // and sel_end cursor
776 setUndo(bview, Undo::EDIT,
777 selection.start.par(), selection.end.par()->next());
779 cursor = selection.start;
780 while (cursor.par() != selection.end.par() ||
781 (cursor.pos() < selection.end.pos()))
783 if (cursor.pos() < cursor.par()->size()) {
784 // an open footnote should behave
786 setCharFont(bview, cursor.par(), cursor.pos(),
788 cursor.pos(cursor.pos() + 1);
791 cursor.par(cursor.par()->next());
796 redoParagraphs(bview, selection.start, selection.end.par()->next());
798 // we have to reset the selection, because the
799 // geometry could have changed, but we keep
800 // it for user convenience
801 setCursor(bview, selection.start.par(), selection.start.pos());
802 selection.cursor = cursor;
803 setCursor(bview, selection.end.par(), selection.end.pos());
805 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
806 tmpcursor.boundary());
810 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
812 Row * tmprow = cur.row();
813 int y = cur.y() - tmprow->baseline();
815 setHeightOfRow(bview, tmprow);
817 while (tmprow->previous()
818 && tmprow->previous()->par() == tmprow->par()) {
819 tmprow = tmprow->previous();
820 y -= tmprow->height();
821 setHeightOfRow(bview, tmprow);
824 // we can set the refreshing parameters now
825 status(bview, LyXText::NEED_MORE_REFRESH);
827 refresh_row = tmprow;
828 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
832 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
834 Row * tmprow = cur.row();
836 int y = cur.y() - tmprow->baseline();
837 setHeightOfRow(bview, tmprow);
839 while (tmprow->previous()
840 && tmprow->previous()->par() == tmprow->par()) {
841 tmprow = tmprow->previous();
842 y -= tmprow->height();
845 // we can set the refreshing parameters now
846 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
848 refresh_row = tmprow;
850 status(bview, LyXText::NEED_MORE_REFRESH);
851 setCursor(bview, cur.par(), cur.pos());
855 // deletes and inserts again all paragaphs between the cursor
856 // and the specified par
857 // This function is needed after SetLayout and SetFont etc.
858 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
859 Paragraph const * endpar) const
862 Paragraph * tmppar = 0;
863 Paragraph * first_phys_par = 0;
865 Row * tmprow = cur.row();
867 int y = cur.y() - tmprow->baseline();
869 if (!tmprow->previous()) {
870 // a trick/hack for UNDO
871 // This is needed because in an UNDO/REDO we could have changed
872 // the ownerParagrah() so the paragraph inside the row is NOT
873 // my really first par anymore. Got it Lars ;) (Jug 20011206)
874 first_phys_par = ownerParagraph();
876 first_phys_par = tmprow->par();
877 while (tmprow->previous()
878 && tmprow->previous()->par() == first_phys_par)
880 tmprow = tmprow->previous();
881 y -= tmprow->height();
885 // we can set the refreshing parameters now
886 status(bview, LyXText::NEED_MORE_REFRESH);
888 refresh_row = tmprow->previous(); /* the real refresh row will
889 be deleted, so I store
893 tmppar = tmprow->next()->par();
896 while (tmprow->next() && tmppar != endpar) {
897 removeRow(tmprow->next());
898 if (tmprow->next()) {
899 tmppar = tmprow->next()->par();
905 // remove the first one
906 tmprow2 = tmprow; /* this is because tmprow->previous()
908 tmprow = tmprow->previous();
911 tmppar = first_phys_par;
915 insertParagraph(bview, tmppar, tmprow);
919 while (tmprow->next()
920 && tmprow->next()->par() == tmppar) {
921 tmprow = tmprow->next();
923 tmppar = tmppar->next();
925 } while (tmppar && tmppar != endpar);
927 // this is because of layout changes
929 refresh_y -= refresh_row->height();
930 setHeightOfRow(bview, refresh_row);
932 refresh_row = firstrow;
934 setHeightOfRow(bview, refresh_row);
937 if (tmprow && tmprow->next())
938 setHeightOfRow(bview, tmprow->next());
942 bool LyXText::fullRebreak(BufferView * bview)
948 if (need_break_row) {
949 breakAgain(bview, need_break_row);
957 // important for the screen
960 /* the cursor set functions have a special mechanism. When they
961 * realize, that you left an empty paragraph, they will delete it.
962 * They also delete the corresponding row */
964 // need the selection cursor:
965 void LyXText::setSelection(BufferView * bview)
967 bool const lsel = selection.set();
969 if (!selection.set()) {
970 last_sel_cursor = selection.cursor;
971 selection.start = selection.cursor;
972 selection.end = selection.cursor;
977 // first the toggling area
978 if (cursor.y() < last_sel_cursor.y()
979 || (cursor.y() == last_sel_cursor.y()
980 && cursor.x() < last_sel_cursor.x())) {
981 toggle_end_cursor = last_sel_cursor;
982 toggle_cursor = cursor;
984 toggle_end_cursor = cursor;
985 toggle_cursor = last_sel_cursor;
988 last_sel_cursor = cursor;
990 // and now the whole selection
992 if (selection.cursor.par() == cursor.par())
993 if (selection.cursor.pos() < cursor.pos()) {
994 selection.end = cursor;
995 selection.start = selection.cursor;
997 selection.end = selection.cursor;
998 selection.start = cursor;
1000 else if (selection.cursor.y() < cursor.y() ||
1001 (selection.cursor.y() == cursor.y()
1002 && selection.cursor.x() < cursor.x())) {
1003 selection.end = cursor;
1004 selection.start = selection.cursor;
1007 selection.end = selection.cursor;
1008 selection.start = cursor;
1011 // a selection with no contents is not a selection
1012 if (selection.start.par() == selection.end.par() &&
1013 selection.start.pos() == selection.end.pos())
1014 selection.set(false);
1016 if (inset_owner && (selection.set() || lsel))
1017 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1021 string const LyXText::selectionAsString(Buffer const * buffer,
1024 if (!selection.set()) return string();
1027 // Special handling if the whole selection is within one paragraph
1028 if (selection.start.par() == selection.end.par()) {
1029 result += selection.start.par()->asString(buffer,
1030 selection.start.pos(),
1031 selection.end.pos(),
1036 // The selection spans more than one paragraph
1038 // First paragraph in selection
1039 result += selection.start.par()->asString(buffer,
1040 selection.start.pos(),
1041 selection.start.par()->size(),
1045 // The paragraphs in between (if any)
1046 LyXCursor tmpcur(selection.start);
1047 tmpcur.par(tmpcur.par()->next());
1048 while (tmpcur.par() != selection.end.par()) {
1049 result += tmpcur.par()->asString(buffer, 0,
1050 tmpcur.par()->size(),
1052 tmpcur.par(tmpcur.par()->next());
1055 // Last paragraph in selection
1056 result += selection.end.par()->asString(buffer, 0,
1057 selection.end.pos(), label);
1063 void LyXText::clearSelection() const
1065 selection.set(false);
1066 selection.mark(false);
1067 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1071 void LyXText::cursorHome(BufferView * bview) const
1073 setCursor(bview, cursor.par(), cursor.row()->pos());
1077 void LyXText::cursorEnd(BufferView * bview) const
1079 if (!cursor.row()->next()
1080 || cursor.row()->next()->par() != cursor.row()->par()) {
1081 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1083 if (cursor.par()->size() &&
1084 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1085 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1086 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1088 setCursor(bview,cursor.par(),
1089 rowLast(cursor.row()) + 1);
1095 void LyXText::cursorTop(BufferView * bview) const
1097 while (cursor.par()->previous())
1098 cursor.par(cursor.par()->previous());
1099 setCursor(bview, cursor.par(), 0);
1103 void LyXText::cursorBottom(BufferView * bview) const
1105 while (cursor.par()->next())
1106 cursor.par(cursor.par()->next());
1107 setCursor(bview, cursor.par(), cursor.par()->size());
1111 void LyXText::toggleFree(BufferView * bview,
1112 LyXFont const & font, bool toggleall)
1114 // If the mask is completely neutral, tell user
1115 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1116 // Could only happen with user style
1117 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1121 // Try implicit word selection
1122 // If there is a change in the language the implicit word selection
1124 LyXCursor resetCursor = cursor;
1125 bool implicitSelection = (font.language() == ignore_language
1126 && font.number() == LyXFont::IGNORE)
1127 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1130 setFont(bview, font, toggleall);
1132 // Implicit selections are cleared afterwards
1133 //and cursor is set to the original position.
1134 if (implicitSelection) {
1136 cursor = resetCursor;
1137 setCursor(bview, cursor.par(), cursor.pos());
1138 selection.cursor = cursor;
1141 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1145 string LyXText::getStringToIndex(BufferView * bview)
1149 // Try implicit word selection
1150 // If there is a change in the language the implicit word selection
1152 LyXCursor resetCursor = cursor;
1153 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1155 if (!selection.set()) {
1156 bview->owner()->message(_("Nothing to index!"));
1159 if (selection.start.par() != selection.end.par()) {
1160 bview->owner()->message(_("Cannot index more than one paragraph!"));
1164 idxstring = selectionAsString(bview->buffer(), false);
1166 // Implicit selections are cleared afterwards
1167 //and cursor is set to the original position.
1168 if (implicitSelection) {
1170 cursor = resetCursor;
1171 setCursor(bview, cursor.par(), cursor.pos());
1172 selection.cursor = cursor;
1178 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1179 Paragraph const * par) const
1181 if (textclasslist.Style(buf->params.textclass,
1182 par->getLayout()).labeltype != LABEL_MANUAL)
1185 return par->beginningOfMainBody();
1189 /* the DTP switches for paragraphs. LyX will store them in the
1190 * first physicla paragraph. When a paragraph is broken, the top settings
1191 * rest, the bottom settings are given to the new one. So I can make shure,
1192 * they do not duplicate themself and you cannnot make dirty things with
1195 void LyXText::setParagraph(BufferView * bview,
1196 bool line_top, bool line_bottom,
1197 bool pagebreak_top, bool pagebreak_bottom,
1198 VSpace const & space_top,
1199 VSpace const & space_bottom,
1200 Spacing const & spacing,
1202 string labelwidthstring,
1205 LyXCursor tmpcursor = cursor;
1206 if (!selection.set()) {
1207 selection.start = cursor;
1208 selection.end = cursor;
1211 // make sure that the depth behind the selection are restored, too
1212 Paragraph * endpar = selection.end.par()->next();
1213 Paragraph * undoendpar = endpar;
1215 if (endpar && endpar->getDepth()) {
1216 while (endpar && endpar->getDepth()) {
1217 endpar = endpar->next();
1218 undoendpar = endpar;
1222 // because of parindents etc.
1223 endpar = endpar->next();
1226 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1229 Paragraph * tmppar = selection.end.par();
1230 while (tmppar != selection.start.par()->previous()) {
1231 setCursor(bview, tmppar, 0);
1232 status(bview, LyXText::NEED_MORE_REFRESH);
1233 refresh_row = cursor.row();
1234 refresh_y = cursor.y() - cursor.row()->baseline();
1235 cursor.par()->params().lineTop(line_top);
1236 cursor.par()->params().lineBottom(line_bottom);
1237 cursor.par()->params().pagebreakTop(pagebreak_top);
1238 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1239 cursor.par()->params().spaceTop(space_top);
1240 cursor.par()->params().spaceBottom(space_bottom);
1241 cursor.par()->params().spacing(spacing);
1242 // does the layout allow the new alignment?
1243 if (align == LYX_ALIGN_LAYOUT)
1244 align = textclasslist
1245 .Style(bview->buffer()->params.textclass,
1246 cursor.par()->getLayout()).align;
1247 if (align & textclasslist
1248 .Style(bview->buffer()->params.textclass,
1249 cursor.par()->getLayout()).alignpossible) {
1250 if (align == textclasslist
1251 .Style(bview->buffer()->params.textclass,
1252 cursor.par()->getLayout()).align)
1253 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1255 cursor.par()->params().align(align);
1257 cursor.par()->setLabelWidthString(labelwidthstring);
1258 cursor.par()->params().noindent(noindent);
1259 tmppar = cursor.par()->previous();
1262 redoParagraphs(bview, selection.start, endpar);
1265 setCursor(bview, selection.start.par(), selection.start.pos());
1266 selection.cursor = cursor;
1267 setCursor(bview, selection.end.par(), selection.end.pos());
1268 setSelection(bview);
1269 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1271 bview->updateInset(inset_owner, true);
1275 char loweralphaCounter(int n)
1277 if (n < 1 || n > 26)
1287 char alphaCounter(int n)
1289 if (n < 1 || n > 26)
1297 char hebrewCounter(int n)
1299 static const char hebrew[22] = {
1300 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1301 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1302 '÷', 'ø', 'ù', 'ú'
1304 if (n < 1 || n > 22)
1312 string const romanCounter(int n)
1314 static char const * roman[20] = {
1315 "i", "ii", "iii", "iv", "v",
1316 "vi", "vii", "viii", "ix", "x",
1317 "xi", "xii", "xiii", "xiv", "xv",
1318 "xvi", "xvii", "xviii", "xix", "xx"
1320 if (n < 1 || n > 20)
1329 // set the counter of a paragraph. This includes the labels
1330 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1332 LyXLayout const & layout =
1333 textclasslist.Style(buf->params.textclass,
1336 LyXTextClass const & textclass =
1337 textclasslist.TextClass(buf->params.textclass);
1339 // copy the prev-counters to this one,
1340 // unless this is the first paragraph
1341 if (par->previous()) {
1342 for (int i = 0; i < 10; ++i) {
1343 par->setCounter(i, par->previous()->getFirstCounter(i));
1345 par->params().appendix(par->previous()->params().appendix());
1346 if (!par->params().appendix() && par->params().startOfAppendix()) {
1347 par->params().appendix(true);
1348 for (int i = 0; i < 10; ++i) {
1349 par->setCounter(i, 0);
1352 par->enumdepth = par->previous()->enumdepth;
1353 par->itemdepth = par->previous()->itemdepth;
1355 for (int i = 0; i < 10; ++i) {
1356 par->setCounter(i, 0);
1358 par->params().appendix(par->params().startOfAppendix());
1363 /* Maybe we have to increment the enumeration depth.
1364 * BUT, enumeration in a footnote is considered in isolation from its
1365 * surrounding paragraph so don't increment if this is the
1366 * first line of the footnote
1367 * AND, bibliographies can't have their depth changed ie. they
1368 * are always of depth 0
1371 && par->previous()->getDepth() < par->getDepth()
1372 && textclasslist.Style(buf->params.textclass,
1373 par->previous()->getLayout()
1374 ).labeltype == LABEL_COUNTER_ENUMI
1375 && par->enumdepth < 3
1376 && layout.labeltype != LABEL_BIBLIO) {
1380 // Maybe we have to decrement the enumeration depth, see note above
1382 && par->previous()->getDepth() > par->getDepth()
1383 && layout.labeltype != LABEL_BIBLIO) {
1384 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1385 par->setCounter(6 + par->enumdepth,
1386 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1387 /* reset the counters.
1388 * A depth change is like a breaking layout
1390 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1391 par->setCounter(i, 0);
1394 if (!par->params().labelString().empty()) {
1395 par->params().labelString(string());
1398 if (layout.margintype == MARGIN_MANUAL) {
1399 if (par->params().labelWidthString().empty()) {
1400 par->setLabelWidthString(layout.labelstring());
1403 par->setLabelWidthString(string());
1406 // is it a layout that has an automatic label?
1407 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1409 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1410 if (i >= 0 && i<= buf->params.secnumdepth) {
1411 par->incCounter(i); // increment the counter
1413 // Is there a label? Useful for Chapter layout
1414 if (!par->params().appendix()) {
1415 if (!layout.labelstring().empty())
1416 par->params().labelString(layout.labelstring());
1418 par->params().labelString(string());
1420 if (!layout.labelstring_appendix().empty())
1421 par->params().labelString(layout.labelstring_appendix());
1423 par->params().labelString(string());
1428 if (!par->params().appendix()) {
1429 switch (2 * LABEL_COUNTER_CHAPTER -
1430 textclass.maxcounter() + i) {
1431 case LABEL_COUNTER_CHAPTER:
1432 s << par->getCounter(i);
1434 case LABEL_COUNTER_SECTION:
1435 s << par->getCounter(i - 1) << '.'
1436 << par->getCounter(i);
1438 case LABEL_COUNTER_SUBSECTION:
1439 s << par->getCounter(i - 2) << '.'
1440 << par->getCounter(i - 1) << '.'
1441 << par->getCounter(i);
1443 case LABEL_COUNTER_SUBSUBSECTION:
1444 s << par->getCounter(i - 3) << '.'
1445 << par->getCounter(i - 2) << '.'
1446 << par->getCounter(i - 1) << '.'
1447 << par->getCounter(i);
1450 case LABEL_COUNTER_PARAGRAPH:
1451 s << par->getCounter(i - 4) << '.'
1452 << par->getCounter(i - 3) << '.'
1453 << par->getCounter(i - 2) << '.'
1454 << par->getCounter(i - 1) << '.'
1455 << par->getCounter(i);
1457 case LABEL_COUNTER_SUBPARAGRAPH:
1458 s << par->getCounter(i - 5) << '.'
1459 << par->getCounter(i - 4) << '.'
1460 << par->getCounter(i - 3) << '.'
1461 << par->getCounter(i - 2) << '.'
1462 << par->getCounter(i - 1) << '.'
1463 << par->getCounter(i);
1467 // Can this ever be reached? And in the
1468 // case it is, how can this be correct?
1470 s << par->getCounter(i) << '.';
1473 } else { // appendix
1474 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1475 case LABEL_COUNTER_CHAPTER:
1476 if (par->isRightToLeftPar(buf->params))
1477 s << hebrewCounter(par->getCounter(i));
1479 s << alphaCounter(par->getCounter(i));
1481 case LABEL_COUNTER_SECTION:
1482 if (par->isRightToLeftPar(buf->params))
1483 s << hebrewCounter(par->getCounter(i - 1));
1485 s << alphaCounter(par->getCounter(i - 1));
1488 << par->getCounter(i);
1491 case LABEL_COUNTER_SUBSECTION:
1492 if (par->isRightToLeftPar(buf->params))
1493 s << hebrewCounter(par->getCounter(i - 2));
1495 s << alphaCounter(par->getCounter(i - 2));
1498 << par->getCounter(i-1) << '.'
1499 << par->getCounter(i);
1502 case LABEL_COUNTER_SUBSUBSECTION:
1503 if (par->isRightToLeftPar(buf->params))
1504 s << hebrewCounter(par->getCounter(i-3));
1506 s << alphaCounter(par->getCounter(i-3));
1509 << par->getCounter(i-2) << '.'
1510 << par->getCounter(i-1) << '.'
1511 << par->getCounter(i);
1514 case LABEL_COUNTER_PARAGRAPH:
1515 if (par->isRightToLeftPar(buf->params))
1516 s << hebrewCounter(par->getCounter(i-4));
1518 s << alphaCounter(par->getCounter(i-4));
1521 << par->getCounter(i-3) << '.'
1522 << par->getCounter(i-2) << '.'
1523 << par->getCounter(i-1) << '.'
1524 << par->getCounter(i);
1527 case LABEL_COUNTER_SUBPARAGRAPH:
1528 if (par->isRightToLeftPar(buf->params))
1529 s << hebrewCounter(par->getCounter(i-5));
1531 s << alphaCounter(par->getCounter(i-5));
1534 << par->getCounter(i-4) << '.'
1535 << par->getCounter(i-3) << '.'
1536 << par->getCounter(i-2) << '.'
1537 << par->getCounter(i-1) << '.'
1538 << par->getCounter(i);
1542 // Can this ever be reached? And in the
1543 // case it is, how can this be correct?
1545 s << par->getCounter(i) << '.';
1551 par->params().labelString(par->params().labelString() +s.str().c_str());
1552 // We really want to remove the c_str as soon as
1555 for (i++; i < 10; ++i) {
1556 // reset the following counters
1557 par->setCounter(i, 0);
1559 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1560 for (i++; i < 10; ++i) {
1561 // reset the following counters
1562 par->setCounter(i, 0);
1564 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1565 par->incCounter(i + par->enumdepth);
1566 int number = par->getCounter(i + par->enumdepth);
1570 switch (par->enumdepth) {
1572 if (par->isRightToLeftPar(buf->params))
1574 << hebrewCounter(number)
1578 << loweralphaCounter(number)
1582 if (par->isRightToLeftPar(buf->params))
1583 s << '.' << romanCounter(number);
1585 s << romanCounter(number) << '.';
1588 if (par->isRightToLeftPar(buf->params))
1590 << alphaCounter(number);
1592 s << alphaCounter(number)
1596 if (par->isRightToLeftPar(buf->params))
1603 par->params().labelString(s.str().c_str());
1605 for (i += par->enumdepth + 1; i < 10; ++i) {
1606 // reset the following counters
1607 par->setCounter(i, 0);
1611 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1612 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1614 int number = par->getCounter(i);
1616 InsetCommandParams p("bibitem" );
1617 par->bibkey = new InsetBibKey(p);
1619 par->bibkey->setCounter(number);
1620 par->params().labelString(layout.labelstring());
1622 // In biblio should't be following counters but...
1624 string s = layout.labelstring();
1626 // the caption hack:
1627 if (layout.labeltype == LABEL_SENSITIVE) {
1628 bool isOK (par->inInset() && par->inInset()->owner() &&
1629 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1632 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1634 = floatList.getType(tmp->type());
1635 // We should get the correct number here too.
1636 s = fl.name() + " #:";
1638 /* par->SetLayout(0);
1639 s = layout->labelstring; */
1640 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1641 ? " :úåòîùî øñç" : "Senseless: ";
1644 par->params().labelString(s);
1646 /* reset the enumeration counter. They are always resetted
1647 * when there is any other layout between */
1648 for (int i = 6 + par->enumdepth; i < 10; ++i)
1649 par->setCounter(i, 0);
1654 // Updates all counters BEHIND the row. Changed paragraphs
1655 // with a dynamic left margin will be rebroken.
1656 void LyXText::updateCounters(BufferView * bview, Row * row) const
1664 par = row->par()->next();
1668 while (row->par() != par)
1671 setCounter(bview->buffer(), par);
1673 // now check for the headline layouts. remember that they
1674 // have a dynamic left margin
1675 if ((textclasslist.Style(bview->buffer()->params.textclass,
1676 par->layout).margintype == MARGIN_DYNAMIC
1677 || textclasslist.Style(bview->buffer()->params.textclass,
1678 par->layout).labeltype == LABEL_SENSITIVE)) {
1680 // Rebreak the paragraph
1681 removeParagraph(row);
1682 appendParagraph(bview, row);
1689 void LyXText::insertInset(BufferView * bview, Inset * inset)
1691 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1693 // I don't know if this is necessary here (Jug 20020102)
1694 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1695 cursor.par()->insertInset(cursor.pos(), inset);
1696 // Just to rebreak and refresh correctly.
1697 // The character will not be inserted a second time
1698 insertChar(bview, Paragraph::META_INSET);
1700 // If we enter a highly editable inset the cursor should be to before
1701 // the inset. This couldn't happen before as Undo was not handled inside
1702 // inset now after the Undo LyX tries to call inset->Edit(...) again
1703 // and cannot do this as the cursor is behind the inset and GetInset
1704 // does not return the inset!
1705 if (isHighlyEditableInset(inset)) {
1706 cursorLeft(bview, true);
1712 void LyXText::copyEnvironmentType()
1714 copylayouttype = cursor.par()->getLayout();
1718 void LyXText::pasteEnvironmentType(BufferView * bview)
1720 setLayout(bview, copylayouttype);
1724 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1726 // Stuff what we got on the clipboard. Even if there is no selection.
1728 // There is a problem with having the stuffing here in that the
1729 // larger the selection the slower LyX will get. This can be
1730 // solved by running the line below only when the selection has
1731 // finished. The solution used currently just works, to make it
1732 // faster we need to be more clever and probably also have more
1733 // calls to stuffClipboard. (Lgb)
1734 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1736 // This doesn't make sense, if there is no selection
1737 if (!selection.set())
1740 // OK, we have a selection. This is always between selection.start
1741 // and selection.end
1743 // make sure that the depth behind the selection are restored, too
1744 Paragraph * endpar = selection.end.par()->next();
1745 Paragraph * undoendpar = endpar;
1747 if (endpar && endpar->getDepth()) {
1748 while (endpar && endpar->getDepth()) {
1749 endpar = endpar->next();
1750 undoendpar = endpar;
1752 } else if (endpar) {
1753 endpar = endpar->next(); // because of parindents etc.
1756 setUndo(bview, Undo::DELETE,
1757 selection.start.par(), undoendpar);
1759 // there are two cases: cut only within one paragraph or
1760 // more than one paragraph
1761 if (selection.start.par() == selection.end.par()) {
1762 // only within one paragraph
1763 endpar = selection.end.par();
1764 int pos = selection.end.pos();
1765 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1766 selection.start.pos(), pos,
1767 bview->buffer()->params.textclass,
1769 selection.end.pos(pos);
1771 endpar = selection.end.par();
1772 int pos = selection.end.pos();
1773 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1774 selection.start.pos(), pos,
1775 bview->buffer()->params.textclass,
1778 selection.end.par(endpar);
1779 selection.end.pos(pos);
1780 cursor.pos(selection.end.pos());
1782 endpar = endpar->next();
1784 // sometimes necessary
1786 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1788 redoParagraphs(bview, selection.start, endpar);
1790 // cutSelection can invalidate the cursor so we need to set
1792 cursor = selection.start;
1794 // need a valid cursor. (Lgb)
1797 setCursor(bview, cursor.par(), cursor.pos());
1798 selection.cursor = cursor;
1799 updateCounters(bview, cursor.row());
1803 void LyXText::copySelection(BufferView * bview)
1805 // stuff the selection onto the X clipboard, from an explicit copy request
1806 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1808 // this doesnt make sense, if there is no selection
1809 if (!selection.set())
1812 // ok we have a selection. This is always between selection.start
1813 // and sel_end cursor
1815 // copy behind a space if there is one
1816 while (selection.start.par()->size() > selection.start.pos()
1817 && selection.start.par()->isLineSeparator(selection.start.pos())
1818 && (selection.start.par() != selection.end.par()
1819 || selection.start.pos() < selection.end.pos()))
1820 selection.start.pos(selection.start.pos() + 1);
1822 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1823 selection.start.pos(), selection.end.pos(),
1824 bview->buffer()->params.textclass);
1828 void LyXText::pasteSelection(BufferView * bview)
1830 // this does not make sense, if there is nothing to paste
1831 if (!CutAndPaste::checkPastePossible(cursor.par()))
1834 setUndo(bview, Undo::INSERT,
1835 cursor.par(), cursor.par()->next());
1838 Paragraph * actpar = cursor.par();
1839 int pos = cursor.pos();
1841 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1842 bview->buffer()->params.textclass);
1844 redoParagraphs(bview, cursor, endpar);
1846 setCursor(bview, cursor.par(), cursor.pos());
1849 setCursor(bview, actpar, pos);
1850 updateCounters(bview, cursor.row());
1854 // sets the selection over the number of characters of string, no check!!
1855 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1860 selection.cursor = cursor;
1861 for (string::size_type i = 0; i < str.length(); ++i)
1863 setSelection(bview);
1867 // simple replacing. The font of the first selected character is used
1868 void LyXText::replaceSelectionWithString(BufferView * bview,
1871 setCursorParUndo(bview);
1874 if (!selection.set()) { // create a dummy selection
1875 selection.end = cursor;
1876 selection.start = cursor;
1879 // Get font setting before we cut
1880 pos_type pos = selection.end.pos();
1881 LyXFont const font = selection.start.par()
1882 ->getFontSettings(bview->buffer()->params,
1883 selection.start.pos());
1885 // Insert the new string
1886 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1887 selection.end.par()->insertChar(pos, (*cit), font);
1891 // Cut the selection
1892 cutSelection(bview, true, false);
1898 // needed to insert the selection
1899 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1901 Paragraph * par = cursor.par();
1902 pos_type pos = cursor.pos();
1903 Paragraph * endpar = cursor.par()->next();
1905 setCursorParUndo(bview);
1907 // only to be sure, should not be neccessary
1910 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1912 redoParagraphs(bview, cursor, endpar);
1913 setCursor(bview, cursor.par(), cursor.pos());
1914 selection.cursor = cursor;
1915 setCursor(bview, par, pos);
1916 setSelection(bview);
1920 // turns double-CR to single CR, others where converted into one
1921 // blank. Then InsertStringAsLines is called
1922 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1924 string linestr(str);
1925 bool newline_inserted = false;
1926 for (string::size_type i = 0; i < linestr.length(); ++i) {
1927 if (linestr[i] == '\n') {
1928 if (newline_inserted) {
1929 // we know that \r will be ignored by
1930 // InsertStringA. Of course, it is a dirty
1931 // trick, but it works...
1932 linestr[i - 1] = '\r';
1936 newline_inserted = true;
1938 } else if (IsPrintable(linestr[i])) {
1939 newline_inserted = false;
1942 insertStringAsLines(bview, linestr);
1946 bool LyXText::gotoNextInset(BufferView * bview,
1947 vector<Inset::Code> const & codes,
1948 string const & contents) const
1950 LyXCursor res = cursor;
1953 if (res.pos() < res.par()->size() - 1) {
1954 res.pos(res.pos() + 1);
1956 res.par(res.par()->next());
1960 } while (res.par() &&
1961 !(res.par()->isInset(res.pos())
1962 && (inset = res.par()->getInset(res.pos())) != 0
1963 && find(codes.begin(), codes.end(), inset->lyxCode())
1965 && (contents.empty() ||
1966 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1970 setCursor(bview, res.par(), res.pos(), false);
1977 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1980 LyXCursor tmpcursor;
1984 Row * row = getRow(par, pos, y);
1986 // is there a break one row above
1987 if (row->previous() && row->previous()->par() == row->par()) {
1988 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1989 if (z >= row->pos()) {
1990 // set the dimensions of the row above
1991 y -= row->previous()->height();
1993 refresh_row = row->previous();
1994 status(bview, LyXText::NEED_MORE_REFRESH);
1996 breakAgain(bview, row->previous());
1998 // set the cursor again. Otherwise
1999 // dangling pointers are possible
2000 setCursor(bview, cursor.par(), cursor.pos(),
2001 false, cursor.boundary());
2002 selection.cursor = cursor;
2007 int const tmpheight = row->height();
2008 pos_type const tmplast = rowLast(row);
2012 breakAgain(bview, row);
2013 if (row->height() == tmpheight && rowLast(row) == tmplast)
2014 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2016 status(bview, LyXText::NEED_MORE_REFRESH);
2018 // check the special right address boxes
2019 if (textclasslist.Style(bview->buffer()->params.textclass,
2020 par->getLayout()).margintype
2021 == MARGIN_RIGHT_ADDRESS_BOX)
2029 redoDrawingOfParagraph(bview, tmpcursor);
2032 // set the cursor again. Otherwise dangling pointers are possible
2033 // also set the selection
2035 if (selection.set()) {
2037 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2038 false, selection.cursor.boundary());
2039 selection.cursor = cursor;
2040 setCursorIntern(bview, selection.start.par(),
2041 selection.start.pos(),
2042 false, selection.start.boundary());
2043 selection.start = cursor;
2044 setCursorIntern(bview, selection.end.par(),
2045 selection.end.pos(),
2046 false, selection.end.boundary());
2047 selection.end = cursor;
2048 setCursorIntern(bview, last_sel_cursor.par(),
2049 last_sel_cursor.pos(),
2050 false, last_sel_cursor.boundary());
2051 last_sel_cursor = cursor;
2054 setCursorIntern(bview, cursor.par(), cursor.pos(),
2055 false, cursor.boundary());
2059 // returns false if inset wasn't found
2060 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2062 // first check the current paragraph
2063 int pos = cursor.par()->getPositionOfInset(inset);
2065 checkParagraph(bview, cursor.par(), pos);
2069 // check every paragraph
2071 Paragraph * par = ownerParagraph();
2073 pos = par->getPositionOfInset(inset);
2075 checkParagraph(bview, par, pos);
2085 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2087 bool setfont, bool boundary) const
2089 LyXCursor old_cursor = cursor;
2090 setCursorIntern(bview, par, pos, setfont, boundary);
2091 return deleteEmptyParagraphMechanism(bview, old_cursor);
2095 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2096 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) {
2124 // This shouldn't happen.
2127 } else if (pos < row->pos()) {
2132 if (last < row->pos())
2133 cursor_vpos = row->pos();
2134 else if (pos > last && !boundary)
2135 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2136 ? row->pos() : last + 1;
2137 else if (pos > row->pos() &&
2138 (pos > last || boundary))
2139 /// Place cursor after char at (logical) position pos - 1
2140 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2141 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2143 /// Place cursor before char at (logical) position pos
2144 cursor_vpos = (bidi_level(pos) % 2 == 0)
2145 ? log2vis(pos) : log2vis(pos) + 1;
2147 pos_type main_body =
2148 beginningOfMainBody(bview->buffer(), row->par());
2149 if ((main_body > 0) &&
2150 ((main_body-1 > last) ||
2151 !row->par()->isLineSeparator(main_body-1)))
2154 for (pos_type vpos = row->pos();
2155 vpos < cursor_vpos; ++vpos) {
2156 pos = vis2log(vpos);
2157 if (main_body > 0 && pos == main_body - 1) {
2158 x += fill_label_hfill +
2159 lyxfont::width(textclasslist.Style(
2160 bview->buffer()->params.textclass,
2161 row->par()->getLayout())
2163 getLabelFont(bview->buffer(), row->par()));
2164 if (row->par()->isLineSeparator(main_body-1))
2165 x -= singleWidth(bview, row->par(),main_body-1);
2167 if (hfillExpansion(bview->buffer(), row, pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2172 x += fill_label_hfill;
2173 } else if (row->par()->isSeparator(pos)) {
2174 x += singleWidth(bview, row->par(), pos);
2175 if (pos >= main_body)
2176 x += fill_separator;
2178 x += singleWidth(bview, row->par(), pos);
2187 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2188 pos_type pos, bool setfont, bool boundary) const
2190 InsetText * it = static_cast<InsetText *>(par->inInset());
2192 if (it != inset_owner) {
2193 lyxerr << "InsetText is " << it << endl;
2194 lyxerr << "inset_owner is " << inset_owner << endl;
2195 #ifdef WITH_WARNINGS
2196 #warning I believe this code is wrong. (Lgb)
2197 #warning Jürgen, have a look at this. (Lgb)
2198 #warning Hmmm, I guess you are right but we
2199 #warning should verify when this is needed
2201 // Jürgen, would you like to have a look?
2202 // I guess we need to move the outer cursor
2203 // and open and lock the inset (bla bla bla)
2204 // stuff I don't know... so can you have a look?
2206 // I moved the lyxerr stuff in here so we can see if
2207 // this is actually really needed and where!
2209 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2214 setCursor(bview, cursor, par, pos, boundary);
2216 setCurrentFont(bview);
2220 void LyXText::setCurrentFont(BufferView * bview) const
2222 pos_type pos = cursor.pos();
2223 if (cursor.boundary() && pos > 0)
2227 if (pos == cursor.par()->size())
2229 else // potentional bug... BUG (Lgb)
2230 if (cursor.par()->isSeparator(pos)) {
2231 if (pos > cursor.row()->pos() &&
2232 bidi_level(pos) % 2 ==
2233 bidi_level(pos - 1) % 2)
2235 else if (pos + 1 < cursor.par()->size())
2241 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2242 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2244 if (cursor.pos() == cursor.par()->size() &&
2245 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2246 !cursor.boundary()) {
2247 Language const * lang =
2248 cursor.par()->getParLanguage(bview->buffer()->params);
2249 current_font.setLanguage(lang);
2250 current_font.setNumber(LyXFont::OFF);
2251 real_current_font.setLanguage(lang);
2252 real_current_font.setNumber(LyXFont::OFF);
2257 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2259 LyXCursor old_cursor = cursor;
2261 setCursorFromCoordinates(bview, cursor, x, y);
2262 setCurrentFont(bview);
2263 deleteEmptyParagraphMechanism(bview, old_cursor);
2267 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2270 // Get the row first.
2272 Row * row = getRowNearY(y);
2274 pos_type const column = getColumnNearX(bview, row, x, bound);
2275 cur.par(row->par());
2276 cur.pos(row->pos() + column);
2278 cur.y(y + row->baseline());
2280 cur.boundary(bound);
2284 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2286 if (cursor.pos() > 0) {
2287 bool boundary = cursor.boundary();
2288 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2289 if (!internal && !boundary &&
2290 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2291 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2292 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2293 Paragraph * par = cursor.par()->previous();
2294 setCursor(bview, par, par->size());
2299 void LyXText::cursorRight(BufferView * bview, bool internal) const
2301 if (!internal && cursor.boundary() &&
2302 !cursor.par()->isNewline(cursor.pos()))
2303 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2304 else if (cursor.pos() < cursor.par()->size()) {
2305 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2307 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2308 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2309 } else if (cursor.par()->next())
2310 setCursor(bview, cursor.par()->next(), 0);
2314 void LyXText::cursorUp(BufferView * bview) const
2316 setCursorFromCoordinates(bview, cursor.x_fix(),
2317 cursor.y() - cursor.row()->baseline() - 1);
2321 void LyXText::cursorDown(BufferView * bview) const
2323 setCursorFromCoordinates(bview, cursor.x_fix(),
2324 cursor.y() - cursor.row()->baseline()
2325 + cursor.row()->height() + 1);
2329 void LyXText::cursorUpParagraph(BufferView * bview) const
2331 if (cursor.pos() > 0) {
2332 setCursor(bview, cursor.par(), 0);
2334 else if (cursor.par()->previous()) {
2335 setCursor(bview, cursor.par()->previous(), 0);
2340 void LyXText::cursorDownParagraph(BufferView * bview) const
2342 if (cursor.par()->next()) {
2343 setCursor(bview, cursor.par()->next(), 0);
2345 setCursor(bview, cursor.par(), cursor.par()->size());
2349 // fix the cursor `cur' after a characters has been deleted at `where'
2350 // position. Called by deleteEmptyParagraphMechanism
2351 void LyXText::fixCursorAfterDelete(BufferView * bview,
2353 LyXCursor const & where) const
2355 // if cursor is not in the paragraph where the delete occured,
2357 if (cur.par() != where.par())
2360 // if cursor position is after the place where the delete occured,
2362 if (cur.pos() > where.pos())
2363 cur.pos(cur.pos()-1);
2365 // recompute row et al. for this cursor
2366 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2370 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2371 LyXCursor const & old_cursor) const
2373 // Would be wrong to delete anything if we have a selection.
2374 if (selection.set()) return false;
2376 // We allow all kinds of "mumbo-jumbo" when freespacing.
2377 if (textclasslist.Style(bview->buffer()->params.textclass,
2378 old_cursor.par()->getLayout()).free_spacing
2379 || old_cursor.par()->isFreeSpacing())
2384 /* Ok I'll put some comments here about what is missing.
2385 I have fixed BackSpace (and thus Delete) to not delete
2386 double-spaces automagically. I have also changed Cut,
2387 Copy and Paste to hopefully do some sensible things.
2388 There are still some small problems that can lead to
2389 double spaces stored in the document file or space at
2390 the beginning of paragraphs. This happens if you have
2391 the cursor betwenn to spaces and then save. Or if you
2392 cut and paste and the selection have a space at the
2393 beginning and then save right after the paste. I am
2394 sure none of these are very hard to fix, but I will
2395 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2396 that I can get some feedback. (Lgb)
2399 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2400 // delete the LineSeparator.
2403 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2404 // delete the LineSeparator.
2407 // If the pos around the old_cursor were spaces, delete one of them.
2408 if (old_cursor.par() != cursor.par()
2409 || old_cursor.pos() != cursor.pos()) {
2410 // Only if the cursor has really moved
2412 if (old_cursor.pos() > 0
2413 && old_cursor.pos() < old_cursor.par()->size()
2414 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2415 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2416 old_cursor.par()->erase(old_cursor.pos() - 1);
2417 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2419 #ifdef WITH_WARNINGS
2420 #warning This will not work anymore when we have multiple views of the same buffer
2421 // In this case, we will have to correct also the cursors held by
2422 // other bufferviews. It will probably be easier to do that in a more
2423 // automated way in LyXCursor code. (JMarc 26/09/2001)
2425 // correct all cursors held by the LyXText
2426 fixCursorAfterDelete(bview, cursor, old_cursor);
2427 fixCursorAfterDelete(bview, selection.cursor,
2429 fixCursorAfterDelete(bview, selection.start,
2431 fixCursorAfterDelete(bview, selection.end, old_cursor);
2432 fixCursorAfterDelete(bview, last_sel_cursor,
2434 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2435 fixCursorAfterDelete(bview, toggle_end_cursor,
2441 // don't delete anything if this is the ONLY paragraph!
2442 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2445 // Do not delete empty paragraphs with keepempty set.
2446 if ((textclasslist.Style(bview->buffer()->params.textclass,
2447 old_cursor.par()->getLayout())).keepempty)
2450 // only do our magic if we changed paragraph
2451 if (old_cursor.par() == cursor.par())
2454 // record if we have deleted a paragraph
2455 // we can't possibly have deleted a paragraph before this point
2456 bool deleted = false;
2458 if ((old_cursor.par()->size() == 0
2459 || (old_cursor.par()->size() == 1
2460 && old_cursor.par()->isLineSeparator(0)))) {
2461 // ok, we will delete anything
2462 LyXCursor tmpcursor;
2464 // make sure that you do not delete any environments
2465 status(bview, LyXText::NEED_MORE_REFRESH);
2468 if (old_cursor.row()->previous()) {
2469 refresh_row = old_cursor.row()->previous();
2470 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2472 cursor = old_cursor; // that undo can restore the right cursor position
2473 Paragraph * endpar = old_cursor.par()->next();
2474 if (endpar && endpar->getDepth()) {
2475 while (endpar && endpar->getDepth()) {
2476 endpar = endpar->next();
2479 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2483 removeRow(old_cursor.row());
2484 if (ownerParagraph() == old_cursor.par()) {
2485 ownerParagraph(ownerParagraph()->next());
2488 delete old_cursor.par();
2490 /* Breakagain the next par. Needed because of
2491 * the parindent that can occur or dissappear.
2492 * The next row can change its height, if
2493 * there is another layout before */
2494 if (refresh_row->next()) {
2495 breakAgain(bview, refresh_row->next());
2496 updateCounters(bview, refresh_row);
2498 setHeightOfRow(bview, refresh_row);
2500 refresh_row = old_cursor.row()->next();
2501 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2504 cursor = old_cursor; // that undo can restore the right cursor position
2505 Paragraph * endpar = old_cursor.par()->next();
2506 if (endpar && endpar->getDepth()) {
2507 while (endpar && endpar->getDepth()) {
2508 endpar = endpar->next();
2511 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2515 removeRow(old_cursor.row());
2517 if (ownerParagraph() == old_cursor.par()) {
2518 ownerParagraph(ownerParagraph()->next());
2521 delete old_cursor.par();
2523 /* Breakagain the next par. Needed because of
2524 the parindent that can occur or dissappear.
2525 The next row can change its height, if
2526 there is another layout before */
2528 breakAgain(bview, refresh_row);
2529 updateCounters(bview, refresh_row->previous());
2534 setCursorIntern(bview, cursor.par(), cursor.pos());
2536 if (selection.cursor.par() == old_cursor.par()
2537 && selection.cursor.pos() == old_cursor.pos()) {
2538 // correct selection
2539 selection.cursor = cursor;
2543 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2544 redoParagraphs(bview, old_cursor,
2545 old_cursor.par()->next());
2547 setCursorIntern(bview, cursor.par(), cursor.pos());
2548 selection.cursor = cursor;
2555 void LyXText::toggleAppendix(BufferView * bview)
2557 Paragraph * par = cursor.par();
2558 bool start = !par->params().startOfAppendix();
2560 // ensure that we have only one start_of_appendix in this document
2561 Paragraph * tmp = ownerParagraph();
2562 for (; tmp; tmp = tmp->next()) {
2563 tmp->params().startOfAppendix(false);
2566 par->params().startOfAppendix(start);
2568 // we can set the refreshing parameters now
2569 status(bview, LyXText::NEED_MORE_REFRESH);
2571 refresh_row = 0; // not needed for full update
2572 updateCounters(bview, 0);
2573 setCursor(bview, cursor.par(), cursor.pos());
2577 Paragraph * LyXText::ownerParagraph() const
2580 return inset_owner->paragraph();
2582 return bv_owner->buffer()->paragraph;
2586 void LyXText::ownerParagraph(Paragraph * p) const
2589 inset_owner->paragraph(p);
2591 bv_owner->buffer()->paragraph = p;
2596 void LyXText::ownerParagraph(int id, Paragraph * p) const
2598 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2599 if (op && op->inInset()) {
2600 static_cast<InsetText *>(op->inInset())->paragraph(p);
2607 LyXText::text_status LyXText::status() const
2613 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2615 // well as much as I know && binds more then || so the above and the
2616 // below are identical (this for your known use of parentesis!)
2617 // Now some explanation:
2618 // We should only go up with refreshing code so this means that if
2619 // we have a MORE refresh we should never set it to LITTLE if we still
2620 // didn't handle it (and then it will be UNCHANGED. Now as long as
2621 // we stay inside one LyXText this may work but we need to tell the
2622 // outermost LyXText that it should REALLY draw us if there is some
2623 // change in a Inset::LyXText. So you see that when we are inside a
2624 // inset's LyXText we give the LITTLE to the outermost LyXText to
2625 // tell'em that it should redraw the actual row (where the inset
2626 // resides! Capito?!
2628 if ((status_ != NEED_MORE_REFRESH)
2629 || (status_ == NEED_MORE_REFRESH
2630 && st != NEED_VERY_LITTLE_REFRESH))
2633 if (inset_owner && st != UNCHANGED) {
2634 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2635 if (!bview->text->refresh_row) {
2636 bview->text->refresh_row = bview->text->cursor.row();
2637 bview->text->refresh_y = bview->text->cursor.y() -
2638 bview->text->cursor.row()->baseline();