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"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
66 LyXText::LyXText(InsetText * inset)
67 : number_of_rows(0), height(0), width(0), first(0),
68 bv_owner(0), inset_owner(inset), the_locking_inset(0),
69 need_break_row(0), refresh_y(0), refresh_row(0),
70 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
74 void LyXText::init(BufferView * bview, bool reinit)
77 // Delete all rows, this does not touch the paragraphs!
78 Row * tmprow = firstrow;
80 tmprow = firstrow->next();
84 lastrow = refresh_row = need_break_row = 0;
85 width = height = copylayouttype = 0;
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font);
132 tmpfont.realize(textclasslist.
133 Style(buf->params.textclass,
134 par->getLayout()).font,
135 buf->params.language);
137 par_depth = par->getDepth();
141 #ifndef INHERIT_LANGUAGE
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
144 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
145 buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout const & layout =
167 textclasslist.Style(buf->params.textclass, par->getLayout());
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout.labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params,
177 #ifndef INHERIT_LANGUAGE
178 return f.realize(layout.reslabelfont);
180 return f.realize(layout.reslabelfont, buf->params.language);
183 LyXFont f = par->getFontSettings(buf->params, pos);
184 #ifndef INHERIT_LANGUAGE
185 return f.realize(layout.resfont);
187 return f.realize(layout.resfont, buf->params.language);
192 // The uncommon case need not be optimized as much
196 if (pos < beginningOfMainBody(buf, par)) {
198 layoutfont = layout.labelfont;
201 layoutfont = layout.font;
204 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
205 #ifndef INHERIT_LANGUAGE
206 tmpfont.realize(layoutfont);
208 tmpfont.realize(layoutfont, buf->params.language);
211 return realizeFont(tmpfont, buf, par);
215 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
217 LyXLayout const & layout =
218 textclasslist.Style(buf->params.textclass, par->getLayout());
220 Paragraph::depth_type par_depth = par->getDepth();
223 return layout.resfont;
226 return realizeFont(layout.font, buf, par);
230 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
232 LyXLayout const & layout =
233 textclasslist.Style(buf->params.textclass, par->getLayout());
235 Paragraph::depth_type par_depth = par->getDepth();
238 return layout.reslabelfont;
241 return realizeFont(layout.labelfont, buf, par);
245 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
246 pos_type pos, LyXFont const & fnt,
249 Buffer const * buf = bv->buffer();
250 LyXFont font = getFont(buf, par, pos);
251 font.update(fnt, buf->params.language, toggleall);
252 // Let the insets convert their font
253 if (par->isInset(pos)) {
254 Inset * inset = par->getInset(pos);
255 if (isEditableInset(inset)) {
256 UpdatableInset * uinset =
257 static_cast<UpdatableInset *>(inset);
258 uinset->setFont(bv, fnt, toggleall, true);
262 LyXLayout const & layout =
263 textclasslist.Style(buf->params.textclass,
266 // Get concrete layout font to reduce against
269 if (pos < beginningOfMainBody(buf, par))
270 layoutfont = layout.labelfont;
272 layoutfont = layout.font;
274 // Realize against environment font information
275 if (par->getDepth()){
276 Paragraph * tp = par;
277 while (!layoutfont.resolved() && tp && tp->getDepth()) {
278 tp = tp->outerHook();
280 #ifndef INHERIT_LANGUAGE
281 layoutfont.realize(textclasslist.
282 Style(buf->params.textclass,
283 tp->getLayout()).font);
285 layoutfont.realize(textclasslist.
286 Style(buf->params.textclass,
287 tp->getLayout()).font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
296 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
297 buf->params.language);
300 // Now, reduce font against full layout font
301 font.reduce(layoutfont);
303 par->setFont(pos, font);
307 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
308 pos_type pos, LyXFont const & fnt)
312 LyXLayout const & layout =
313 textclasslist.Style(buf->params.textclass,
316 // Get concrete layout font to reduce against
319 if (pos < beginningOfMainBody(buf, par))
320 layoutfont = layout.labelfont;
322 layoutfont = layout.font;
324 // Realize against environment font information
325 if (par->getDepth()){
326 Paragraph * tp = par;
327 while (!layoutfont.resolved() && tp && tp->getDepth()) {
328 tp = tp->outerHook();
330 #ifndef INHERIT_LANGUAGE
331 layoutfont.realize(textclasslist.
332 Style(buf->params.textclass,
333 tp->getLayout()).font);
335 layoutfont.realize(textclasslist.
336 Style(buf->params.textclass,
337 tp->getLayout()).font,
338 buf->params.language);
343 #ifndef INHERIT_LANGUAGE
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
346 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
347 buf->params.language);
350 // Now, reduce font against full layout font
351 font.reduce(layoutfont);
353 par->setFont(pos, font);
357 // inserts a new row behind the specified row, increments
358 // the touched counters
359 void LyXText::insertRow(Row * row, Paragraph * par,
362 Row * tmprow = new Row;
365 tmprow->next(firstrow);
368 tmprow->previous(row);
369 tmprow->next(row->next());
374 tmprow->next()->previous(tmprow);
376 if (tmprow->previous())
377 tmprow->previous()->next(tmprow);
389 // removes the row and reset the touched counters
390 void LyXText::removeRow(Row * row) const
392 Row * row_prev = row->previous();
394 row->next()->previous(row_prev);
396 firstrow = row->next();
397 // lyx::Assert(firstrow);
399 row_prev->next(row->next());
401 if (row == lastrow) {
402 lyx::Assert(!row->next());
405 if (refresh_row == row) {
406 refresh_row = row_prev ? row_prev : row->next();
407 // what about refresh_y, refresh_height
410 height -= row->height(); // the text becomes smaller
413 --number_of_rows; // one row less
417 // remove all following rows of the paragraph of the specified row.
418 void LyXText::removeParagraph(Row * row) const
420 Paragraph * tmppar = row->par();
424 while (row && row->par() == tmppar) {
425 tmprow = row->next();
432 // insert the specified paragraph behind the specified row
433 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
436 insertRow(row, par, 0); /* insert a new row, starting
439 setCounter(bview->buffer(), par); // set the counters
441 // and now append the whole paragraph behind the new row
444 appendParagraph(bview, firstrow);
446 row->next()->height(0);
447 appendParagraph(bview, row->next());
452 Inset * LyXText::getInset() const
455 if (cursor.pos() == 0 && cursor.par()->bibkey) {
456 inset = cursor.par()->bibkey;
457 } else if (cursor.pos() < cursor.par()->size()
458 && cursor.par()->isInset(cursor.pos())) {
459 inset = cursor.par()->getInset(cursor.pos());
465 void LyXText::toggleInset(BufferView * bview)
467 Inset * inset = getInset();
468 if (!isEditableInset(inset))
470 //bview->owner()->message(inset->editMessage());
472 // do we want to keep this?? (JMarc)
473 if (!isHighlyEditableInset(inset))
474 setCursorParUndo(bview);
476 if (inset->isOpen()) {
482 inset->open(bview, !inset->isOpen());
487 /* used in setlayout */
488 // Asger is not sure we want to do this...
489 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
492 LyXLayout const & layout =
493 textclasslist.Style(buf->params.textclass, par->getLayout());
496 for (pos_type pos = 0; pos < par->size(); ++pos) {
497 if (pos < beginningOfMainBody(buf, par))
498 layoutfont = layout.labelfont;
500 layoutfont = layout.font;
502 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
503 tmpfont.reduce(layoutfont);
504 par->setFont(pos, tmpfont);
509 Paragraph * LyXText::setLayout(BufferView * bview,
510 LyXCursor & cur, LyXCursor & sstart_cur,
511 LyXCursor & send_cur,
512 lyx::layout_type layout)
514 Paragraph * endpar = send_cur.par()->next();
515 Paragraph * undoendpar = endpar;
517 if (endpar && endpar->getDepth()) {
518 while (endpar && endpar->getDepth()) {
519 endpar = endpar->next();
523 endpar = endpar->next(); // because of parindents etc.
526 setUndo(bview, Undo::EDIT,
527 sstart_cur.par(), undoendpar);
529 // ok we have a selection. This is always between sstart_cur
530 // and sel_end cursor
533 LyXLayout const & lyxlayout =
534 textclasslist.Style(bview->buffer()->params.textclass, layout);
536 while (cur.par() != send_cur.par()) {
537 cur.par()->setLayout(layout);
538 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
539 Paragraph * fppar = cur.par();
540 fppar->params().spaceTop(lyxlayout.fill_top ?
541 VSpace(VSpace::VFILL)
542 : VSpace(VSpace::NONE));
543 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
544 VSpace(VSpace::VFILL)
545 : VSpace(VSpace::NONE));
546 if (lyxlayout.margintype == MARGIN_MANUAL)
547 cur.par()->setLabelWidthString(lyxlayout.labelstring());
548 if (lyxlayout.labeltype != LABEL_BIBLIO
550 delete fppar->bibkey;
553 cur.par(cur.par()->next());
555 cur.par()->setLayout(layout);
556 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
557 Paragraph * fppar = cur.par();
558 fppar->params().spaceTop(lyxlayout.fill_top ?
559 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
560 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
561 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
562 if (lyxlayout.margintype == MARGIN_MANUAL)
563 cur.par()->setLabelWidthString(lyxlayout.labelstring());
564 if (lyxlayout.labeltype != LABEL_BIBLIO
566 delete fppar->bibkey;
573 // set layout over selection and make a total rebreak of those paragraphs
574 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
576 LyXCursor tmpcursor = cursor; /* store the current cursor */
578 // if there is no selection just set the layout
579 // of the current paragraph */
580 if (!selection.set()) {
581 selection.start = cursor; // dummy selection
582 selection.end = cursor;
584 Paragraph * endpar = setLayout(bview, cursor, selection.start,
585 selection.end, layout);
586 redoParagraphs(bview, selection.start, endpar);
588 // we have to reset the selection, because the
589 // geometry could have changed
590 setCursor(bview, selection.start.par(),
591 selection.start.pos(), false);
592 selection.cursor = cursor;
593 setCursor(bview, selection.end.par(), selection.end.pos(), false);
594 updateCounters(bview, cursor.row());
597 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
601 // increment depth over selection and
602 // make a total rebreak of those paragraphs
603 void LyXText::incDepth(BufferView * bview)
605 // If there is no selection, just use the current paragraph
606 if (!selection.set()) {
607 selection.start = cursor; // dummy selection
608 selection.end = cursor;
611 // We end at the next paragraph with depth 0
612 Paragraph * endpar = selection.end.par()->next();
614 Paragraph * undoendpar = endpar;
616 if (endpar && endpar->getDepth()) {
617 while (endpar && endpar->getDepth()) {
618 endpar = endpar->next();
622 endpar = endpar->next(); // because of parindents etc.
625 setUndo(bview, Undo::EDIT,
626 selection.start.par(), undoendpar);
628 LyXCursor tmpcursor = cursor; // store the current cursor
630 // ok we have a selection. This is always between sel_start_cursor
631 // and sel_end cursor
632 cursor = selection.start;
634 bool anything_changed = false;
637 // NOTE: you can't change the depth of a bibliography entry
639 textclasslist.Style(bview->buffer()->params.textclass,
640 cursor.par()->getLayout()
641 ).labeltype != LABEL_BIBLIO) {
642 Paragraph * prev = cursor.par()->previous();
645 && (prev->getDepth() - cursor.par()->getDepth() > 0
646 || (prev->getDepth() == cursor.par()->getDepth()
647 && textclasslist.Style(bview->buffer()->params.textclass,
648 prev->getLayout()).isEnvironment()))) {
649 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
650 anything_changed = true;
653 if (cursor.par() == selection.end.par())
655 cursor.par(cursor.par()->next());
658 // if nothing changed set all depth to 0
659 if (!anything_changed) {
660 cursor = selection.start;
661 while (cursor.par() != selection.end.par()) {
662 cursor.par()->params().depth(0);
663 cursor.par(cursor.par()->next());
665 cursor.par()->params().depth(0);
668 redoParagraphs(bview, selection.start, endpar);
670 // we have to reset the selection, because the
671 // geometry could have changed
672 setCursor(bview, selection.start.par(), selection.start.pos());
673 selection.cursor = cursor;
674 setCursor(bview, selection.end.par(), selection.end.pos());
675 updateCounters(bview, cursor.row());
678 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
682 // decrement depth over selection and
683 // make a total rebreak of those paragraphs
684 void LyXText::decDepth(BufferView * bview)
686 // if there is no selection just set the layout
687 // of the current paragraph
688 if (!selection.set()) {
689 selection.start = cursor; // dummy selection
690 selection.end = cursor;
692 Paragraph * endpar = selection.end.par()->next();
693 Paragraph * undoendpar = endpar;
695 if (endpar && endpar->getDepth()) {
696 while (endpar && endpar->getDepth()) {
697 endpar = endpar->next();
701 endpar = endpar->next(); // because of parindents etc.
704 setUndo(bview, Undo::EDIT,
705 selection.start.par(), undoendpar);
707 LyXCursor tmpcursor = cursor; // store the current cursor
709 // ok we have a selection. This is always between sel_start_cursor
710 // and sel_end cursor
711 cursor = selection.start;
714 if (cursor.par()->params().depth()) {
715 cursor.par()->params()
716 .depth(cursor.par()->params().depth() - 1);
718 if (cursor.par() == selection.end.par()) {
721 cursor.par(cursor.par()->next());
724 redoParagraphs(bview, selection.start, endpar);
726 // we have to reset the selection, because the
727 // geometry could have changed
728 setCursor(bview, selection.start.par(),
729 selection.start.pos());
730 selection.cursor = cursor;
731 setCursor(bview, selection.end.par(), selection.end.pos());
732 updateCounters(bview, cursor.row());
735 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
739 // set font over selection and make a total rebreak of those paragraphs
740 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
742 // if there is no selection just set the current_font
743 if (!selection.set()) {
744 // Determine basis font
746 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
748 layoutfont = getLabelFont(bview->buffer(),
751 layoutfont = getLayoutFont(bview->buffer(),
754 // Update current font
755 real_current_font.update(font,
756 bview->buffer()->params.language,
759 // Reduce to implicit settings
760 current_font = real_current_font;
761 current_font.reduce(layoutfont);
762 // And resolve it completely
763 #ifndef INHERIT_LANGUAGE
764 real_current_font.realize(layoutfont);
766 real_current_font.realize(layoutfont,
767 bview->buffer()->params.language);
772 LyXCursor tmpcursor = cursor; // store the current cursor
774 // ok we have a selection. This is always between sel_start_cursor
775 // and sel_end cursor
777 setUndo(bview, Undo::EDIT,
778 selection.start.par(), selection.end.par()->next());
780 cursor = selection.start;
781 while (cursor.par() != selection.end.par() ||
782 (cursor.pos() < selection.end.pos()))
784 if (cursor.pos() < cursor.par()->size()) {
785 // an open footnote should behave
787 setCharFont(bview, cursor.par(), cursor.pos(),
789 cursor.pos(cursor.pos() + 1);
792 cursor.par(cursor.par()->next());
797 redoParagraphs(bview, selection.start, selection.end.par()->next());
799 // we have to reset the selection, because the
800 // geometry could have changed, but we keep
801 // it for user convenience
802 setCursor(bview, selection.start.par(), selection.start.pos());
803 selection.cursor = cursor;
804 setCursor(bview, selection.end.par(), selection.end.pos());
806 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
807 tmpcursor.boundary());
811 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
813 Row * tmprow = cur.row();
814 int y = cur.y() - tmprow->baseline();
816 setHeightOfRow(bview, tmprow);
818 while (tmprow->previous()
819 && tmprow->previous()->par() == tmprow->par()) {
820 tmprow = tmprow->previous();
821 y -= tmprow->height();
822 setHeightOfRow(bview, tmprow);
825 // we can set the refreshing parameters now
826 status(bview, LyXText::NEED_MORE_REFRESH);
828 refresh_row = tmprow;
829 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
833 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
835 Row * tmprow = cur.row();
837 int y = cur.y() - tmprow->baseline();
838 setHeightOfRow(bview, tmprow);
840 while (tmprow->previous()
841 && tmprow->previous()->par() == tmprow->par()) {
842 tmprow = tmprow->previous();
843 y -= tmprow->height();
846 // we can set the refreshing parameters now
847 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
849 refresh_row = tmprow;
851 status(bview, LyXText::NEED_MORE_REFRESH);
852 setCursor(bview, cur.par(), cur.pos());
856 // deletes and inserts again all paragaphs between the cursor
857 // and the specified par
858 // This function is needed after SetLayout and SetFont etc.
859 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
860 Paragraph const * endpar) const
863 Paragraph * tmppar = 0;
864 Paragraph * first_phys_par = 0;
866 Row * tmprow = cur.row();
868 int y = cur.y() - tmprow->baseline();
870 if (!tmprow->previous()) {
871 // a trick/hack for UNDO
872 // This is needed because in an UNDO/REDO we could have changed
873 // the ownerParagrah() so the paragraph inside the row is NOT
874 // my really first par anymore. Got it Lars ;) (Jug 20011206)
875 first_phys_par = ownerParagraph();
877 first_phys_par = tmprow->par();
878 while (tmprow->previous()
879 && tmprow->previous()->par() == first_phys_par)
881 tmprow = tmprow->previous();
882 y -= tmprow->height();
886 // we can set the refreshing parameters now
887 status(bview, LyXText::NEED_MORE_REFRESH);
889 refresh_row = tmprow->previous(); /* the real refresh row will
890 be deleted, so I store
894 tmppar = tmprow->next()->par();
897 while (tmprow->next() && tmppar != endpar) {
898 removeRow(tmprow->next());
899 if (tmprow->next()) {
900 tmppar = tmprow->next()->par();
906 // remove the first one
907 tmprow2 = tmprow; /* this is because tmprow->previous()
909 tmprow = tmprow->previous();
912 tmppar = first_phys_par;
916 insertParagraph(bview, tmppar, tmprow);
920 while (tmprow->next()
921 && tmprow->next()->par() == tmppar) {
922 tmprow = tmprow->next();
924 tmppar = tmppar->next();
926 } while (tmppar && tmppar != endpar);
928 // this is because of layout changes
930 refresh_y -= refresh_row->height();
931 setHeightOfRow(bview, refresh_row);
933 refresh_row = firstrow;
935 setHeightOfRow(bview, refresh_row);
938 if (tmprow && tmprow->next())
939 setHeightOfRow(bview, tmprow->next());
943 bool LyXText::fullRebreak(BufferView * bview)
949 if (need_break_row) {
950 breakAgain(bview, need_break_row);
958 // important for the screen
961 /* the cursor set functions have a special mechanism. When they
962 * realize, that you left an empty paragraph, they will delete it.
963 * They also delete the corresponding row */
965 // need the selection cursor:
966 void LyXText::setSelection(BufferView * bview)
968 bool const lsel = selection.set();
970 if (!selection.set()) {
971 last_sel_cursor = selection.cursor;
972 selection.start = selection.cursor;
973 selection.end = selection.cursor;
978 // first the toggling area
979 if (cursor.y() < last_sel_cursor.y()
980 || (cursor.y() == last_sel_cursor.y()
981 && cursor.x() < last_sel_cursor.x())) {
982 toggle_end_cursor = last_sel_cursor;
983 toggle_cursor = cursor;
985 toggle_end_cursor = cursor;
986 toggle_cursor = last_sel_cursor;
989 last_sel_cursor = cursor;
991 // and now the whole selection
993 if (selection.cursor.par() == cursor.par())
994 if (selection.cursor.pos() < cursor.pos()) {
995 selection.end = cursor;
996 selection.start = selection.cursor;
998 selection.end = selection.cursor;
999 selection.start = cursor;
1001 else if (selection.cursor.y() < cursor.y() ||
1002 (selection.cursor.y() == cursor.y()
1003 && selection.cursor.x() < cursor.x())) {
1004 selection.end = cursor;
1005 selection.start = selection.cursor;
1008 selection.end = selection.cursor;
1009 selection.start = cursor;
1012 // a selection with no contents is not a selection
1013 if (selection.start.par() == selection.end.par() &&
1014 selection.start.pos() == selection.end.pos())
1015 selection.set(false);
1017 if (inset_owner && (selection.set() || lsel))
1018 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1022 string const LyXText::selectionAsString(Buffer const * buffer,
1025 if (!selection.set()) return string();
1028 // Special handling if the whole selection is within one paragraph
1029 if (selection.start.par() == selection.end.par()) {
1030 result += selection.start.par()->asString(buffer,
1031 selection.start.pos(),
1032 selection.end.pos(),
1037 // The selection spans more than one paragraph
1039 // First paragraph in selection
1040 result += selection.start.par()->asString(buffer,
1041 selection.start.pos(),
1042 selection.start.par()->size(),
1046 // The paragraphs in between (if any)
1047 LyXCursor tmpcur(selection.start);
1048 tmpcur.par(tmpcur.par()->next());
1049 while (tmpcur.par() != selection.end.par()) {
1050 result += tmpcur.par()->asString(buffer, 0,
1051 tmpcur.par()->size(),
1053 tmpcur.par(tmpcur.par()->next());
1056 // Last paragraph in selection
1057 result += selection.end.par()->asString(buffer, 0,
1058 selection.end.pos(), label);
1064 void LyXText::clearSelection() const
1066 selection.set(false);
1067 selection.mark(false);
1068 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1072 void LyXText::cursorHome(BufferView * bview) const
1074 setCursor(bview, cursor.par(), cursor.row()->pos());
1078 void LyXText::cursorEnd(BufferView * bview) const
1080 if (!cursor.row()->next()
1081 || cursor.row()->next()->par() != cursor.row()->par()) {
1082 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1084 if (cursor.par()->size() &&
1085 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1086 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1087 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1089 setCursor(bview,cursor.par(),
1090 rowLast(cursor.row()) + 1);
1096 void LyXText::cursorTop(BufferView * bview) const
1098 while (cursor.par()->previous())
1099 cursor.par(cursor.par()->previous());
1100 setCursor(bview, cursor.par(), 0);
1104 void LyXText::cursorBottom(BufferView * bview) const
1106 while (cursor.par()->next())
1107 cursor.par(cursor.par()->next());
1108 setCursor(bview, cursor.par(), cursor.par()->size());
1112 void LyXText::toggleFree(BufferView * bview,
1113 LyXFont const & font, bool toggleall)
1115 // If the mask is completely neutral, tell user
1116 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1117 // Could only happen with user style
1118 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1122 // Try implicit word selection
1123 // If there is a change in the language the implicit word selection
1125 LyXCursor resetCursor = cursor;
1126 bool implicitSelection = (font.language() == ignore_language
1127 && font.number() == LyXFont::IGNORE)
1128 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1131 setFont(bview, font, toggleall);
1133 // Implicit selections are cleared afterwards
1134 //and cursor is set to the original position.
1135 if (implicitSelection) {
1137 cursor = resetCursor;
1138 setCursor(bview, cursor.par(), cursor.pos());
1139 selection.cursor = cursor;
1142 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1146 string LyXText::getStringToIndex(BufferView * bview)
1150 // Try implicit word selection
1151 // If there is a change in the language the implicit word selection
1153 LyXCursor resetCursor = cursor;
1154 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1156 if (!selection.set()) {
1157 bview->owner()->message(_("Nothing to index!"));
1160 if (selection.start.par() != selection.end.par()) {
1161 bview->owner()->message(_("Cannot index more than one paragraph!"));
1165 idxstring = selectionAsString(bview->buffer(), false);
1167 // Implicit selections are cleared afterwards
1168 //and cursor is set to the original position.
1169 if (implicitSelection) {
1171 cursor = resetCursor;
1172 setCursor(bview, cursor.par(), cursor.pos());
1173 selection.cursor = cursor;
1179 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1180 Paragraph const * par) const
1182 if (textclasslist.Style(buf->params.textclass,
1183 par->getLayout()).labeltype != LABEL_MANUAL)
1186 return par->beginningOfMainBody();
1190 /* the DTP switches for paragraphs. LyX will store them in the
1191 * first physicla paragraph. When a paragraph is broken, the top settings
1192 * rest, the bottom settings are given to the new one. So I can make shure,
1193 * they do not duplicate themself and you cannnot make dirty things with
1196 void LyXText::setParagraph(BufferView * bview,
1197 bool line_top, bool line_bottom,
1198 bool pagebreak_top, bool pagebreak_bottom,
1199 VSpace const & space_top,
1200 VSpace const & space_bottom,
1201 Spacing const & spacing,
1203 string labelwidthstring,
1206 LyXCursor tmpcursor = cursor;
1207 if (!selection.set()) {
1208 selection.start = cursor;
1209 selection.end = cursor;
1212 // make sure that the depth behind the selection are restored, too
1213 Paragraph * endpar = selection.end.par()->next();
1214 Paragraph * undoendpar = endpar;
1216 if (endpar && endpar->getDepth()) {
1217 while (endpar && endpar->getDepth()) {
1218 endpar = endpar->next();
1219 undoendpar = endpar;
1223 // because of parindents etc.
1224 endpar = endpar->next();
1227 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1230 Paragraph * tmppar = selection.end.par();
1231 while (tmppar != selection.start.par()->previous()) {
1232 setCursor(bview, tmppar, 0);
1233 status(bview, LyXText::NEED_MORE_REFRESH);
1234 refresh_row = cursor.row();
1235 refresh_y = cursor.y() - cursor.row()->baseline();
1236 cursor.par()->params().lineTop(line_top);
1237 cursor.par()->params().lineBottom(line_bottom);
1238 cursor.par()->params().pagebreakTop(pagebreak_top);
1239 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1240 cursor.par()->params().spaceTop(space_top);
1241 cursor.par()->params().spaceBottom(space_bottom);
1242 cursor.par()->params().spacing(spacing);
1243 // does the layout allow the new alignment?
1244 if (align == LYX_ALIGN_LAYOUT)
1245 align = textclasslist
1246 .Style(bview->buffer()->params.textclass,
1247 cursor.par()->getLayout()).align;
1248 if (align & textclasslist
1249 .Style(bview->buffer()->params.textclass,
1250 cursor.par()->getLayout()).alignpossible) {
1251 if (align == textclasslist
1252 .Style(bview->buffer()->params.textclass,
1253 cursor.par()->getLayout()).align)
1254 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1256 cursor.par()->params().align(align);
1258 cursor.par()->setLabelWidthString(labelwidthstring);
1259 cursor.par()->params().noindent(noindent);
1260 tmppar = cursor.par()->previous();
1263 redoParagraphs(bview, selection.start, endpar);
1266 setCursor(bview, selection.start.par(), selection.start.pos());
1267 selection.cursor = cursor;
1268 setCursor(bview, selection.end.par(), selection.end.pos());
1269 setSelection(bview);
1270 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1272 bview->updateInset(inset_owner, true);
1276 char loweralphaCounter(int n)
1278 if (n < 1 || n > 26)
1288 char alphaCounter(int n)
1290 if (n < 1 || n > 26)
1298 char hebrewCounter(int n)
1300 static const char hebrew[22] = {
1301 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1302 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1303 '÷', 'ø', 'ù', 'ú'
1305 if (n < 1 || n > 22)
1313 string const romanCounter(int n)
1315 static char const * roman[20] = {
1316 "i", "ii", "iii", "iv", "v",
1317 "vi", "vii", "viii", "ix", "x",
1318 "xi", "xii", "xiii", "xiv", "xv",
1319 "xvi", "xvii", "xviii", "xix", "xx"
1321 if (n < 1 || n > 20)
1330 // set the counter of a paragraph. This includes the labels
1331 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1333 LyXLayout const & layout =
1334 textclasslist.Style(buf->params.textclass,
1337 LyXTextClass const & textclass =
1338 textclasslist.TextClass(buf->params.textclass);
1340 // copy the prev-counters to this one,
1341 // unless this is the first paragraph
1342 if (par->previous()) {
1343 for (int i = 0; i < 10; ++i) {
1344 par->setCounter(i, par->previous()->getFirstCounter(i));
1346 par->params().appendix(par->previous()->params().appendix());
1347 if (!par->params().appendix() && par->params().startOfAppendix()) {
1348 par->params().appendix(true);
1349 for (int i = 0; i < 10; ++i) {
1350 par->setCounter(i, 0);
1353 par->enumdepth = par->previous()->enumdepth;
1354 par->itemdepth = par->previous()->itemdepth;
1356 for (int i = 0; i < 10; ++i) {
1357 par->setCounter(i, 0);
1359 par->params().appendix(par->params().startOfAppendix());
1364 /* Maybe we have to increment the enumeration depth.
1365 * BUT, enumeration in a footnote is considered in isolation from its
1366 * surrounding paragraph so don't increment if this is the
1367 * first line of the footnote
1368 * AND, bibliographies can't have their depth changed ie. they
1369 * are always of depth 0
1372 && par->previous()->getDepth() < par->getDepth()
1373 && textclasslist.Style(buf->params.textclass,
1374 par->previous()->getLayout()
1375 ).labeltype == LABEL_COUNTER_ENUMI
1376 && par->enumdepth < 3
1377 && layout.labeltype != LABEL_BIBLIO) {
1381 // Maybe we have to decrement the enumeration depth, see note above
1383 && par->previous()->getDepth() > par->getDepth()
1384 && layout.labeltype != LABEL_BIBLIO) {
1385 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1386 par->setCounter(6 + par->enumdepth,
1387 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1388 /* reset the counters.
1389 * A depth change is like a breaking layout
1391 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1392 par->setCounter(i, 0);
1395 if (!par->params().labelString().empty()) {
1396 par->params().labelString(string());
1399 if (layout.margintype == MARGIN_MANUAL) {
1400 if (par->params().labelWidthString().empty()) {
1401 par->setLabelWidthString(layout.labelstring());
1404 par->setLabelWidthString(string());
1407 // is it a layout that has an automatic label?
1408 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1410 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1411 if (i >= 0 && i<= buf->params.secnumdepth) {
1412 par->incCounter(i); // increment the counter
1414 // Is there a label? Useful for Chapter layout
1415 if (!par->params().appendix()) {
1416 if (!layout.labelstring().empty())
1417 par->params().labelString(layout.labelstring());
1419 par->params().labelString(string());
1421 if (!layout.labelstring_appendix().empty())
1422 par->params().labelString(layout.labelstring_appendix());
1424 par->params().labelString(string());
1429 if (!par->params().appendix()) {
1430 switch (2 * LABEL_COUNTER_CHAPTER -
1431 textclass.maxcounter() + i) {
1432 case LABEL_COUNTER_CHAPTER:
1433 s << par->getCounter(i);
1435 case LABEL_COUNTER_SECTION:
1436 s << par->getCounter(i - 1) << '.'
1437 << par->getCounter(i);
1439 case LABEL_COUNTER_SUBSECTION:
1440 s << par->getCounter(i - 2) << '.'
1441 << par->getCounter(i - 1) << '.'
1442 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBSUBSECTION:
1445 s << par->getCounter(i - 3) << '.'
1446 << par->getCounter(i - 2) << '.'
1447 << par->getCounter(i - 1) << '.'
1448 << par->getCounter(i);
1451 case LABEL_COUNTER_PARAGRAPH:
1452 s << par->getCounter(i - 4) << '.'
1453 << par->getCounter(i - 3) << '.'
1454 << par->getCounter(i - 2) << '.'
1455 << par->getCounter(i - 1) << '.'
1456 << par->getCounter(i);
1458 case LABEL_COUNTER_SUBPARAGRAPH:
1459 s << par->getCounter(i - 5) << '.'
1460 << par->getCounter(i - 4) << '.'
1461 << par->getCounter(i - 3) << '.'
1462 << par->getCounter(i - 2) << '.'
1463 << par->getCounter(i - 1) << '.'
1464 << par->getCounter(i);
1468 // Can this ever be reached? And in the
1469 // case it is, how can this be correct?
1471 s << par->getCounter(i) << '.';
1474 } else { // appendix
1475 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1476 case LABEL_COUNTER_CHAPTER:
1477 if (par->isRightToLeftPar(buf->params))
1478 s << hebrewCounter(par->getCounter(i));
1480 s << alphaCounter(par->getCounter(i));
1482 case LABEL_COUNTER_SECTION:
1483 if (par->isRightToLeftPar(buf->params))
1484 s << hebrewCounter(par->getCounter(i - 1));
1486 s << alphaCounter(par->getCounter(i - 1));
1489 << par->getCounter(i);
1492 case LABEL_COUNTER_SUBSECTION:
1493 if (par->isRightToLeftPar(buf->params))
1494 s << hebrewCounter(par->getCounter(i - 2));
1496 s << alphaCounter(par->getCounter(i - 2));
1499 << par->getCounter(i-1) << '.'
1500 << par->getCounter(i);
1503 case LABEL_COUNTER_SUBSUBSECTION:
1504 if (par->isRightToLeftPar(buf->params))
1505 s << hebrewCounter(par->getCounter(i-3));
1507 s << alphaCounter(par->getCounter(i-3));
1510 << par->getCounter(i-2) << '.'
1511 << par->getCounter(i-1) << '.'
1512 << par->getCounter(i);
1515 case LABEL_COUNTER_PARAGRAPH:
1516 if (par->isRightToLeftPar(buf->params))
1517 s << hebrewCounter(par->getCounter(i-4));
1519 s << alphaCounter(par->getCounter(i-4));
1522 << par->getCounter(i-3) << '.'
1523 << par->getCounter(i-2) << '.'
1524 << par->getCounter(i-1) << '.'
1525 << par->getCounter(i);
1528 case LABEL_COUNTER_SUBPARAGRAPH:
1529 if (par->isRightToLeftPar(buf->params))
1530 s << hebrewCounter(par->getCounter(i-5));
1532 s << alphaCounter(par->getCounter(i-5));
1535 << par->getCounter(i-4) << '.'
1536 << par->getCounter(i-3) << '.'
1537 << par->getCounter(i-2) << '.'
1538 << par->getCounter(i-1) << '.'
1539 << par->getCounter(i);
1543 // Can this ever be reached? And in the
1544 // case it is, how can this be correct?
1546 s << par->getCounter(i) << '.';
1552 par->params().labelString(par->params().labelString() +s.str().c_str());
1553 // We really want to remove the c_str as soon as
1556 for (i++; i < 10; ++i) {
1557 // reset the following counters
1558 par->setCounter(i, 0);
1560 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1561 for (i++; i < 10; ++i) {
1562 // reset the following counters
1563 par->setCounter(i, 0);
1565 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1566 par->incCounter(i + par->enumdepth);
1567 int number = par->getCounter(i + par->enumdepth);
1571 switch (par->enumdepth) {
1573 if (par->isRightToLeftPar(buf->params))
1575 << hebrewCounter(number)
1579 << loweralphaCounter(number)
1583 if (par->isRightToLeftPar(buf->params))
1584 s << '.' << romanCounter(number);
1586 s << romanCounter(number) << '.';
1589 if (par->isRightToLeftPar(buf->params))
1591 << alphaCounter(number);
1593 s << alphaCounter(number)
1597 if (par->isRightToLeftPar(buf->params))
1604 par->params().labelString(s.str().c_str());
1606 for (i += par->enumdepth + 1; i < 10; ++i) {
1607 // reset the following counters
1608 par->setCounter(i, 0);
1612 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1613 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1615 int number = par->getCounter(i);
1617 InsetCommandParams p( "bibitem" );
1618 par->bibkey = new InsetBibKey(p);
1620 par->bibkey->setCounter(number);
1621 par->params().labelString(layout.labelstring());
1623 // In biblio should't be following counters but...
1625 string s = layout.labelstring();
1627 // the caption hack:
1628 if (layout.labeltype == LABEL_SENSITIVE) {
1629 bool isOK (par->inInset() && par->inInset()->owner() &&
1630 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1633 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1635 = floatList.getType(tmp->type());
1636 // We should get the correct number here too.
1637 s = fl.name() + " #:";
1639 /* par->SetLayout(0);
1640 s = layout->labelstring; */
1641 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1642 ? " :úåòîùî øñç" : "Senseless: ";
1645 par->params().labelString(s);
1647 /* reset the enumeration counter. They are always resetted
1648 * when there is any other layout between */
1649 for (int i = 6 + par->enumdepth; i < 10; ++i)
1650 par->setCounter(i, 0);
1655 // Updates all counters BEHIND the row. Changed paragraphs
1656 // with a dynamic left margin will be rebroken.
1657 void LyXText::updateCounters(BufferView * bview, Row * row) const
1665 par = row->par()->next();
1669 while (row->par() != par)
1672 setCounter(bview->buffer(), par);
1674 // now check for the headline layouts. remember that they
1675 // have a dynamic left margin
1676 if ((textclasslist.Style(bview->buffer()->params.textclass,
1677 par->layout).margintype == MARGIN_DYNAMIC
1678 || textclasslist.Style(bview->buffer()->params.textclass,
1679 par->layout).labeltype == LABEL_SENSITIVE)) {
1681 // Rebreak the paragraph
1682 removeParagraph(row);
1683 appendParagraph(bview, row);
1690 void LyXText::insertInset(BufferView * bview, Inset * inset)
1692 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1694 // I don't know if this is necessary here (Jug 20020102)
1695 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1696 cursor.par()->insertInset(cursor.pos(), inset);
1697 // Just to rebreak and refresh correctly.
1698 // The character will not be inserted a second time
1699 insertChar(bview, Paragraph::META_INSET);
1701 // If we enter a highly editable inset the cursor should be to before
1702 // the inset. This couldn't happen before as Undo was not handled inside
1703 // inset now after the Undo LyX tries to call inset->Edit(...) again
1704 // and cannot do this as the cursor is behind the inset and GetInset
1705 // does not return the inset!
1706 if (isHighlyEditableInset(inset)) {
1707 cursorLeft(bview, true);
1713 void LyXText::copyEnvironmentType()
1715 copylayouttype = cursor.par()->getLayout();
1719 void LyXText::pasteEnvironmentType(BufferView * bview)
1721 setLayout(bview, copylayouttype);
1725 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1727 // Stuff what we got on the clipboard. Even if there is no selection.
1729 // There is a problem with having the stuffing here in that the
1730 // larger the selection the slower LyX will get. This can be
1731 // solved by running the line below only when the selection has
1732 // finished. The solution used currently just works, to make it
1733 // faster we need to be more clever and probably also have more
1734 // calls to stuffClipboard. (Lgb)
1735 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1737 // This doesn't make sense, if there is no selection
1738 if (!selection.set())
1741 // OK, we have a selection. This is always between selection.start
1742 // and selection.end
1744 // make sure that the depth behind the selection are restored, too
1745 Paragraph * endpar = selection.end.par()->next();
1746 Paragraph * undoendpar = endpar;
1748 if (endpar && endpar->getDepth()) {
1749 while (endpar && endpar->getDepth()) {
1750 endpar = endpar->next();
1751 undoendpar = endpar;
1753 } else if (endpar) {
1754 endpar = endpar->next(); // because of parindents etc.
1757 setUndo(bview, Undo::DELETE,
1758 selection.start.par(), undoendpar);
1760 // there are two cases: cut only within one paragraph or
1761 // more than one paragraph
1762 if (selection.start.par() == selection.end.par()) {
1763 // only within one paragraph
1764 endpar = selection.end.par();
1765 int pos = selection.end.pos();
1766 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1767 selection.start.pos(), pos,
1768 bview->buffer()->params.textclass,
1770 selection.end.pos(pos);
1772 endpar = selection.end.par();
1773 int pos = selection.end.pos();
1774 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1775 selection.start.pos(), pos,
1776 bview->buffer()->params.textclass,
1779 selection.end.par(endpar);
1780 selection.end.pos(pos);
1781 cursor.pos(selection.end.pos());
1783 endpar = endpar->next();
1785 // sometimes necessary
1787 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1789 redoParagraphs(bview, selection.start, endpar);
1791 // cutSelection can invalidate the cursor so we need to set
1793 cursor = selection.start;
1795 // need a valid cursor. (Lgb)
1798 setCursor(bview, cursor.par(), cursor.pos());
1799 selection.cursor = cursor;
1800 updateCounters(bview, cursor.row());
1804 void LyXText::copySelection(BufferView * bview)
1806 // stuff the selection onto the X clipboard, from an explicit copy request
1807 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1809 // this doesnt make sense, if there is no selection
1810 if (!selection.set())
1813 // ok we have a selection. This is always between selection.start
1814 // and sel_end cursor
1816 // copy behind a space if there is one
1817 while (selection.start.par()->size() > selection.start.pos()
1818 && selection.start.par()->isLineSeparator(selection.start.pos())
1819 && (selection.start.par() != selection.end.par()
1820 || selection.start.pos() < selection.end.pos()))
1821 selection.start.pos(selection.start.pos() + 1);
1823 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1824 selection.start.pos(), selection.end.pos(),
1825 bview->buffer()->params.textclass);
1829 void LyXText::pasteSelection(BufferView * bview)
1831 // this does not make sense, if there is nothing to paste
1832 if (!CutAndPaste::checkPastePossible(cursor.par()))
1835 setUndo(bview, Undo::INSERT,
1836 cursor.par(), cursor.par()->next());
1839 Paragraph * actpar = cursor.par();
1840 int pos = cursor.pos();
1842 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1843 bview->buffer()->params.textclass);
1845 redoParagraphs(bview, cursor, endpar);
1847 setCursor(bview, cursor.par(), cursor.pos());
1850 setCursor(bview, actpar, pos);
1851 updateCounters(bview, cursor.row());
1855 // sets the selection over the number of characters of string, no check!!
1856 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1861 selection.cursor = cursor;
1862 for (string::size_type i = 0; i < str.length(); ++i)
1864 setSelection(bview);
1868 // simple replacing. The font of the first selected character is used
1869 void LyXText::replaceSelectionWithString(BufferView * bview,
1872 setCursorParUndo(bview);
1875 if (!selection.set()) { // create a dummy selection
1876 selection.end = cursor;
1877 selection.start = cursor;
1880 // Get font setting before we cut
1881 pos_type pos = selection.end.pos();
1882 LyXFont const font = selection.start.par()
1883 ->getFontSettings(bview->buffer()->params,
1884 selection.start.pos());
1886 // Insert the new string
1887 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1888 selection.end.par()->insertChar(pos, (*cit), font);
1892 // Cut the selection
1893 cutSelection(bview, true, false);
1899 // needed to insert the selection
1900 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1902 Paragraph * par = cursor.par();
1903 pos_type pos = cursor.pos();
1904 Paragraph * endpar = cursor.par()->next();
1906 setCursorParUndo(bview);
1908 // only to be sure, should not be neccessary
1911 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1913 redoParagraphs(bview, cursor, endpar);
1914 setCursor(bview, cursor.par(), cursor.pos());
1915 selection.cursor = cursor;
1916 setCursor(bview, par, pos);
1917 setSelection(bview);
1921 // turns double-CR to single CR, others where converted into one
1922 // blank. Then InsertStringAsLines is called
1923 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1925 string linestr(str);
1926 bool newline_inserted = false;
1927 for (string::size_type i = 0; i < linestr.length(); ++i) {
1928 if (linestr[i] == '\n') {
1929 if (newline_inserted) {
1930 // we know that \r will be ignored by
1931 // InsertStringA. Of course, it is a dirty
1932 // trick, but it works...
1933 linestr[i - 1] = '\r';
1937 newline_inserted = true;
1939 } else if (IsPrintable(linestr[i])) {
1940 newline_inserted = false;
1943 insertStringAsLines(bview, linestr);
1947 bool LyXText::gotoNextInset(BufferView * bview,
1948 std::vector<Inset::Code> const & codes,
1949 string const & contents) const
1951 LyXCursor res = cursor;
1954 if (res.pos() < res.par()->size() - 1) {
1955 res.pos(res.pos() + 1);
1957 res.par(res.par()->next());
1961 } while (res.par() &&
1962 !(res.par()->isInset(res.pos())
1963 && (inset = res.par()->getInset(res.pos())) != 0
1964 && find(codes.begin(), codes.end(), inset->lyxCode())
1966 && (contents.empty() ||
1967 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1971 setCursor(bview, res.par(), res.pos(), false);
1978 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1981 LyXCursor tmpcursor;
1985 Row * row = getRow(par, pos, y);
1987 // is there a break one row above
1988 if (row->previous() && row->previous()->par() == row->par()) {
1989 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1990 if (z >= row->pos()) {
1991 // set the dimensions of the row above
1992 y -= row->previous()->height();
1994 refresh_row = row->previous();
1995 status(bview, LyXText::NEED_MORE_REFRESH);
1997 breakAgain(bview, row->previous());
1999 // set the cursor again. Otherwise
2000 // dangling pointers are possible
2001 setCursor(bview, cursor.par(), cursor.pos(),
2002 false, cursor.boundary());
2003 selection.cursor = cursor;
2008 int const tmpheight = row->height();
2009 pos_type const tmplast = rowLast(row);
2013 breakAgain(bview, row);
2014 if (row->height() == tmpheight && rowLast(row) == tmplast)
2015 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2017 status(bview, LyXText::NEED_MORE_REFRESH);
2019 // check the special right address boxes
2020 if (textclasslist.Style(bview->buffer()->params.textclass,
2021 par->getLayout()).margintype
2022 == MARGIN_RIGHT_ADDRESS_BOX)
2030 redoDrawingOfParagraph(bview, tmpcursor);
2033 // set the cursor again. Otherwise dangling pointers are possible
2034 // also set the selection
2036 if (selection.set()) {
2038 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2039 false, selection.cursor.boundary());
2040 selection.cursor = cursor;
2041 setCursorIntern(bview, selection.start.par(),
2042 selection.start.pos(),
2043 false, selection.start.boundary());
2044 selection.start = cursor;
2045 setCursorIntern(bview, selection.end.par(),
2046 selection.end.pos(),
2047 false, selection.end.boundary());
2048 selection.end = cursor;
2049 setCursorIntern(bview, last_sel_cursor.par(),
2050 last_sel_cursor.pos(),
2051 false, last_sel_cursor.boundary());
2052 last_sel_cursor = cursor;
2055 setCursorIntern(bview, cursor.par(), cursor.pos(),
2056 false, cursor.boundary());
2060 // returns false if inset wasn't found
2061 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2063 // first check the current paragraph
2064 int pos = cursor.par()->getPositionOfInset(inset);
2066 checkParagraph(bview, cursor.par(), pos);
2070 // check every paragraph
2072 Paragraph * par = ownerParagraph();
2074 pos = par->getPositionOfInset(inset);
2076 checkParagraph(bview, par, pos);
2086 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2088 bool setfont, bool boundary) const
2090 LyXCursor old_cursor = cursor;
2091 setCursorIntern(bview, par, pos, setfont, boundary);
2092 return deleteEmptyParagraphMechanism(bview, old_cursor);
2096 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2097 pos_type pos, bool boundary) const
2104 cur.boundary(boundary);
2106 // get the cursor y position in text
2108 Row * row = getRow(par, pos, y);
2109 // y is now the beginning of the cursor row
2110 y += row->baseline();
2111 // y is now the cursor baseline
2114 // now get the cursors x position
2116 float fill_separator;
2118 float fill_label_hfill;
2119 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2121 pos_type cursor_vpos = 0;
2122 pos_type last = rowLastPrintable(row);
2124 if (pos > last + 1) {
2125 // This shouldn't happen.
2128 } else if (pos < row->pos()) {
2133 if (last < row->pos())
2134 cursor_vpos = row->pos();
2135 else if (pos > last && !boundary)
2136 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2137 ? row->pos() : last + 1;
2138 else if (pos > row->pos() &&
2139 (pos > last || boundary))
2140 /// Place cursor after char at (logical) position pos - 1
2141 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2142 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2144 /// Place cursor before char at (logical) position pos
2145 cursor_vpos = (bidi_level(pos) % 2 == 0)
2146 ? log2vis(pos) : log2vis(pos) + 1;
2148 pos_type main_body =
2149 beginningOfMainBody(bview->buffer(), row->par());
2150 if ((main_body > 0) &&
2151 ((main_body-1 > last) ||
2152 !row->par()->isLineSeparator(main_body-1)))
2155 for (pos_type vpos = row->pos();
2156 vpos < cursor_vpos; ++vpos) {
2157 pos = vis2log(vpos);
2158 if (main_body > 0 && pos == main_body - 1) {
2159 x += fill_label_hfill +
2160 lyxfont::width(textclasslist.Style(
2161 bview->buffer()->params.textclass,
2162 row->par()->getLayout())
2164 getLabelFont(bview->buffer(), row->par()));
2165 if (row->par()->isLineSeparator(main_body-1))
2166 x -= singleWidth(bview, row->par(),main_body-1);
2168 if (hfillExpansion(bview->buffer(), row, pos)) {
2169 x += singleWidth(bview, row->par(), pos);
2170 if (pos >= main_body)
2173 x += fill_label_hfill;
2174 } else if (row->par()->isSeparator(pos)) {
2175 x += singleWidth(bview, row->par(), pos);
2176 if (pos >= main_body)
2177 x += fill_separator;
2179 x += singleWidth(bview, row->par(), pos);
2188 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2189 pos_type pos, bool setfont, bool boundary) const
2191 InsetText * it = static_cast<InsetText *>(par->inInset());
2193 if (it != inset_owner) {
2194 lyxerr << "InsetText is " << it << endl;
2195 lyxerr << "inset_owner is " << inset_owner << endl;
2196 #warning I believe this code is wrong. (Lgb)
2197 #warning Jürgen, have a look at this. (Lgb)
2198 #warning Hmmm, I guess you are right but we
2199 #warning should verify when this is needed
2200 // Jürgen, would you like to have a look?
2201 // I guess we need to move the outer cursor
2202 // and open and lock the inset (bla bla bla)
2203 // stuff I don't know... so can you have a look?
2205 // I moved the lyxerr stuff in here so we can see if
2206 // this is actually really needed and where!
2208 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2213 setCursor(bview, cursor, par, pos, boundary);
2215 setCurrentFont(bview);
2219 void LyXText::setCurrentFont(BufferView * bview) const
2221 pos_type pos = cursor.pos();
2222 if (cursor.boundary() && pos > 0)
2226 if (pos == cursor.par()->size())
2228 else // potentional bug... BUG (Lgb)
2229 if (cursor.par()->isSeparator(pos)) {
2230 if (pos > cursor.row()->pos() &&
2231 bidi_level(pos) % 2 ==
2232 bidi_level(pos - 1) % 2)
2234 else if (pos + 1 < cursor.par()->size())
2240 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2241 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2243 if (cursor.pos() == cursor.par()->size() &&
2244 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2245 !cursor.boundary()) {
2246 Language const * lang =
2247 cursor.par()->getParLanguage(bview->buffer()->params);
2248 current_font.setLanguage(lang);
2249 current_font.setNumber(LyXFont::OFF);
2250 real_current_font.setLanguage(lang);
2251 real_current_font.setNumber(LyXFont::OFF);
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2258 LyXCursor old_cursor = cursor;
2260 setCursorFromCoordinates(bview, cursor, x, y);
2261 setCurrentFont(bview);
2262 deleteEmptyParagraphMechanism(bview, old_cursor);
2266 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2269 // Get the row first.
2271 Row * row = getRowNearY(y);
2273 pos_type const column = getColumnNearX(bview, row, x, bound);
2274 cur.par(row->par());
2275 cur.pos(row->pos() + column);
2277 cur.y(y + row->baseline());
2279 cur.boundary(bound);
2283 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2285 if (cursor.pos() > 0) {
2286 bool boundary = cursor.boundary();
2287 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2288 if (!internal && !boundary &&
2289 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2290 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2291 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2292 Paragraph * par = cursor.par()->previous();
2293 setCursor(bview, par, par->size());
2298 void LyXText::cursorRight(BufferView * bview, bool internal) const
2300 if (!internal && cursor.boundary() &&
2301 !cursor.par()->isNewline(cursor.pos()))
2302 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2303 else if (cursor.pos() < cursor.par()->size()) {
2304 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2306 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2307 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2308 } else if (cursor.par()->next())
2309 setCursor(bview, cursor.par()->next(), 0);
2313 void LyXText::cursorUp(BufferView * bview) const
2315 setCursorFromCoordinates(bview, cursor.x_fix(),
2316 cursor.y() - cursor.row()->baseline() - 1);
2320 void LyXText::cursorDown(BufferView * bview) const
2322 setCursorFromCoordinates(bview, cursor.x_fix(),
2323 cursor.y() - cursor.row()->baseline()
2324 + cursor.row()->height() + 1);
2328 void LyXText::cursorUpParagraph(BufferView * bview) const
2330 if (cursor.pos() > 0) {
2331 setCursor(bview, cursor.par(), 0);
2333 else if (cursor.par()->previous()) {
2334 setCursor(bview, cursor.par()->previous(), 0);
2339 void LyXText::cursorDownParagraph(BufferView * bview) const
2341 if (cursor.par()->next()) {
2342 setCursor(bview, cursor.par()->next(), 0);
2344 setCursor(bview, cursor.par(), cursor.par()->size());
2348 // fix the cursor `cur' after a characters has been deleted at `where'
2349 // position. Called by deleteEmptyParagraphMechanism
2350 void LyXText::fixCursorAfterDelete(BufferView * bview,
2352 LyXCursor const & where) const
2354 // if cursor is not in the paragraph where the delete occured,
2356 if (cur.par() != where.par())
2359 // if cursor position is after the place where the delete occured,
2361 if (cur.pos() > where.pos())
2362 cur.pos(cur.pos()-1);
2364 // recompute row et al. for this cursor
2365 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2369 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2370 LyXCursor const & old_cursor) const
2372 // Would be wrong to delete anything if we have a selection.
2373 if (selection.set()) return false;
2375 // We allow all kinds of "mumbo-jumbo" when freespacing.
2376 if (textclasslist.Style(bview->buffer()->params.textclass,
2377 old_cursor.par()->getLayout()).free_spacing
2378 || old_cursor.par()->isFreeSpacing())
2383 /* Ok I'll put some comments here about what is missing.
2384 I have fixed BackSpace (and thus Delete) to not delete
2385 double-spaces automagically. I have also changed Cut,
2386 Copy and Paste to hopefully do some sensible things.
2387 There are still some small problems that can lead to
2388 double spaces stored in the document file or space at
2389 the beginning of paragraphs. This happens if you have
2390 the cursor betwenn to spaces and then save. Or if you
2391 cut and paste and the selection have a space at the
2392 beginning and then save right after the paste. I am
2393 sure none of these are very hard to fix, but I will
2394 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2395 that I can get some feedback. (Lgb)
2398 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2399 // delete the LineSeparator.
2402 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2403 // delete the LineSeparator.
2406 // If the pos around the old_cursor were spaces, delete one of them.
2407 if (old_cursor.par() != cursor.par()
2408 || old_cursor.pos() != cursor.pos()) {
2409 // Only if the cursor has really moved
2411 if (old_cursor.pos() > 0
2412 && old_cursor.pos() < old_cursor.par()->size()
2413 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2414 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2415 old_cursor.par()->erase(old_cursor.pos() - 1);
2416 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2418 #ifdef WITH_WARNINGS
2419 #warning This will not work anymore when we have multiple views of the same buffer
2420 // In this case, we will have to correct also the cursors held by
2421 // other bufferviews. It will probably be easier to do that in a more
2422 // automated way in LyXCursor code. (JMarc 26/09/2001)
2424 // correct all cursors held by the LyXText
2425 fixCursorAfterDelete(bview, cursor, old_cursor);
2426 fixCursorAfterDelete(bview, selection.cursor,
2428 fixCursorAfterDelete(bview, selection.start,
2430 fixCursorAfterDelete(bview, selection.end, old_cursor);
2431 fixCursorAfterDelete(bview, last_sel_cursor,
2433 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2434 fixCursorAfterDelete(bview, toggle_end_cursor,
2440 // don't delete anything if this is the ONLY paragraph!
2441 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2444 // Do not delete empty paragraphs with keepempty set.
2445 if ((textclasslist.Style(bview->buffer()->params.textclass,
2446 old_cursor.par()->getLayout())).keepempty)
2449 // only do our magic if we changed paragraph
2450 if (old_cursor.par() == cursor.par())
2453 // record if we have deleted a paragraph
2454 // we can't possibly have deleted a paragraph before this point
2455 bool deleted = false;
2457 if ((old_cursor.par()->size() == 0
2458 || (old_cursor.par()->size() == 1
2459 && old_cursor.par()->isLineSeparator(0)))) {
2460 // ok, we will delete anything
2461 LyXCursor tmpcursor;
2463 // make sure that you do not delete any environments
2464 status(bview, LyXText::NEED_MORE_REFRESH);
2467 if (old_cursor.row()->previous()) {
2468 refresh_row = old_cursor.row()->previous();
2469 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2471 cursor = old_cursor; // that undo can restore the right cursor position
2472 Paragraph * endpar = old_cursor.par()->next();
2473 if (endpar && endpar->getDepth()) {
2474 while (endpar && endpar->getDepth()) {
2475 endpar = endpar->next();
2478 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2482 removeRow(old_cursor.row());
2483 if (ownerParagraph() == old_cursor.par()) {
2484 ownerParagraph(ownerParagraph()->next());
2487 delete old_cursor.par();
2489 /* Breakagain the next par. Needed because of
2490 * the parindent that can occur or dissappear.
2491 * The next row can change its height, if
2492 * there is another layout before */
2493 if (refresh_row->next()) {
2494 breakAgain(bview, refresh_row->next());
2495 updateCounters(bview, refresh_row);
2497 setHeightOfRow(bview, refresh_row);
2499 refresh_row = old_cursor.row()->next();
2500 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2503 cursor = old_cursor; // that undo can restore the right cursor position
2504 Paragraph * endpar = old_cursor.par()->next();
2505 if (endpar && endpar->getDepth()) {
2506 while (endpar && endpar->getDepth()) {
2507 endpar = endpar->next();
2510 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2514 removeRow(old_cursor.row());
2516 if (ownerParagraph() == old_cursor.par()) {
2517 ownerParagraph(ownerParagraph()->next());
2520 delete old_cursor.par();
2522 /* Breakagain the next par. Needed because of
2523 the parindent that can occur or dissappear.
2524 The next row can change its height, if
2525 there is another layout before */
2527 breakAgain(bview, refresh_row);
2528 updateCounters(bview, refresh_row->previous());
2533 setCursorIntern(bview, cursor.par(), cursor.pos());
2535 if (selection.cursor.par() == old_cursor.par()
2536 && selection.cursor.pos() == old_cursor.pos()) {
2537 // correct selection
2538 selection.cursor = cursor;
2542 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2543 redoParagraphs(bview, old_cursor,
2544 old_cursor.par()->next());
2546 setCursorIntern(bview, cursor.par(), cursor.pos());
2547 selection.cursor = cursor;
2554 void LyXText::toggleAppendix(BufferView * bview)
2556 Paragraph * par = cursor.par();
2557 bool start = !par->params().startOfAppendix();
2559 // ensure that we have only one start_of_appendix in this document
2560 Paragraph * tmp = ownerParagraph();
2561 for (; tmp; tmp = tmp->next()) {
2562 tmp->params().startOfAppendix(false);
2565 par->params().startOfAppendix(start);
2567 // we can set the refreshing parameters now
2568 status(bview, LyXText::NEED_MORE_REFRESH);
2570 refresh_row = 0; // not needed for full update
2571 updateCounters(bview, 0);
2572 setCursor(bview, cursor.par(), cursor.pos());
2576 Paragraph * LyXText::ownerParagraph() const
2579 return inset_owner->paragraph();
2581 return bv_owner->buffer()->paragraph;
2585 void LyXText::ownerParagraph(Paragraph * p) const
2588 inset_owner->paragraph(p);
2590 bv_owner->buffer()->paragraph = p;
2595 void LyXText::ownerParagraph(int id, Paragraph * p) const
2597 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2598 if (op && op->inInset()) {
2599 static_cast<InsetText *>(op->inInset())->paragraph(p);
2606 LyXText::text_status LyXText::status() const
2612 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2614 // well as much as I know && binds more then || so the above and the
2615 // below are identical (this for your known use of parentesis!)
2616 // Now some explanation:
2617 // We should only go up with refreshing code so this means that if
2618 // we have a MORE refresh we should never set it to LITTLE if we still
2619 // didn't handle it (and then it will be UNCHANGED. Now as long as
2620 // we stay inside one LyXText this may work but we need to tell the
2621 // outermost LyXText that it should REALLY draw us if there is some
2622 // change in a Inset::LyXText. So you see that when we are inside a
2623 // inset's LyXText we give the LITTLE to the outermost LyXText to
2624 // tell'em that it should redraw the actual row (where the inset
2625 // resides! Capito?!
2627 if ((status_ != NEED_MORE_REFRESH)
2628 || (status_ == NEED_MORE_REFRESH
2629 && st != NEED_VERY_LITTLE_REFRESH))
2632 if (inset_owner && st != UNCHANGED) {
2633 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2634 if (!bview->text->refresh_row) {
2635 bview->text->refresh_row = bview->text->cursor.row();
2636 bview->text->refresh_y = bview->text->cursor.y() -
2637 bview->text->cursor.row()->baseline();