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);
550 while (cur.par() != send_cur.par()) {
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 cur.par(cur.par()->next());
569 cur.par()->setLayout(layout);
570 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
571 Paragraph * fppar = cur.par();
572 fppar->params().spaceTop(lyxlayout.fill_top ?
573 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
574 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
575 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
576 if (lyxlayout.margintype == MARGIN_MANUAL)
577 cur.par()->setLabelWidthString(lyxlayout.labelstring());
578 if (lyxlayout.labeltype != LABEL_BIBLIO
580 delete fppar->bibkey;
587 // set layout over selection and make a total rebreak of those paragraphs
588 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
590 LyXCursor tmpcursor = cursor; /* store the current cursor */
592 // if there is no selection just set the layout
593 // of the current paragraph */
594 if (!selection.set()) {
595 selection.start = cursor; // dummy selection
596 selection.end = cursor;
598 Paragraph * endpar = setLayout(bview, cursor, selection.start,
599 selection.end, layout);
600 redoParagraphs(bview, selection.start, endpar);
602 // we have to reset the selection, because the
603 // geometry could have changed
604 setCursor(bview, selection.start.par(),
605 selection.start.pos(), false);
606 selection.cursor = cursor;
607 setCursor(bview, selection.end.par(), selection.end.pos(), false);
608 updateCounters(bview, cursor.row());
611 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
615 // increment depth over selection and
616 // make a total rebreak of those paragraphs
617 void LyXText::incDepth(BufferView * bview)
619 // If there is no selection, just use the current paragraph
620 if (!selection.set()) {
621 selection.start = cursor; // dummy selection
622 selection.end = cursor;
625 // We end at the next paragraph with depth 0
626 Paragraph * endpar = selection.end.par()->next();
628 Paragraph * undoendpar = endpar;
630 if (endpar && endpar->getDepth()) {
631 while (endpar && endpar->getDepth()) {
632 endpar = endpar->next();
636 endpar = endpar->next(); // because of parindents etc.
639 setUndo(bview, Undo::EDIT,
640 selection.start.par(), undoendpar);
642 LyXCursor tmpcursor = cursor; // store the current cursor
644 // ok we have a selection. This is always between sel_start_cursor
645 // and sel_end cursor
646 cursor = selection.start;
648 bool anything_changed = false;
651 // NOTE: you can't change the depth of a bibliography entry
653 textclasslist.Style(bview->buffer()->params.textclass,
654 cursor.par()->getLayout()
655 ).labeltype != LABEL_BIBLIO) {
656 Paragraph * prev = cursor.par()->previous();
659 && (prev->getDepth() - cursor.par()->getDepth() > 0
660 || (prev->getDepth() == cursor.par()->getDepth()
661 && textclasslist.Style(bview->buffer()->params.textclass,
662 prev->getLayout()).isEnvironment()))) {
663 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
664 anything_changed = true;
667 if (cursor.par() == selection.end.par())
669 cursor.par(cursor.par()->next());
672 // if nothing changed set all depth to 0
673 if (!anything_changed) {
674 cursor = selection.start;
675 while (cursor.par() != selection.end.par()) {
676 cursor.par()->params().depth(0);
677 cursor.par(cursor.par()->next());
679 cursor.par()->params().depth(0);
682 redoParagraphs(bview, selection.start, endpar);
684 // we have to reset the selection, because the
685 // geometry could have changed
686 setCursor(bview, selection.start.par(), selection.start.pos());
687 selection.cursor = cursor;
688 setCursor(bview, selection.end.par(), selection.end.pos());
689 updateCounters(bview, cursor.row());
692 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
696 // decrement depth over selection and
697 // make a total rebreak of those paragraphs
698 void LyXText::decDepth(BufferView * bview)
700 // if there is no selection just set the layout
701 // of the current paragraph
702 if (!selection.set()) {
703 selection.start = cursor; // dummy selection
704 selection.end = cursor;
706 Paragraph * endpar = selection.end.par()->next();
707 Paragraph * undoendpar = endpar;
709 if (endpar && endpar->getDepth()) {
710 while (endpar && endpar->getDepth()) {
711 endpar = endpar->next();
715 endpar = endpar->next(); // because of parindents etc.
718 setUndo(bview, Undo::EDIT,
719 selection.start.par(), undoendpar);
721 LyXCursor tmpcursor = cursor; // store the current cursor
723 // ok we have a selection. This is always between sel_start_cursor
724 // and sel_end cursor
725 cursor = selection.start;
728 if (cursor.par()->params().depth()) {
729 cursor.par()->params()
730 .depth(cursor.par()->params().depth() - 1);
732 if (cursor.par() == selection.end.par()) {
735 cursor.par(cursor.par()->next());
738 redoParagraphs(bview, selection.start, endpar);
740 // we have to reset the selection, because the
741 // geometry could have changed
742 setCursor(bview, selection.start.par(),
743 selection.start.pos());
744 selection.cursor = cursor;
745 setCursor(bview, selection.end.par(), selection.end.pos());
746 updateCounters(bview, cursor.row());
749 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
753 // set font over selection and make a total rebreak of those paragraphs
754 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
756 // if there is no selection just set the current_font
757 if (!selection.set()) {
758 // Determine basis font
760 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
762 layoutfont = getLabelFont(bview->buffer(),
765 layoutfont = getLayoutFont(bview->buffer(),
768 // Update current font
769 real_current_font.update(font,
770 bview->buffer()->params.language,
773 // Reduce to implicit settings
774 current_font = real_current_font;
775 current_font.reduce(layoutfont);
776 // And resolve it completely
777 #ifndef INHERIT_LANGUAGE
778 real_current_font.realize(layoutfont);
780 real_current_font.realize(layoutfont,
781 bview->buffer()->params.language);
786 LyXCursor tmpcursor = cursor; // store the current cursor
788 // ok we have a selection. This is always between sel_start_cursor
789 // and sel_end cursor
791 setUndo(bview, Undo::EDIT,
792 selection.start.par(), selection.end.par()->next());
794 cursor = selection.start;
795 while (cursor.par() != selection.end.par() ||
796 (cursor.pos() < selection.end.pos()))
798 if (cursor.pos() < cursor.par()->size()) {
799 // an open footnote should behave
801 setCharFont(bview, cursor.par(), cursor.pos(),
803 cursor.pos(cursor.pos() + 1);
806 cursor.par(cursor.par()->next());
811 redoParagraphs(bview, selection.start, selection.end.par()->next());
813 // we have to reset the selection, because the
814 // geometry could have changed, but we keep
815 // it for user convenience
816 setCursor(bview, selection.start.par(), selection.start.pos());
817 selection.cursor = cursor;
818 setCursor(bview, selection.end.par(), selection.end.pos());
820 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
821 tmpcursor.boundary());
825 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
827 Row * tmprow = cur.row();
828 int y = cur.y() - tmprow->baseline();
830 setHeightOfRow(bview, tmprow);
832 while (tmprow->previous()
833 && tmprow->previous()->par() == tmprow->par()) {
834 tmprow = tmprow->previous();
835 y -= tmprow->height();
836 setHeightOfRow(bview, tmprow);
839 // we can set the refreshing parameters now
840 status(bview, LyXText::NEED_MORE_REFRESH);
842 refresh_row = tmprow;
843 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
847 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
849 Row * tmprow = cur.row();
851 int y = cur.y() - tmprow->baseline();
852 setHeightOfRow(bview, tmprow);
854 while (tmprow->previous()
855 && tmprow->previous()->par() == tmprow->par()) {
856 tmprow = tmprow->previous();
857 y -= tmprow->height();
860 // we can set the refreshing parameters now
861 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
863 refresh_row = tmprow;
865 status(bview, LyXText::NEED_MORE_REFRESH);
866 setCursor(bview, cur.par(), cur.pos());
870 // deletes and inserts again all paragaphs between the cursor
871 // and the specified par
872 // This function is needed after SetLayout and SetFont etc.
873 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
874 Paragraph const * endpar) const
877 Paragraph * tmppar = 0;
878 Paragraph * first_phys_par = 0;
880 Row * tmprow = cur.row();
882 int y = cur.y() - tmprow->baseline();
884 if (!tmprow->previous()) {
885 // a trick/hack for UNDO
886 // This is needed because in an UNDO/REDO we could have changed
887 // the ownerParagrah() so the paragraph inside the row is NOT
888 // my really first par anymore. Got it Lars ;) (Jug 20011206)
889 first_phys_par = ownerParagraph();
891 first_phys_par = tmprow->par();
892 while (tmprow->previous()
893 && tmprow->previous()->par() == first_phys_par)
895 tmprow = tmprow->previous();
896 y -= tmprow->height();
900 // we can set the refreshing parameters now
901 status(bview, LyXText::NEED_MORE_REFRESH);
903 refresh_row = tmprow->previous(); /* the real refresh row will
904 be deleted, so I store
908 tmppar = tmprow->next()->par();
911 while (tmprow->next() && tmppar != endpar) {
912 removeRow(tmprow->next());
913 if (tmprow->next()) {
914 tmppar = tmprow->next()->par();
920 // remove the first one
921 tmprow2 = tmprow; /* this is because tmprow->previous()
923 tmprow = tmprow->previous();
926 tmppar = first_phys_par;
930 insertParagraph(bview, tmppar, tmprow);
934 while (tmprow->next()
935 && tmprow->next()->par() == tmppar) {
936 tmprow = tmprow->next();
938 tmppar = tmppar->next();
940 } while (tmppar && tmppar != endpar);
942 // this is because of layout changes
944 refresh_y -= refresh_row->height();
945 setHeightOfRow(bview, refresh_row);
947 refresh_row = firstrow;
949 setHeightOfRow(bview, refresh_row);
952 if (tmprow && tmprow->next())
953 setHeightOfRow(bview, tmprow->next());
957 bool LyXText::fullRebreak(BufferView * bview)
963 if (need_break_row) {
964 breakAgain(bview, need_break_row);
972 // important for the screen
975 /* the cursor set functions have a special mechanism. When they
976 * realize, that you left an empty paragraph, they will delete it.
977 * They also delete the corresponding row */
979 // need the selection cursor:
980 void LyXText::setSelection(BufferView * bview)
982 bool const lsel = selection.set();
984 if (!selection.set()) {
985 last_sel_cursor = selection.cursor;
986 selection.start = selection.cursor;
987 selection.end = selection.cursor;
992 // first the toggling area
993 if (cursor.y() < last_sel_cursor.y()
994 || (cursor.y() == last_sel_cursor.y()
995 && cursor.x() < last_sel_cursor.x())) {
996 toggle_end_cursor = last_sel_cursor;
997 toggle_cursor = cursor;
999 toggle_end_cursor = cursor;
1000 toggle_cursor = last_sel_cursor;
1003 last_sel_cursor = cursor;
1005 // and now the whole selection
1007 if (selection.cursor.par() == cursor.par())
1008 if (selection.cursor.pos() < cursor.pos()) {
1009 selection.end = cursor;
1010 selection.start = selection.cursor;
1012 selection.end = selection.cursor;
1013 selection.start = cursor;
1015 else if (selection.cursor.y() < cursor.y() ||
1016 (selection.cursor.y() == cursor.y()
1017 && selection.cursor.x() < cursor.x())) {
1018 selection.end = cursor;
1019 selection.start = selection.cursor;
1022 selection.end = selection.cursor;
1023 selection.start = cursor;
1026 // a selection with no contents is not a selection
1027 if (selection.start.par() == selection.end.par() &&
1028 selection.start.pos() == selection.end.pos())
1029 selection.set(false);
1031 if (inset_owner && (selection.set() || lsel))
1032 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1036 string const LyXText::selectionAsString(Buffer const * buffer,
1039 if (!selection.set()) return string();
1042 // Special handling if the whole selection is within one paragraph
1043 if (selection.start.par() == selection.end.par()) {
1044 result += selection.start.par()->asString(buffer,
1045 selection.start.pos(),
1046 selection.end.pos(),
1051 // The selection spans more than one paragraph
1053 // First paragraph in selection
1054 result += selection.start.par()->asString(buffer,
1055 selection.start.pos(),
1056 selection.start.par()->size(),
1060 // The paragraphs in between (if any)
1061 LyXCursor tmpcur(selection.start);
1062 tmpcur.par(tmpcur.par()->next());
1063 while (tmpcur.par() != selection.end.par()) {
1064 result += tmpcur.par()->asString(buffer, 0,
1065 tmpcur.par()->size(),
1067 tmpcur.par(tmpcur.par()->next());
1070 // Last paragraph in selection
1071 result += selection.end.par()->asString(buffer, 0,
1072 selection.end.pos(), label);
1078 void LyXText::clearSelection() const
1080 selection.set(false);
1081 selection.mark(false);
1082 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1086 void LyXText::cursorHome(BufferView * bview) const
1088 setCursor(bview, cursor.par(), cursor.row()->pos());
1092 void LyXText::cursorEnd(BufferView * bview) const
1094 if (!cursor.row()->next()
1095 || cursor.row()->next()->par() != cursor.row()->par()) {
1096 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1098 if (cursor.par()->size() &&
1099 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1100 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1101 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1103 setCursor(bview,cursor.par(),
1104 rowLast(cursor.row()) + 1);
1110 void LyXText::cursorTop(BufferView * bview) const
1112 while (cursor.par()->previous())
1113 cursor.par(cursor.par()->previous());
1114 setCursor(bview, cursor.par(), 0);
1118 void LyXText::cursorBottom(BufferView * bview) const
1120 while (cursor.par()->next())
1121 cursor.par(cursor.par()->next());
1122 setCursor(bview, cursor.par(), cursor.par()->size());
1126 void LyXText::toggleFree(BufferView * bview,
1127 LyXFont const & font, bool toggleall)
1129 // If the mask is completely neutral, tell user
1130 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1131 // Could only happen with user style
1132 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1136 // Try implicit word selection
1137 // If there is a change in the language the implicit word selection
1139 LyXCursor resetCursor = cursor;
1140 bool implicitSelection = (font.language() == ignore_language
1141 && font.number() == LyXFont::IGNORE)
1142 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1145 setFont(bview, font, toggleall);
1147 // Implicit selections are cleared afterwards
1148 //and cursor is set to the original position.
1149 if (implicitSelection) {
1151 cursor = resetCursor;
1152 setCursor(bview, cursor.par(), cursor.pos());
1153 selection.cursor = cursor;
1156 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1160 string LyXText::getStringToIndex(BufferView * bview)
1164 // Try implicit word selection
1165 // If there is a change in the language the implicit word selection
1167 LyXCursor resetCursor = cursor;
1168 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1170 if (!selection.set()) {
1171 bview->owner()->message(_("Nothing to index!"));
1174 if (selection.start.par() != selection.end.par()) {
1175 bview->owner()->message(_("Cannot index more than one paragraph!"));
1179 idxstring = selectionAsString(bview->buffer(), false);
1181 // Implicit selections are cleared afterwards
1182 //and cursor is set to the original position.
1183 if (implicitSelection) {
1185 cursor = resetCursor;
1186 setCursor(bview, cursor.par(), cursor.pos());
1187 selection.cursor = cursor;
1193 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1194 Paragraph const * par) const
1196 if (textclasslist.Style(buf->params.textclass,
1197 par->getLayout()).labeltype != LABEL_MANUAL)
1200 return par->beginningOfMainBody();
1204 /* the DTP switches for paragraphs. LyX will store them in the
1205 * first physicla paragraph. When a paragraph is broken, the top settings
1206 * rest, the bottom settings are given to the new one. So I can make shure,
1207 * they do not duplicate themself and you cannnot make dirty things with
1210 void LyXText::setParagraph(BufferView * bview,
1211 bool line_top, bool line_bottom,
1212 bool pagebreak_top, bool pagebreak_bottom,
1213 VSpace const & space_top,
1214 VSpace const & space_bottom,
1215 Spacing const & spacing,
1217 string labelwidthstring,
1220 LyXCursor tmpcursor = cursor;
1221 if (!selection.set()) {
1222 selection.start = cursor;
1223 selection.end = cursor;
1226 // make sure that the depth behind the selection are restored, too
1227 Paragraph * endpar = selection.end.par()->next();
1228 Paragraph * undoendpar = endpar;
1230 if (endpar && endpar->getDepth()) {
1231 while (endpar && endpar->getDepth()) {
1232 endpar = endpar->next();
1233 undoendpar = endpar;
1237 // because of parindents etc.
1238 endpar = endpar->next();
1241 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1244 Paragraph * tmppar = selection.end.par();
1245 while (tmppar != selection.start.par()->previous()) {
1246 setCursor(bview, tmppar, 0);
1247 status(bview, LyXText::NEED_MORE_REFRESH);
1248 refresh_row = cursor.row();
1249 refresh_y = cursor.y() - cursor.row()->baseline();
1250 cursor.par()->params().lineTop(line_top);
1251 cursor.par()->params().lineBottom(line_bottom);
1252 cursor.par()->params().pagebreakTop(pagebreak_top);
1253 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1254 cursor.par()->params().spaceTop(space_top);
1255 cursor.par()->params().spaceBottom(space_bottom);
1256 cursor.par()->params().spacing(spacing);
1257 // does the layout allow the new alignment?
1258 if (align == LYX_ALIGN_LAYOUT)
1259 align = textclasslist
1260 .Style(bview->buffer()->params.textclass,
1261 cursor.par()->getLayout()).align;
1262 if (align & textclasslist
1263 .Style(bview->buffer()->params.textclass,
1264 cursor.par()->getLayout()).alignpossible) {
1265 if (align == textclasslist
1266 .Style(bview->buffer()->params.textclass,
1267 cursor.par()->getLayout()).align)
1268 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1270 cursor.par()->params().align(align);
1272 cursor.par()->setLabelWidthString(labelwidthstring);
1273 cursor.par()->params().noindent(noindent);
1274 tmppar = cursor.par()->previous();
1277 redoParagraphs(bview, selection.start, endpar);
1280 setCursor(bview, selection.start.par(), selection.start.pos());
1281 selection.cursor = cursor;
1282 setCursor(bview, selection.end.par(), selection.end.pos());
1283 setSelection(bview);
1284 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1286 bview->updateInset(inset_owner, true);
1290 char loweralphaCounter(int n)
1292 if (n < 1 || n > 26)
1302 char alphaCounter(int n)
1304 if (n < 1 || n > 26)
1312 char hebrewCounter(int n)
1314 static const char hebrew[22] = {
1315 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1316 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1317 '÷', 'ø', 'ù', 'ú'
1319 if (n < 1 || n > 22)
1327 string const romanCounter(int n)
1329 static char const * roman[20] = {
1330 "i", "ii", "iii", "iv", "v",
1331 "vi", "vii", "viii", "ix", "x",
1332 "xi", "xii", "xiii", "xiv", "xv",
1333 "xvi", "xvii", "xviii", "xix", "xx"
1335 if (n < 1 || n > 20)
1344 // set the counter of a paragraph. This includes the labels
1345 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1347 LyXLayout const & layout =
1348 textclasslist.Style(buf->params.textclass,
1351 LyXTextClass const & textclass =
1352 textclasslist.TextClass(buf->params.textclass);
1354 // copy the prev-counters to this one,
1355 // unless this is the first paragraph
1356 if (par->previous()) {
1357 for (int i = 0; i < 10; ++i) {
1358 par->setCounter(i, par->previous()->getFirstCounter(i));
1360 par->params().appendix(par->previous()->params().appendix());
1361 if (!par->params().appendix() && par->params().startOfAppendix()) {
1362 par->params().appendix(true);
1363 for (int i = 0; i < 10; ++i) {
1364 par->setCounter(i, 0);
1367 par->enumdepth = par->previous()->enumdepth;
1368 par->itemdepth = par->previous()->itemdepth;
1370 for (int i = 0; i < 10; ++i) {
1371 par->setCounter(i, 0);
1373 par->params().appendix(par->params().startOfAppendix());
1378 /* Maybe we have to increment the enumeration depth.
1379 * BUT, enumeration in a footnote is considered in isolation from its
1380 * surrounding paragraph so don't increment if this is the
1381 * first line of the footnote
1382 * AND, bibliographies can't have their depth changed ie. they
1383 * are always of depth 0
1386 && par->previous()->getDepth() < par->getDepth()
1387 && textclasslist.Style(buf->params.textclass,
1388 par->previous()->getLayout()
1389 ).labeltype == LABEL_COUNTER_ENUMI
1390 && par->enumdepth < 3
1391 && layout.labeltype != LABEL_BIBLIO) {
1395 // Maybe we have to decrement the enumeration depth, see note above
1397 && par->previous()->getDepth() > par->getDepth()
1398 && layout.labeltype != LABEL_BIBLIO) {
1399 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1400 par->setCounter(6 + par->enumdepth,
1401 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1402 /* reset the counters.
1403 * A depth change is like a breaking layout
1405 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1406 par->setCounter(i, 0);
1409 if (!par->params().labelString().empty()) {
1410 par->params().labelString(string());
1413 if (layout.margintype == MARGIN_MANUAL) {
1414 if (par->params().labelWidthString().empty()) {
1415 par->setLabelWidthString(layout.labelstring());
1418 par->setLabelWidthString(string());
1421 // is it a layout that has an automatic label?
1422 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1424 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1425 if (i >= 0 && i<= buf->params.secnumdepth) {
1426 par->incCounter(i); // increment the counter
1428 // Is there a label? Useful for Chapter layout
1429 if (!par->params().appendix()) {
1430 if (!layout.labelstring().empty())
1431 par->params().labelString(layout.labelstring());
1433 par->params().labelString(string());
1435 if (!layout.labelstring_appendix().empty())
1436 par->params().labelString(layout.labelstring_appendix());
1438 par->params().labelString(string());
1443 if (!par->params().appendix()) {
1444 switch (2 * LABEL_COUNTER_CHAPTER -
1445 textclass.maxcounter() + i) {
1446 case LABEL_COUNTER_CHAPTER:
1447 s << par->getCounter(i);
1449 case LABEL_COUNTER_SECTION:
1450 s << par->getCounter(i - 1) << '.'
1451 << par->getCounter(i);
1453 case LABEL_COUNTER_SUBSECTION:
1454 s << par->getCounter(i - 2) << '.'
1455 << par->getCounter(i - 1) << '.'
1456 << par->getCounter(i);
1458 case LABEL_COUNTER_SUBSUBSECTION:
1459 s << par->getCounter(i - 3) << '.'
1460 << par->getCounter(i - 2) << '.'
1461 << par->getCounter(i - 1) << '.'
1462 << par->getCounter(i);
1465 case LABEL_COUNTER_PARAGRAPH:
1466 s << par->getCounter(i - 4) << '.'
1467 << par->getCounter(i - 3) << '.'
1468 << par->getCounter(i - 2) << '.'
1469 << par->getCounter(i - 1) << '.'
1470 << par->getCounter(i);
1472 case LABEL_COUNTER_SUBPARAGRAPH:
1473 s << par->getCounter(i - 5) << '.'
1474 << par->getCounter(i - 4) << '.'
1475 << par->getCounter(i - 3) << '.'
1476 << par->getCounter(i - 2) << '.'
1477 << par->getCounter(i - 1) << '.'
1478 << par->getCounter(i);
1482 // Can this ever be reached? And in the
1483 // case it is, how can this be correct?
1485 s << par->getCounter(i) << '.';
1488 } else { // appendix
1489 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1490 case LABEL_COUNTER_CHAPTER:
1491 if (par->isRightToLeftPar(buf->params))
1492 s << hebrewCounter(par->getCounter(i));
1494 s << alphaCounter(par->getCounter(i));
1496 case LABEL_COUNTER_SECTION:
1497 if (par->isRightToLeftPar(buf->params))
1498 s << hebrewCounter(par->getCounter(i - 1));
1500 s << alphaCounter(par->getCounter(i - 1));
1503 << par->getCounter(i);
1506 case LABEL_COUNTER_SUBSECTION:
1507 if (par->isRightToLeftPar(buf->params))
1508 s << hebrewCounter(par->getCounter(i - 2));
1510 s << alphaCounter(par->getCounter(i - 2));
1513 << par->getCounter(i-1) << '.'
1514 << par->getCounter(i);
1517 case LABEL_COUNTER_SUBSUBSECTION:
1518 if (par->isRightToLeftPar(buf->params))
1519 s << hebrewCounter(par->getCounter(i-3));
1521 s << alphaCounter(par->getCounter(i-3));
1524 << par->getCounter(i-2) << '.'
1525 << par->getCounter(i-1) << '.'
1526 << par->getCounter(i);
1529 case LABEL_COUNTER_PARAGRAPH:
1530 if (par->isRightToLeftPar(buf->params))
1531 s << hebrewCounter(par->getCounter(i-4));
1533 s << alphaCounter(par->getCounter(i-4));
1536 << par->getCounter(i-3) << '.'
1537 << par->getCounter(i-2) << '.'
1538 << par->getCounter(i-1) << '.'
1539 << par->getCounter(i);
1542 case LABEL_COUNTER_SUBPARAGRAPH:
1543 if (par->isRightToLeftPar(buf->params))
1544 s << hebrewCounter(par->getCounter(i-5));
1546 s << alphaCounter(par->getCounter(i-5));
1549 << par->getCounter(i-4) << '.'
1550 << par->getCounter(i-3) << '.'
1551 << par->getCounter(i-2) << '.'
1552 << par->getCounter(i-1) << '.'
1553 << par->getCounter(i);
1557 // Can this ever be reached? And in the
1558 // case it is, how can this be correct?
1560 s << par->getCounter(i) << '.';
1566 par->params().labelString(par->params().labelString() +s.str().c_str());
1567 // We really want to remove the c_str as soon as
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 for (i++; i < 10; ++i) {
1576 // reset the following counters
1577 par->setCounter(i, 0);
1579 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1580 par->incCounter(i + par->enumdepth);
1581 int number = par->getCounter(i + par->enumdepth);
1585 switch (par->enumdepth) {
1587 if (par->isRightToLeftPar(buf->params))
1589 << hebrewCounter(number)
1593 << loweralphaCounter(number)
1597 if (par->isRightToLeftPar(buf->params))
1598 s << '.' << romanCounter(number);
1600 s << romanCounter(number) << '.';
1603 if (par->isRightToLeftPar(buf->params))
1605 << alphaCounter(number);
1607 s << alphaCounter(number)
1611 if (par->isRightToLeftPar(buf->params))
1618 par->params().labelString(s.str().c_str());
1620 for (i += par->enumdepth + 1; i < 10; ++i) {
1621 // reset the following counters
1622 par->setCounter(i, 0);
1626 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1627 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1629 int number = par->getCounter(i);
1631 InsetCommandParams p("bibitem" );
1632 par->bibkey = new InsetBibKey(p);
1634 par->bibkey->setCounter(number);
1635 par->params().labelString(layout.labelstring());
1637 // In biblio should't be following counters but...
1639 string s = layout.labelstring();
1641 // the caption hack:
1642 if (layout.labeltype == LABEL_SENSITIVE) {
1643 bool isOK (par->inInset() && par->inInset()->owner() &&
1644 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1647 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1649 = floatList.getType(tmp->type());
1650 // We should get the correct number here too.
1651 s = fl.name() + " #:";
1653 /* par->SetLayout(0);
1654 s = layout->labelstring; */
1655 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1656 ? " :úåòîùî øñç" : "Senseless: ";
1659 par->params().labelString(s);
1661 /* reset the enumeration counter. They are always resetted
1662 * when there is any other layout between */
1663 for (int i = 6 + par->enumdepth; i < 10; ++i)
1664 par->setCounter(i, 0);
1669 // Updates all counters BEHIND the row. Changed paragraphs
1670 // with a dynamic left margin will be rebroken.
1671 void LyXText::updateCounters(BufferView * bview, Row * row) const
1679 par = row->par()->next();
1683 while (row->par() != par)
1686 setCounter(bview->buffer(), par);
1688 // now check for the headline layouts. remember that they
1689 // have a dynamic left margin
1690 if ((textclasslist.Style(bview->buffer()->params.textclass,
1691 par->layout).margintype == MARGIN_DYNAMIC
1692 || textclasslist.Style(bview->buffer()->params.textclass,
1693 par->layout).labeltype == LABEL_SENSITIVE)) {
1695 // Rebreak the paragraph
1696 removeParagraph(row);
1697 appendParagraph(bview, row);
1704 void LyXText::insertInset(BufferView * bview, Inset * inset)
1706 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1708 // I don't know if this is necessary here (Jug 20020102)
1709 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1710 cursor.par()->insertInset(cursor.pos(), inset);
1711 // Just to rebreak and refresh correctly.
1712 // The character will not be inserted a second time
1713 insertChar(bview, Paragraph::META_INSET);
1715 // If we enter a highly editable inset the cursor should be to before
1716 // the inset. This couldn't happen before as Undo was not handled inside
1717 // inset now after the Undo LyX tries to call inset->Edit(...) again
1718 // and cannot do this as the cursor is behind the inset and GetInset
1719 // does not return the inset!
1720 if (isHighlyEditableInset(inset)) {
1721 cursorLeft(bview, true);
1727 void LyXText::copyEnvironmentType()
1729 copylayouttype = cursor.par()->getLayout();
1733 void LyXText::pasteEnvironmentType(BufferView * bview)
1735 setLayout(bview, copylayouttype);
1739 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1741 // Stuff what we got on the clipboard. Even if there is no selection.
1743 // There is a problem with having the stuffing here in that the
1744 // larger the selection the slower LyX will get. This can be
1745 // solved by running the line below only when the selection has
1746 // finished. The solution used currently just works, to make it
1747 // faster we need to be more clever and probably also have more
1748 // calls to stuffClipboard. (Lgb)
1749 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1751 // This doesn't make sense, if there is no selection
1752 if (!selection.set())
1755 // OK, we have a selection. This is always between selection.start
1756 // and selection.end
1758 // make sure that the depth behind the selection are restored, too
1759 Paragraph * endpar = selection.end.par()->next();
1760 Paragraph * undoendpar = endpar;
1762 if (endpar && endpar->getDepth()) {
1763 while (endpar && endpar->getDepth()) {
1764 endpar = endpar->next();
1765 undoendpar = endpar;
1767 } else if (endpar) {
1768 endpar = endpar->next(); // because of parindents etc.
1771 setUndo(bview, Undo::DELETE,
1772 selection.start.par(), undoendpar);
1774 // there are two cases: cut only within one paragraph or
1775 // more than one paragraph
1776 if (selection.start.par() == selection.end.par()) {
1777 // only within one paragraph
1778 endpar = selection.end.par();
1779 int pos = selection.end.pos();
1780 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1781 selection.start.pos(), pos,
1782 bview->buffer()->params.textclass,
1784 selection.end.pos(pos);
1786 endpar = selection.end.par();
1787 int pos = selection.end.pos();
1788 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1789 selection.start.pos(), pos,
1790 bview->buffer()->params.textclass,
1793 selection.end.par(endpar);
1794 selection.end.pos(pos);
1795 cursor.pos(selection.end.pos());
1797 endpar = endpar->next();
1799 // sometimes necessary
1801 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1803 redoParagraphs(bview, selection.start, endpar);
1805 // cutSelection can invalidate the cursor so we need to set
1807 cursor = selection.start;
1809 // need a valid cursor. (Lgb)
1812 setCursor(bview, cursor.par(), cursor.pos());
1813 selection.cursor = cursor;
1814 updateCounters(bview, cursor.row());
1818 void LyXText::copySelection(BufferView * bview)
1820 // stuff the selection onto the X clipboard, from an explicit copy request
1821 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1823 // this doesnt make sense, if there is no selection
1824 if (!selection.set())
1827 // ok we have a selection. This is always between selection.start
1828 // and sel_end cursor
1830 // copy behind a space if there is one
1831 while (selection.start.par()->size() > selection.start.pos()
1832 && selection.start.par()->isLineSeparator(selection.start.pos())
1833 && (selection.start.par() != selection.end.par()
1834 || selection.start.pos() < selection.end.pos()))
1835 selection.start.pos(selection.start.pos() + 1);
1837 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1838 selection.start.pos(), selection.end.pos(),
1839 bview->buffer()->params.textclass);
1843 void LyXText::pasteSelection(BufferView * bview)
1845 // this does not make sense, if there is nothing to paste
1846 if (!CutAndPaste::checkPastePossible(cursor.par()))
1849 setUndo(bview, Undo::INSERT,
1850 cursor.par(), cursor.par()->next());
1853 Paragraph * actpar = cursor.par();
1854 int pos = cursor.pos();
1856 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1857 bview->buffer()->params.textclass);
1859 redoParagraphs(bview, cursor, endpar);
1861 setCursor(bview, cursor.par(), cursor.pos());
1864 setCursor(bview, actpar, pos);
1865 updateCounters(bview, cursor.row());
1869 // sets the selection over the number of characters of string, no check!!
1870 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1875 selection.cursor = cursor;
1876 for (string::size_type i = 0; i < str.length(); ++i)
1878 setSelection(bview);
1882 // simple replacing. The font of the first selected character is used
1883 void LyXText::replaceSelectionWithString(BufferView * bview,
1886 setCursorParUndo(bview);
1889 if (!selection.set()) { // create a dummy selection
1890 selection.end = cursor;
1891 selection.start = cursor;
1894 // Get font setting before we cut
1895 pos_type pos = selection.end.pos();
1896 LyXFont const font = selection.start.par()
1897 ->getFontSettings(bview->buffer()->params,
1898 selection.start.pos());
1900 // Insert the new string
1901 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1902 selection.end.par()->insertChar(pos, (*cit), font);
1906 // Cut the selection
1907 cutSelection(bview, true, false);
1913 // needed to insert the selection
1914 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1916 Paragraph * par = cursor.par();
1917 pos_type pos = cursor.pos();
1918 Paragraph * endpar = cursor.par()->next();
1920 setCursorParUndo(bview);
1922 // only to be sure, should not be neccessary
1925 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1927 redoParagraphs(bview, cursor, endpar);
1928 setCursor(bview, cursor.par(), cursor.pos());
1929 selection.cursor = cursor;
1930 setCursor(bview, par, pos);
1931 setSelection(bview);
1935 // turns double-CR to single CR, others where converted into one
1936 // blank. Then InsertStringAsLines is called
1937 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1939 string linestr(str);
1940 bool newline_inserted = false;
1941 for (string::size_type i = 0; i < linestr.length(); ++i) {
1942 if (linestr[i] == '\n') {
1943 if (newline_inserted) {
1944 // we know that \r will be ignored by
1945 // InsertStringA. Of course, it is a dirty
1946 // trick, but it works...
1947 linestr[i - 1] = '\r';
1951 newline_inserted = true;
1953 } else if (IsPrintable(linestr[i])) {
1954 newline_inserted = false;
1957 insertStringAsLines(bview, linestr);
1961 bool LyXText::gotoNextInset(BufferView * bview,
1962 vector<Inset::Code> const & codes,
1963 string const & contents) const
1965 LyXCursor res = cursor;
1968 if (res.pos() < res.par()->size() - 1) {
1969 res.pos(res.pos() + 1);
1971 res.par(res.par()->next());
1975 } while (res.par() &&
1976 !(res.par()->isInset(res.pos())
1977 && (inset = res.par()->getInset(res.pos())) != 0
1978 && find(codes.begin(), codes.end(), inset->lyxCode())
1980 && (contents.empty() ||
1981 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1985 setCursor(bview, res.par(), res.pos(), false);
1992 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1995 LyXCursor tmpcursor;
1999 Row * row = getRow(par, pos, y);
2001 // is there a break one row above
2002 if (row->previous() && row->previous()->par() == row->par()) {
2003 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
2004 if (z >= row->pos()) {
2005 // set the dimensions of the row above
2006 y -= row->previous()->height();
2008 refresh_row = row->previous();
2009 status(bview, LyXText::NEED_MORE_REFRESH);
2011 breakAgain(bview, row->previous());
2013 // set the cursor again. Otherwise
2014 // dangling pointers are possible
2015 setCursor(bview, cursor.par(), cursor.pos(),
2016 false, cursor.boundary());
2017 selection.cursor = cursor;
2022 int const tmpheight = row->height();
2023 pos_type const tmplast = rowLast(row);
2027 breakAgain(bview, row);
2028 if (row->height() == tmpheight && rowLast(row) == tmplast)
2029 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2031 status(bview, LyXText::NEED_MORE_REFRESH);
2033 // check the special right address boxes
2034 if (textclasslist.Style(bview->buffer()->params.textclass,
2035 par->getLayout()).margintype
2036 == MARGIN_RIGHT_ADDRESS_BOX)
2044 redoDrawingOfParagraph(bview, tmpcursor);
2047 // set the cursor again. Otherwise dangling pointers are possible
2048 // also set the selection
2050 if (selection.set()) {
2052 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2053 false, selection.cursor.boundary());
2054 selection.cursor = cursor;
2055 setCursorIntern(bview, selection.start.par(),
2056 selection.start.pos(),
2057 false, selection.start.boundary());
2058 selection.start = cursor;
2059 setCursorIntern(bview, selection.end.par(),
2060 selection.end.pos(),
2061 false, selection.end.boundary());
2062 selection.end = cursor;
2063 setCursorIntern(bview, last_sel_cursor.par(),
2064 last_sel_cursor.pos(),
2065 false, last_sel_cursor.boundary());
2066 last_sel_cursor = cursor;
2069 setCursorIntern(bview, cursor.par(), cursor.pos(),
2070 false, cursor.boundary());
2074 // returns false if inset wasn't found
2075 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2077 // first check the current paragraph
2078 int pos = cursor.par()->getPositionOfInset(inset);
2080 checkParagraph(bview, cursor.par(), pos);
2084 // check every paragraph
2086 Paragraph * par = ownerParagraph();
2088 pos = par->getPositionOfInset(inset);
2090 checkParagraph(bview, par, pos);
2100 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2102 bool setfont, bool boundary) const
2104 LyXCursor old_cursor = cursor;
2105 setCursorIntern(bview, par, pos, setfont, boundary);
2106 return deleteEmptyParagraphMechanism(bview, old_cursor);
2110 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2111 pos_type pos, bool boundary) const
2118 cur.boundary(boundary);
2120 // get the cursor y position in text
2122 Row * row = getRow(par, pos, y);
2123 // y is now the beginning of the cursor row
2124 y += row->baseline();
2125 // y is now the cursor baseline
2128 // now get the cursors x position
2130 float fill_separator;
2132 float fill_label_hfill;
2133 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2135 pos_type cursor_vpos = 0;
2136 pos_type last = rowLastPrintable(row);
2138 if (pos > last + 1) {
2139 // This shouldn't happen.
2142 } else if (pos < row->pos()) {
2147 if (last < row->pos())
2148 cursor_vpos = row->pos();
2149 else if (pos > last && !boundary)
2150 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2151 ? row->pos() : last + 1;
2152 else if (pos > row->pos() &&
2153 (pos > last || boundary))
2154 /// Place cursor after char at (logical) position pos - 1
2155 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2156 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2158 /// Place cursor before char at (logical) position pos
2159 cursor_vpos = (bidi_level(pos) % 2 == 0)
2160 ? log2vis(pos) : log2vis(pos) + 1;
2162 pos_type main_body =
2163 beginningOfMainBody(bview->buffer(), row->par());
2164 if ((main_body > 0) &&
2165 ((main_body-1 > last) ||
2166 !row->par()->isLineSeparator(main_body-1)))
2169 for (pos_type vpos = row->pos();
2170 vpos < cursor_vpos; ++vpos) {
2171 pos = vis2log(vpos);
2172 if (main_body > 0 && pos == main_body - 1) {
2173 x += fill_label_hfill +
2174 lyxfont::width(textclasslist.Style(
2175 bview->buffer()->params.textclass,
2176 row->par()->getLayout())
2178 getLabelFont(bview->buffer(), row->par()));
2179 if (row->par()->isLineSeparator(main_body-1))
2180 x -= singleWidth(bview, row->par(),main_body-1);
2182 if (hfillExpansion(bview->buffer(), row, pos)) {
2183 x += singleWidth(bview, row->par(), pos);
2184 if (pos >= main_body)
2187 x += fill_label_hfill;
2188 } else if (row->par()->isSeparator(pos)) {
2189 x += singleWidth(bview, row->par(), pos);
2190 if (pos >= main_body)
2191 x += fill_separator;
2193 x += singleWidth(bview, row->par(), pos);
2202 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2203 pos_type pos, bool setfont, bool boundary) const
2205 InsetText * it = static_cast<InsetText *>(par->inInset());
2207 if (it != inset_owner) {
2208 lyxerr << "InsetText is " << it << endl;
2209 lyxerr << "inset_owner is " << inset_owner << endl;
2210 #ifdef WITH_WARNINGS
2211 #warning I believe this code is wrong. (Lgb)
2212 #warning Jürgen, have a look at this. (Lgb)
2213 #warning Hmmm, I guess you are right but we
2214 #warning should verify when this is needed
2216 // Jürgen, would you like to have a look?
2217 // I guess we need to move the outer cursor
2218 // and open and lock the inset (bla bla bla)
2219 // stuff I don't know... so can you have a look?
2221 // I moved the lyxerr stuff in here so we can see if
2222 // this is actually really needed and where!
2224 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2229 setCursor(bview, cursor, par, pos, boundary);
2231 setCurrentFont(bview);
2235 void LyXText::setCurrentFont(BufferView * bview) const
2237 pos_type pos = cursor.pos();
2238 if (cursor.boundary() && pos > 0)
2242 if (pos == cursor.par()->size())
2244 else // potentional bug... BUG (Lgb)
2245 if (cursor.par()->isSeparator(pos)) {
2246 if (pos > cursor.row()->pos() &&
2247 bidi_level(pos) % 2 ==
2248 bidi_level(pos - 1) % 2)
2250 else if (pos + 1 < cursor.par()->size())
2256 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2257 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2259 if (cursor.pos() == cursor.par()->size() &&
2260 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2261 !cursor.boundary()) {
2262 Language const * lang =
2263 cursor.par()->getParLanguage(bview->buffer()->params);
2264 current_font.setLanguage(lang);
2265 current_font.setNumber(LyXFont::OFF);
2266 real_current_font.setLanguage(lang);
2267 real_current_font.setNumber(LyXFont::OFF);
2272 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2274 LyXCursor old_cursor = cursor;
2276 setCursorFromCoordinates(bview, cursor, x, y);
2277 setCurrentFont(bview);
2278 deleteEmptyParagraphMechanism(bview, old_cursor);
2282 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2285 // Get the row first.
2287 Row * row = getRowNearY(y);
2289 pos_type const column = getColumnNearX(bview, row, x, bound);
2290 cur.par(row->par());
2291 cur.pos(row->pos() + column);
2293 cur.y(y + row->baseline());
2295 cur.boundary(bound);
2299 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2301 if (cursor.pos() > 0) {
2302 bool boundary = cursor.boundary();
2303 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2304 if (!internal && !boundary &&
2305 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2306 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2307 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2308 Paragraph * par = cursor.par()->previous();
2309 setCursor(bview, par, par->size());
2314 void LyXText::cursorRight(BufferView * bview, bool internal) const
2316 if (!internal && cursor.boundary() &&
2317 !cursor.par()->isNewline(cursor.pos()))
2318 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2319 else if (cursor.pos() < cursor.par()->size()) {
2320 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2322 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2323 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2324 } else if (cursor.par()->next())
2325 setCursor(bview, cursor.par()->next(), 0);
2329 void LyXText::cursorUp(BufferView * bview) const
2331 setCursorFromCoordinates(bview, cursor.x_fix(),
2332 cursor.y() - cursor.row()->baseline() - 1);
2336 void LyXText::cursorDown(BufferView * bview) const
2338 setCursorFromCoordinates(bview, cursor.x_fix(),
2339 cursor.y() - cursor.row()->baseline()
2340 + cursor.row()->height() + 1);
2344 void LyXText::cursorUpParagraph(BufferView * bview) const
2346 if (cursor.pos() > 0) {
2347 setCursor(bview, cursor.par(), 0);
2349 else if (cursor.par()->previous()) {
2350 setCursor(bview, cursor.par()->previous(), 0);
2355 void LyXText::cursorDownParagraph(BufferView * bview) const
2357 if (cursor.par()->next()) {
2358 setCursor(bview, cursor.par()->next(), 0);
2360 setCursor(bview, cursor.par(), cursor.par()->size());
2364 // fix the cursor `cur' after a characters has been deleted at `where'
2365 // position. Called by deleteEmptyParagraphMechanism
2366 void LyXText::fixCursorAfterDelete(BufferView * bview,
2368 LyXCursor const & where) const
2370 // if cursor is not in the paragraph where the delete occured,
2372 if (cur.par() != where.par())
2375 // if cursor position is after the place where the delete occured,
2377 if (cur.pos() > where.pos())
2378 cur.pos(cur.pos()-1);
2380 // recompute row et al. for this cursor
2381 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2385 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2386 LyXCursor const & old_cursor) const
2388 // Would be wrong to delete anything if we have a selection.
2389 if (selection.set()) return false;
2391 // We allow all kinds of "mumbo-jumbo" when freespacing.
2392 if (textclasslist.Style(bview->buffer()->params.textclass,
2393 old_cursor.par()->getLayout()).free_spacing
2394 || old_cursor.par()->isFreeSpacing())
2399 /* Ok I'll put some comments here about what is missing.
2400 I have fixed BackSpace (and thus Delete) to not delete
2401 double-spaces automagically. I have also changed Cut,
2402 Copy and Paste to hopefully do some sensible things.
2403 There are still some small problems that can lead to
2404 double spaces stored in the document file or space at
2405 the beginning of paragraphs. This happens if you have
2406 the cursor betwenn to spaces and then save. Or if you
2407 cut and paste and the selection have a space at the
2408 beginning and then save right after the paste. I am
2409 sure none of these are very hard to fix, but I will
2410 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2411 that I can get some feedback. (Lgb)
2414 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2415 // delete the LineSeparator.
2418 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2419 // delete the LineSeparator.
2422 // If the pos around the old_cursor were spaces, delete one of them.
2423 if (old_cursor.par() != cursor.par()
2424 || old_cursor.pos() != cursor.pos()) {
2425 // Only if the cursor has really moved
2427 if (old_cursor.pos() > 0
2428 && old_cursor.pos() < old_cursor.par()->size()
2429 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2430 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2431 old_cursor.par()->erase(old_cursor.pos() - 1);
2432 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2434 #ifdef WITH_WARNINGS
2435 #warning This will not work anymore when we have multiple views of the same buffer
2436 // In this case, we will have to correct also the cursors held by
2437 // other bufferviews. It will probably be easier to do that in a more
2438 // automated way in LyXCursor code. (JMarc 26/09/2001)
2440 // correct all cursors held by the LyXText
2441 fixCursorAfterDelete(bview, cursor, old_cursor);
2442 fixCursorAfterDelete(bview, selection.cursor,
2444 fixCursorAfterDelete(bview, selection.start,
2446 fixCursorAfterDelete(bview, selection.end, old_cursor);
2447 fixCursorAfterDelete(bview, last_sel_cursor,
2449 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2450 fixCursorAfterDelete(bview, toggle_end_cursor,
2456 // don't delete anything if this is the ONLY paragraph!
2457 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2460 // Do not delete empty paragraphs with keepempty set.
2461 if ((textclasslist.Style(bview->buffer()->params.textclass,
2462 old_cursor.par()->getLayout())).keepempty)
2465 // only do our magic if we changed paragraph
2466 if (old_cursor.par() == cursor.par())
2469 // record if we have deleted a paragraph
2470 // we can't possibly have deleted a paragraph before this point
2471 bool deleted = false;
2473 if ((old_cursor.par()->size() == 0
2474 || (old_cursor.par()->size() == 1
2475 && old_cursor.par()->isLineSeparator(0)))) {
2476 // ok, we will delete anything
2477 LyXCursor tmpcursor;
2479 // make sure that you do not delete any environments
2480 status(bview, LyXText::NEED_MORE_REFRESH);
2483 if (old_cursor.row()->previous()) {
2484 refresh_row = old_cursor.row()->previous();
2485 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2487 cursor = old_cursor; // that undo can restore the right cursor position
2488 Paragraph * endpar = old_cursor.par()->next();
2489 if (endpar && endpar->getDepth()) {
2490 while (endpar && endpar->getDepth()) {
2491 endpar = endpar->next();
2494 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2498 removeRow(old_cursor.row());
2499 if (ownerParagraph() == old_cursor.par()) {
2500 ownerParagraph(ownerParagraph()->next());
2503 delete old_cursor.par();
2505 /* Breakagain the next par. Needed because of
2506 * the parindent that can occur or dissappear.
2507 * The next row can change its height, if
2508 * there is another layout before */
2509 if (refresh_row->next()) {
2510 breakAgain(bview, refresh_row->next());
2511 updateCounters(bview, refresh_row);
2513 setHeightOfRow(bview, refresh_row);
2515 refresh_row = old_cursor.row()->next();
2516 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2519 cursor = old_cursor; // that undo can restore the right cursor position
2520 Paragraph * endpar = old_cursor.par()->next();
2521 if (endpar && endpar->getDepth()) {
2522 while (endpar && endpar->getDepth()) {
2523 endpar = endpar->next();
2526 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2530 removeRow(old_cursor.row());
2532 if (ownerParagraph() == old_cursor.par()) {
2533 ownerParagraph(ownerParagraph()->next());
2536 delete old_cursor.par();
2538 /* Breakagain the next par. Needed because of
2539 the parindent that can occur or dissappear.
2540 The next row can change its height, if
2541 there is another layout before */
2543 breakAgain(bview, refresh_row);
2544 updateCounters(bview, refresh_row->previous());
2549 setCursorIntern(bview, cursor.par(), cursor.pos());
2551 if (selection.cursor.par() == old_cursor.par()
2552 && selection.cursor.pos() == old_cursor.pos()) {
2553 // correct selection
2554 selection.cursor = cursor;
2558 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2559 redoParagraphs(bview, old_cursor,
2560 old_cursor.par()->next());
2562 setCursorIntern(bview, cursor.par(), cursor.pos());
2563 selection.cursor = cursor;
2570 void LyXText::toggleAppendix(BufferView * bview)
2572 Paragraph * par = cursor.par();
2573 bool start = !par->params().startOfAppendix();
2575 // ensure that we have only one start_of_appendix in this document
2576 Paragraph * tmp = ownerParagraph();
2577 for (; tmp; tmp = tmp->next()) {
2578 tmp->params().startOfAppendix(false);
2581 par->params().startOfAppendix(start);
2583 // we can set the refreshing parameters now
2584 status(bview, LyXText::NEED_MORE_REFRESH);
2586 refresh_row = 0; // not needed for full update
2587 updateCounters(bview, 0);
2588 setCursor(bview, cursor.par(), cursor.pos());
2592 Paragraph * LyXText::ownerParagraph() const
2595 return inset_owner->paragraph();
2597 return bv_owner->buffer()->paragraph;
2601 void LyXText::ownerParagraph(Paragraph * p) const
2604 inset_owner->paragraph(p);
2606 bv_owner->buffer()->paragraph = p;
2611 void LyXText::ownerParagraph(int id, Paragraph * p) const
2613 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2614 if (op && op->inInset()) {
2615 static_cast<InsetText *>(op->inInset())->paragraph(p);
2622 LyXText::text_status LyXText::status() const
2628 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2630 // well as much as I know && binds more then || so the above and the
2631 // below are identical (this for your known use of parentesis!)
2632 // Now some explanation:
2633 // We should only go up with refreshing code so this means that if
2634 // we have a MORE refresh we should never set it to LITTLE if we still
2635 // didn't handle it (and then it will be UNCHANGED. Now as long as
2636 // we stay inside one LyXText this may work but we need to tell the
2637 // outermost LyXText that it should REALLY draw us if there is some
2638 // change in a Inset::LyXText. So you see that when we are inside a
2639 // inset's LyXText we give the LITTLE to the outermost LyXText to
2640 // tell'em that it should redraw the actual row (where the inset
2641 // resides! Capito?!
2643 if ((status_ != NEED_MORE_REFRESH)
2644 || (status_ == NEED_MORE_REFRESH
2645 && st != NEED_VERY_LITTLE_REFRESH))
2648 if (inset_owner && st != UNCHANGED) {
2649 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2650 if (!bview->text->refresh_row) {
2651 bview->text->refresh_row = bview->text->cursor.row();
2652 bview->text->refresh_y = bview->text->cursor.y() -
2653 bview->text->cursor.row()->baseline();