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 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
22 #include "paragraph.h"
23 #include "insets/inseterror.h"
24 #include "insets/insetbib.h"
25 #include "insets/insetspecialchar.h"
26 #include "insets/insettext.h"
27 #include "insets/insetfloat.h"
30 #include "support/textutils.h"
33 #include "bufferparams.h"
34 #include "lyx_gui_misc.h"
36 #include "BufferView.h"
38 #include "CutAndPaste.h"
43 #include "FloatList.h"
45 #include "ParagraphParameters.h"
54 LyXText::LyXText(BufferView * bv)
62 LyXText::LyXText(InsetText * inset)
72 the_locking_inset = 0;
80 status = LyXText::UNCHANGED;
82 // set cursor at the very top position
84 selection = true; /* these setting is necessary
85 because of the delete-empty-
86 paragraph mechanism in
89 selection.set(true); /* these setting is necessary
90 because of the delete-empty-
91 paragraph mechanism in
95 Paragraph * par = ownerParagraph();
96 current_font = getFont(bv_owner->buffer(), par, 0);
98 insertParagraph(bv_owner, par, lastrow);
101 setCursor(bv_owner, firstrow->par(), 0);
103 current_font = LyXFont(LyXFont::ALL_SANE);
110 selection.cursor = cursor;
111 selection.set(false);
112 selection.mark(false);
115 // no rebreak necessary
118 undo_finished = true;
121 // Default layouttype for copy environment type
125 // Dump all rowinformation:
126 Row * tmprow = firstrow;
127 lyxerr << "Baseline Paragraph Pos Height Ascent Fill\n";
129 lyxerr << tmprow->baseline() << '\t'
130 << tmprow->par << '\t'
131 << tmprow->pos() << '\t'
132 << tmprow->height << '\t'
133 << tmprow->ascent_of_text << '\t'
134 << tmprow->fill << '\n';
135 tmprow = tmprow->next();
142 void LyXText::init(BufferView * bview)
147 Paragraph * par = ownerParagraph();
148 current_font = getFont(bview->buffer(), par, 0);
150 insertParagraph(bview, par, lastrow);
153 setCursorIntern(bview, firstrow->par(), 0);
157 selection.cursor = cursor;
160 printf("TP = %x\n",inset_owner->owner());
161 // Dump all rowinformation:
162 Row * tmprow = firstrow;
163 lyxerr << "Width = " << width << endl;
164 lyxerr << "Baseline Paragraph Pos Height Ascent Fill\n";
166 lyxerr << tmprow->baseline() << '\t'
167 << tmprow->par() << '\t'
168 << tmprow->pos() << '\t'
169 << tmprow->height() << '\t'
170 << tmprow->ascent_of_text() << '\t'
171 << tmprow->fill() << '\n';
172 tmprow = tmprow->next();
180 // Delete all rows, this does not touch the paragraphs!
181 Row * tmprow = firstrow;
183 tmprow = firstrow->next();
190 // Gets the fully instantiated font at a given position in a paragraph
191 // Basically the same routine as Paragraph::getFont() in paragraph.C.
192 // The difference is that this one is used for displaying, and thus we
193 // are allowed to make cosmetic improvements. For instance make footnotes
195 // If position is -1, we get the layout font of the paragraph.
196 // If position is -2, we get the font of the manual label of the paragraph.
197 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
198 Paragraph::size_type pos) const
200 LyXLayout const & layout =
201 textclasslist.Style(buf->params.textclass, par->getLayout());
203 Paragraph::depth_type par_depth = par->getDepth();
204 // We specialize the 95% common case:
208 if (layout.labeltype == LABEL_MANUAL
209 && pos < beginningOfMainBody(buf, par)) {
211 LyXFont f = par->getFontSettings(buf->params,
213 return f.realize(layout.reslabelfont);
215 LyXFont f = par->getFontSettings(buf->params, pos);
216 return f.realize(layout.resfont);
221 // process layoutfont for pos == -1 and labelfont for pos < -1
223 return layout.resfont;
225 return layout.reslabelfont;
229 // The uncommon case need not be optimized as much
231 LyXFont layoutfont, tmpfont;
235 if (pos < beginningOfMainBody(buf, par)) {
237 layoutfont = layout.labelfont;
240 layoutfont = layout.font;
242 tmpfont = par->getFontSettings(buf->params, pos);
243 tmpfont.realize(layoutfont);
246 // process layoutfont for pos == -1 and labelfont for pos < -1
248 tmpfont = layout.font;
250 tmpfont = layout.labelfont;
253 // Resolve against environment font information
254 while (par && par_depth && !tmpfont.resolved()) {
255 par = par->outerHook();
257 tmpfont.realize(textclasslist.
258 Style(buf->params.textclass,
259 par->getLayout()).font);
260 par_depth = par->getDepth();
264 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
270 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
271 Paragraph::size_type pos, LyXFont const & fnt,
274 Buffer const * buf = bv->buffer();
275 LyXFont font = getFont(buf, par, pos);
276 font.update(fnt, buf->params.language, toggleall);
277 // Let the insets convert their font
278 if (par->getChar(pos) == Paragraph::META_INSET) {
279 Inset * inset = par->getInset(pos);
281 if (inset->Editable()==Inset::HIGHLY_EDITABLE) {
282 UpdatableInset * uinset = static_cast<UpdatableInset *>(inset);
283 uinset->SetFont(bv, fnt, toggleall, true);
285 font = inset->ConvertFont(font);
289 LyXLayout const & layout =
290 textclasslist.Style(buf->params.textclass,
293 // Get concrete layout font to reduce against
296 if (pos < beginningOfMainBody(buf, par))
297 layoutfont = layout.labelfont;
299 layoutfont = layout.font;
301 // Realize against environment font information
302 if (par->getDepth()){
303 Paragraph * tp = par;
304 while (!layoutfont.resolved() && tp && tp->getDepth()) {
305 tp = tp->outerHook();
307 layoutfont.realize(textclasslist.
308 Style(buf->params.textclass,
309 tp->getLayout()).font);
313 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
315 // Now, reduce font against full layout font
316 font.reduce(layoutfont);
318 par->setFont(pos, font);
321 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
322 Paragraph::size_type pos, LyXFont const & fnt)
325 // Let the insets convert their font
326 if (par->getChar(pos) == Paragraph::META_INSET) {
327 font = par->getInset(pos)->ConvertFont(font);
330 LyXLayout const & layout =
331 textclasslist.Style(buf->params.textclass,
334 // Get concrete layout font to reduce against
337 if (pos < beginningOfMainBody(buf, par))
338 layoutfont = layout.labelfont;
340 layoutfont = layout.font;
342 // Realize against environment font information
343 if (par->getDepth()){
344 Paragraph * tp = par;
345 while (!layoutfont.resolved() && tp && tp->getDepth()) {
346 tp = tp->outerHook();
348 layoutfont.realize(textclasslist.
349 Style(buf->params.textclass,
350 tp->getLayout()).font);
354 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
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,
366 Paragraph::size_type pos) const
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);
391 ++number_of_rows; // one more row
395 // removes the row and reset the touched counters
396 void LyXText::removeRow(Row * row) const
398 /* this must not happen before the currentrow for clear reasons.
399 so the trick is just to set the current row onto the previous
402 getRow(row->par(), row->pos(), unused_y);
405 row->next()->previous(row->previous());
406 if (!row->previous()) {
407 firstrow = row->next();
409 row->previous()->next(row->next());
412 lastrow = row->previous();
414 height -= row->height(); // the text becomes smaller
417 --number_of_rows; // one row less
421 // remove all following rows of the paragraph of the specified row.
422 void LyXText::removeParagraph(Row * row) const
424 Paragraph * tmppar = row->par();
428 while (row && row->par() == tmppar) {
429 tmprow = row->next();
436 // insert the specified paragraph behind the specified row
437 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
440 insertRow(row, par, 0); /* insert a new row, starting
443 setCounter(bview->buffer(), par); // set the counters
445 // and now append the whole paragraph behind the new row
448 appendParagraph(bview, firstrow);
450 row->next()->height(0);
451 appendParagraph(bview, row->next());
456 /* used in setlayout */
457 // Asger is not sure we want to do this...
458 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
462 LyXLayout const & layout =
463 textclasslist.Style(buf->params.textclass, par->getLayout());
465 LyXFont layoutfont, tmpfont;
466 for (Paragraph::size_type pos = 0;
467 pos < par->size(); ++pos) {
468 if (pos < beginningOfMainBody(buf, par))
469 layoutfont = layout.labelfont;
471 layoutfont = layout.font;
473 tmpfont = par->getFontSettings(buf->params, pos);
474 tmpfont.reduce(layoutfont);
475 par->setFont(pos, tmpfont);
480 Paragraph * LyXText::setLayout(BufferView * bview,
481 LyXCursor & cur, LyXCursor & sstart_cur,
482 LyXCursor & send_cur,
483 LyXTextClass::size_type layout)
485 Paragraph * endpar = send_cur.par()->next();
486 Paragraph * undoendpar = endpar;
488 if (endpar && endpar->getDepth()) {
489 while (endpar && endpar->getDepth()) {
490 endpar = endpar->next();
494 endpar = endpar->next(); // because of parindents etc.
497 setUndo(bview->buffer(), Undo::EDIT,
498 sstart_cur.par()->previous(),
501 /* ok we have a selection. This is always between sstart_cur
502 * and sel_end cursor */
505 LyXLayout const & lyxlayout =
506 textclasslist.Style(bview->buffer()->params.textclass, layout);
508 while (cur.par() != send_cur.par()) {
509 cur.par()->setLayout(layout);
510 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
511 Paragraph * fppar = cur.par();
512 fppar->params().spaceTop(lyxlayout.fill_top ?
513 VSpace(VSpace::VFILL)
514 : VSpace(VSpace::NONE));
515 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
516 VSpace(VSpace::VFILL)
517 : VSpace(VSpace::NONE));
518 if (lyxlayout.margintype == MARGIN_MANUAL)
519 cur.par()->setLabelWidthString(lyxlayout.labelstring());
520 if (lyxlayout.labeltype != LABEL_BIBLIO
522 delete fppar->bibkey;
525 cur.par(cur.par()->next());
527 cur.par()->setLayout(layout);
528 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
529 Paragraph * fppar = cur.par();
530 fppar->params().spaceTop(lyxlayout.fill_top ?
531 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
532 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
533 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
534 if (lyxlayout.margintype == MARGIN_MANUAL)
535 cur.par()->setLabelWidthString(lyxlayout.labelstring());
536 if (lyxlayout.labeltype != LABEL_BIBLIO
538 delete fppar->bibkey;
545 // set layout over selection and make a total rebreak of those paragraphs
546 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
549 LyXCursor tmpcursor = cursor; /* store the current cursor */
551 // if there is no selection just set the layout
552 // of the current paragraph */
554 sel_start_cursor = cursor; // dummy selection
555 sel_end_cursor = cursor;
558 endpar = SetLayout(bview, cursor, sel_start_cursor,
559 sel_end_cursor, layout);
560 RedoParagraphs(bview, sel_start_cursor, endpar);
562 // we have to reset the selection, because the
563 // geometry could have changed
564 SetCursor(bview, sel_start_cursor.par(),
565 sel_start_cursor.pos(), false);
567 SetCursor(bview, sel_end_cursor.par(), sel_end_cursor.pos(),
569 UpdateCounters(bview, cursor.row());
570 ClearSelection(bview);
572 SetCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
574 LyXCursor tmpcursor = cursor; /* store the current cursor */
576 // if there is no selection just set the layout
577 // of the current paragraph */
578 if (!selection.set()) {
579 selection.start = cursor; // dummy selection
580 selection.end = cursor;
582 Paragraph * endpar = setLayout(bview, cursor, selection.start,
583 selection.end, layout);
584 redoParagraphs(bview, selection.start, endpar);
586 // we have to reset the selection, because the
587 // geometry could have changed
588 setCursor(bview, selection.start.par(),
589 selection.start.pos(), false);
590 selection.cursor = cursor;
591 setCursor(bview, selection.end.par(), selection.end.pos(),
593 updateCounters(bview, cursor.row());
594 clearSelection(bview);
596 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
602 // increment depth over selection and
603 // make a total rebreak of those paragraphs
604 void LyXText::IncDepth(BufferView * bview)
606 // If there is no selection, just use the current paragraph
608 sel_start_cursor = cursor; // dummy selection
609 sel_end_cursor = cursor;
612 // We end at the next paragraph with depth 0
613 Paragraph * endpar = sel_end_cursor.par()->next();
615 Paragraph * undoendpar = endpar;
617 if (endpar && endpar->getDepth()) {
618 while (endpar && endpar->getDepth()) {
619 endpar = endpar->next();
624 endpar = endpar->next(); // because of parindents etc.
627 SetUndo(bview->buffer(), Undo::EDIT,
628 sel_start_cursor.par()->previous(),
631 LyXCursor tmpcursor = cursor; // store the current cursor
633 // ok we have a selection. This is always between sel_start_cursor
634 // and sel_end cursor
635 cursor = sel_start_cursor;
637 bool anything_changed = false;
640 // NOTE: you can't change the depth of a bibliography entry
642 textclasslist.Style(bview->buffer()->params.textclass,
643 cursor.par()->GetLayout()
644 ).labeltype != LABEL_BIBLIO) {
645 Paragraph * prev = cursor.par()->previous();
648 && (prev->GetDepth() - cursor.par()->GetDepth() > 0
649 || (prev->GetDepth() == cursor.par()->GetDepth()
650 && textclasslist.Style(bview->buffer()->params.textclass,
651 prev->GetLayout()).isEnvironment()))) {
652 cursor.par()->params.depth(cursor.par()->params.depth() + 1);
653 anything_changed = true;
656 if (cursor.par() == sel_end_cursor.par())
658 cursor.par(cursor.par()->next());
661 // if nothing changed set all depth to 0
662 if (!anything_changed) {
663 cursor = sel_start_cursor;
664 while (cursor.par() != sel_end_cursor.par()) {
665 cursor.par()->params.depth(0);
666 cursor.par(cursor.par()->next());
668 cursor.par()->params.depth(0);
671 redoParagraphs(bview, sel_start_cursor, endpar);
673 // we have to reset the selection, because the
674 // geometry could have changed
675 setCursor(bview, sel_start_cursor.par(),
676 sel_start_cursor.pos());
678 setCursor(bview, sel_end_cursor.par(), sel_end_cursor.pos());
679 updateCounters(bview, cursor.row());
680 clearSelection(bview);
682 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
685 // increment depth over selection and
686 // make a total rebreak of those paragraphs
687 void LyXText::incDepth(BufferView * bview)
689 // If there is no selection, just use the current paragraph
690 if (!selection.set()) {
691 selection.start = cursor; // dummy selection
692 selection.end = cursor;
695 // We end at the next paragraph with depth 0
696 Paragraph * endpar = selection.end.par()->next();
698 Paragraph * undoendpar = endpar;
700 if (endpar && endpar->getDepth()) {
701 while (endpar && endpar->getDepth()) {
702 endpar = endpar->next();
707 endpar = endpar->next(); // because of parindents etc.
710 setUndo(bview->buffer(), Undo::EDIT,
711 selection.start.par()->previous(),
714 LyXCursor tmpcursor = cursor; // store the current cursor
716 // ok we have a selection. This is always between sel_start_cursor
717 // and sel_end cursor
718 cursor = selection.start;
720 bool anything_changed = false;
723 // NOTE: you can't change the depth of a bibliography entry
725 textclasslist.Style(bview->buffer()->params.textclass,
726 cursor.par()->getLayout()
727 ).labeltype != LABEL_BIBLIO) {
728 Paragraph * prev = cursor.par()->previous();
731 && (prev->getDepth() - cursor.par()->getDepth() > 0
732 || (prev->getDepth() == cursor.par()->getDepth()
733 && textclasslist.Style(bview->buffer()->params.textclass,
734 prev->getLayout()).isEnvironment()))) {
735 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
736 anything_changed = true;
739 if (cursor.par() == selection.end.par())
741 cursor.par(cursor.par()->next());
744 // if nothing changed set all depth to 0
745 if (!anything_changed) {
746 cursor = selection.start;
747 while (cursor.par() != selection.end.par()) {
748 cursor.par()->params().depth(0);
749 cursor.par(cursor.par()->next());
751 cursor.par()->params().depth(0);
754 redoParagraphs(bview, selection.start, endpar);
756 // we have to reset the selection, because the
757 // geometry could have changed
758 setCursor(bview, selection.start.par(), selection.start.pos());
759 selection.cursor = cursor;
760 setCursor(bview, selection.end.par(), selection.end.pos());
761 updateCounters(bview, cursor.row());
762 clearSelection(bview);
764 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
769 // decrement depth over selection and
770 // make a total rebreak of those paragraphs
771 void LyXText::decDepth(BufferView * bview)
773 // if there is no selection just set the layout
774 // of the current paragraph
775 if (!selection.set()) {
776 selection.start = cursor; // dummy selection
777 selection.end = cursor;
779 Paragraph * endpar = selection.end.par()->next();
780 Paragraph * undoendpar = endpar;
782 if (endpar && endpar->getDepth()) {
783 while (endpar && endpar->getDepth()) {
784 endpar = endpar->next();
788 endpar = endpar->next(); // because of parindents etc.
791 setUndo(bview->buffer(), Undo::EDIT,
792 selection.start.par()->previous(),
795 LyXCursor tmpcursor = cursor; // store the current cursor
797 // ok we have a selection. This is always between sel_start_cursor
798 // and sel_end cursor
799 cursor = selection.start;
802 if (cursor.par()->params().depth())
803 cursor.par()->params().depth(cursor.par()->params().depth() - 1);
804 if (cursor.par() == selection.end.par())
806 cursor.par(cursor.par()->next());
809 redoParagraphs(bview, selection.start, endpar);
811 // we have to reset the selection, because the
812 // geometry could have changed
813 setCursor(bview, selection.start.par(),
814 selection.start.pos());
815 selection.cursor = cursor;
816 setCursor(bview, selection.end.par(), selection.end.pos());
817 updateCounters(bview, cursor.row());
818 clearSelection(bview);
820 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
824 // set font over selection and make a total rebreak of those paragraphs
825 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
827 // if there is no selection just set the current_font
828 if (!selection.set()) {
829 // Determine basis font
831 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
833 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
835 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
836 // Update current font
837 real_current_font.update(font,
838 bview->buffer()->params.language,
841 // Reduce to implicit settings
842 current_font = real_current_font;
843 current_font.reduce(layoutfont);
844 // And resolve it completely
845 real_current_font.realize(layoutfont);
849 LyXCursor tmpcursor = cursor; // store the current cursor
851 // ok we have a selection. This is always between sel_start_cursor
852 // and sel_end cursor
854 setUndo(bview->buffer(), Undo::EDIT,
855 selection.start.par()->previous(),
856 selection.end.par()->next());
858 cursor = selection.start;
859 while (cursor.par() != selection.end.par() ||
860 (cursor.pos() < selection.end.pos())) {
861 if (cursor.pos() < cursor.par()->size()) {
862 // an open footnote should behave
864 setCharFont(bview, cursor.par(), cursor.pos(), font, toggleall);
865 cursor.pos(cursor.pos() + 1);
868 cursor.par(cursor.par()->next());
873 redoParagraphs(bview, selection.start, selection.end.par()->next());
875 // we have to reset the selection, because the
876 // geometry could have changed
877 setCursor(bview, selection.start.par(), selection.start.pos());
878 selection.cursor = cursor;
879 setCursor(bview, selection.end.par(), selection.end.pos());
880 clearSelection(bview);
882 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
883 tmpcursor.boundary());
887 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
889 Row * tmprow = cur.row();
890 int y = cur.y() - tmprow->baseline();
892 setHeightOfRow(bview, tmprow);
893 Paragraph * first_phys_par = tmprow->par();
895 // find the first row of the paragraph
896 if (first_phys_par != tmprow->par())
897 while (tmprow->previous()
898 && tmprow->previous()->par() != first_phys_par) {
899 tmprow = tmprow->previous();
900 y -= tmprow->height();
901 setHeightOfRow(bview, tmprow);
903 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
904 tmprow = tmprow->previous();
905 y -= tmprow->height();
906 setHeightOfRow(bview, tmprow);
909 // we can set the refreshing parameters now
910 status = LyXText::NEED_MORE_REFRESH;
912 refresh_row = tmprow;
913 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
917 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
919 Row * tmprow = cur.row();
921 int y = cur.y() - tmprow->baseline();
922 setHeightOfRow(bview, tmprow);
923 Paragraph * first_phys_par = tmprow->par();
925 // find the first row of the paragraph
926 if (first_phys_par != tmprow->par())
927 while (tmprow->previous() && tmprow->previous()->par() != first_phys_par) {
928 tmprow = tmprow->previous();
929 y -= tmprow->height();
931 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
932 tmprow = tmprow->previous();
933 y -= tmprow->height();
936 // we can set the refreshing parameters now
937 if (status == LyXText::UNCHANGED || y < refresh_y) {
939 refresh_row = tmprow;
941 status = LyXText::NEED_MORE_REFRESH;
942 setCursor(bview, cur.par(), cur.pos());
946 /* deletes and inserts again all paragaphs between the cursor
947 * and the specified par
948 * This function is needed after SetLayout and SetFont etc. */
949 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
950 Paragraph const * endpar) const
953 Paragraph * tmppar = 0, * first_phys_par = 0;
955 Row * tmprow = cur.row();
957 int y = cur.y() - tmprow->baseline();
959 if (!tmprow->previous()){
960 first_phys_par = firstParagraph(); // a trick/hack for UNDO
962 first_phys_par = tmprow->par();
963 // find the first row of the paragraph
964 if (first_phys_par != tmprow->par())
965 while (tmprow->previous() &&
966 (tmprow->previous()->par() != first_phys_par)) {
967 tmprow = tmprow->previous();
968 y -= tmprow->height();
970 while (tmprow->previous()
971 && tmprow->previous()->par() == first_phys_par) {
972 tmprow = tmprow->previous();
973 y -= tmprow->height();
977 // we can set the refreshing parameters now
978 status = LyXText::NEED_MORE_REFRESH;
980 refresh_row = tmprow->previous(); /* the real refresh row will
981 be deleted, so I store
985 tmppar = tmprow->next()->par();
988 while (tmppar != endpar) {
989 removeRow(tmprow->next());
991 tmppar = tmprow->next()->par();
996 // remove the first one
997 tmprow2 = tmprow; /* this is because tmprow->previous()
999 tmprow = tmprow->previous();
1002 tmppar = first_phys_par;
1006 insertParagraph(bview, tmppar, tmprow);
1009 while (tmprow->next() && tmprow->next()->par() == tmppar)
1010 tmprow = tmprow->next();
1011 tmppar = tmppar->next();
1013 } while (tmppar != endpar);
1015 // this is because of layout changes
1017 refresh_y -= refresh_row->height();
1018 setHeightOfRow(bview, refresh_row);
1020 refresh_row = firstrow;
1022 setHeightOfRow(bview, refresh_row);
1025 if (tmprow && tmprow->next())
1026 setHeightOfRow(bview, tmprow->next());
1030 bool LyXText::fullRebreak(BufferView * bview)
1036 if (need_break_row) {
1037 breakAgain(bview, need_break_row);
1045 /* important for the screen */
1048 /* the cursor set functions have a special mechanism. When they
1049 * realize, that you left an empty paragraph, they will delete it.
1050 * They also delete the corresponding row */
1052 // need the selection cursor:
1053 void LyXText::setSelection(BufferView * bview)
1055 bool const lsel = selection.set();
1057 if (!selection.set()) {
1058 last_sel_cursor = selection.cursor;
1059 selection.start = selection.cursor;
1060 selection.end = selection.cursor;
1063 selection.set(true);
1065 // first the toggling area
1066 if (cursor.y() < last_sel_cursor.y()
1067 || (cursor.y() == last_sel_cursor.y()
1068 && cursor.x() < last_sel_cursor.x())) {
1069 toggle_end_cursor = last_sel_cursor;
1070 toggle_cursor = cursor;
1072 toggle_end_cursor = cursor;
1073 toggle_cursor = last_sel_cursor;
1076 last_sel_cursor = cursor;
1078 // and now the whole selection
1080 if (selection.cursor.par() == cursor.par())
1081 if (selection.cursor.pos() < cursor.pos()) {
1082 selection.end = cursor;
1083 selection.start = selection.cursor;
1085 selection.end = selection.cursor;
1086 selection.start = cursor;
1088 else if (selection.cursor.y() < cursor.y() ||
1089 (selection.cursor.y() == cursor.y() && selection.cursor.x() < cursor.x())) {
1090 selection.end = cursor;
1091 selection.start = selection.cursor;
1094 selection.end = selection.cursor;
1095 selection.start = cursor;
1098 // a selection with no contents is not a selection
1099 if (selection.start.par() == selection.end.par() &&
1100 selection.start.pos() == selection.end.pos())
1101 selection.set(false);
1103 if (inset_owner && (selection.set() || lsel))
1104 inset_owner->SetUpdateStatus(bview, InsetText::SELECTION);
1108 string const LyXText::selectionAsString(Buffer const * buffer) const
1110 if (!selection.set()) return string();
1113 // Special handling if the whole selection is within one paragraph
1114 if (selection.start.par() == selection.end.par()) {
1115 result += selection.start.par()->asString(buffer,
1116 selection.start.pos(),
1117 selection.end.pos());
1121 // The selection spans more than one paragraph
1123 // First paragraph in selection
1124 result += selection.start.par()->asString(buffer,
1125 selection.start.pos(),
1126 selection.start.par()->size())
1129 // The paragraphs in between (if any)
1130 LyXCursor tmpcur(selection.start);
1131 tmpcur.par(tmpcur.par()->next());
1132 while (tmpcur.par() != selection.end.par()) {
1133 result += tmpcur.par()->asString(buffer, 0, tmpcur.par()->size()) + "\n\n";
1134 tmpcur.par(tmpcur.par()->next()); // Or NextAfterFootnote??
1137 // Last paragraph in selection
1138 result += selection.end.par()->asString(buffer, 0, selection.end.pos());
1144 void LyXText::clearSelection(BufferView * /*bview*/) const
1146 selection.set(false);
1147 selection.mark(false);
1148 selection.end = selection.start = cursor;
1152 void LyXText::cursorHome(BufferView * bview) const
1154 setCursor(bview, cursor.par(), cursor.row()->pos());
1158 void LyXText::cursorEnd(BufferView * bview) const
1160 if (!cursor.row()->next() || cursor.row()->next()->par() != cursor.row()->par())
1161 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1163 if (cursor.par()->size() &&
1164 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1165 || cursor.par()->isNewline(rowLast(cursor.row()))))
1166 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1168 setCursor(bview,cursor.par(), rowLast(cursor.row()) + 1);
1173 void LyXText::cursorTop(BufferView * bview) const
1175 while (cursor.par()->previous())
1176 cursor.par(cursor.par()->previous());
1177 setCursor(bview, cursor.par(), 0);
1181 void LyXText::cursorBottom(BufferView * bview) const
1183 while (cursor.par()->next())
1184 cursor.par(cursor.par()->next());
1185 setCursor(bview, cursor.par(), cursor.par()->size());
1189 void LyXText::toggleFree(BufferView * bview,
1190 LyXFont const & font, bool toggleall)
1192 // If the mask is completely neutral, tell user
1193 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1194 // Could only happen with user style
1195 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1199 // Try implicit word selection
1200 // If there is a change in the language the implicit word selection
1202 LyXCursor resetCursor = cursor;
1203 bool implicitSelection = (font.language() == ignore_language
1204 && font.number() == LyXFont::IGNORE)
1205 ? selectWordWhenUnderCursor(bview) : false;
1208 setFont(bview, font, toggleall);
1210 /* Implicit selections are cleared afterwards and cursor is set to the
1211 original position. */
1212 if (implicitSelection) {
1213 clearSelection(bview);
1214 cursor = resetCursor;
1215 setCursor(bview, cursor.par(), cursor.pos());
1216 selection.cursor = cursor;
1219 inset_owner->SetUpdateStatus(bview, InsetText::CURSOR_PAR);
1223 Paragraph::size_type
1224 LyXText::beginningOfMainBody(Buffer const * buf,
1225 Paragraph const * par) const
1227 if (textclasslist.Style(buf->params.textclass,
1228 par->getLayout()).labeltype != LABEL_MANUAL)
1231 return par->beginningOfMainBody();
1235 /* the DTP switches for paragraphs. LyX will store them in the
1236 * first physicla paragraph. When a paragraph is broken, the top settings
1237 * rest, the bottom settings are given to the new one. So I can make shure,
1238 * they do not duplicate themself and you cannnot make dirty things with
1241 void LyXText::setParagraph(BufferView * bview,
1242 bool line_top, bool line_bottom,
1243 bool pagebreak_top, bool pagebreak_bottom,
1244 VSpace const & space_top,
1245 VSpace const & space_bottom,
1247 string labelwidthstring,
1250 LyXCursor tmpcursor = cursor;
1251 if (!selection.set()) {
1252 selection.start = cursor;
1253 selection.end = cursor;
1256 // make sure that the depth behind the selection are restored, too
1257 Paragraph * endpar = selection.end.par()->next();
1258 Paragraph * undoendpar = endpar;
1260 if (endpar && endpar->getDepth()) {
1261 while (endpar && endpar->getDepth()) {
1262 endpar = endpar->next();
1263 undoendpar = endpar;
1267 endpar = endpar->next(); // because of parindents etc.
1270 setUndo(bview->buffer(), Undo::EDIT,
1271 selection.start.par()->previous(),
1275 Paragraph * tmppar = selection.end.par();
1276 while (tmppar != selection.start.par()->previous()) {
1277 setCursor(bview, tmppar, 0);
1278 status = LyXText::NEED_MORE_REFRESH;
1279 refresh_row = cursor.row();
1280 refresh_y = cursor.y() - cursor.row()->baseline();
1281 cursor.par()->params().lineTop(line_top);
1282 cursor.par()->params().lineBottom(line_bottom);
1283 cursor.par()->params().pagebreakTop(pagebreak_top);
1284 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1285 cursor.par()->params().spaceTop(space_top);
1286 cursor.par()->params().spaceBottom(space_bottom);
1287 // does the layout allow the new alignment?
1288 if (align == LYX_ALIGN_LAYOUT)
1289 align = textclasslist
1290 .Style(bview->buffer()->params.textclass,
1291 cursor.par()->getLayout()).align;
1292 if (align & textclasslist
1293 .Style(bview->buffer()->params.textclass,
1294 cursor.par()->getLayout()).alignpossible) {
1295 if (align == textclasslist
1296 .Style(bview->buffer()->params.textclass,
1297 cursor.par()->getLayout()).align)
1298 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1300 cursor.par()->params().align(align);
1302 cursor.par()->setLabelWidthString(labelwidthstring);
1303 cursor.par()->params().noindent(noindent);
1304 tmppar = cursor.par()->previous();
1307 redoParagraphs(bview, selection.start, endpar);
1309 clearSelection(bview);
1310 setCursor(bview, selection.start.par(), selection.start.pos());
1311 selection.cursor = cursor;
1312 setCursor(bview, selection.end.par(), selection.end.pos());
1313 setSelection(bview);
1314 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1316 bview->updateInset(inset_owner, true);
1320 char loweralphaCounter(int n)
1322 if (n < 1 || n > 26)
1332 char alphaCounter(int n)
1334 if (n < 1 || n > 26)
1342 char hebrewCounter(int n)
1344 static const char hebrew[22] = {
1345 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1346 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1347 '÷', 'ø', 'ù', 'ú'
1349 if (n < 1 || n > 22)
1357 string const romanCounter(int n)
1359 static char const * roman[20] = {
1360 "i", "ii", "iii", "iv", "v",
1361 "vi", "vii", "viii", "ix", "x",
1362 "xi", "xii", "xiii", "xiv", "xv",
1363 "xvi", "xvii", "xviii", "xix", "xx"
1365 if (n < 1 || n > 20)
1374 // set the counter of a paragraph. This includes the labels
1375 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1377 LyXLayout const & layout =
1378 textclasslist.Style(buf->params.textclass,
1381 LyXTextClass const & textclass =
1382 textclasslist.TextClass(buf->params.textclass);
1384 /* copy the prev-counters to this one, unless this is the start of a
1385 footnote or of a bibliography or the very first paragraph */
1387 && !(textclasslist.Style(buf->params.textclass,
1388 par->previous()->getLayout()
1389 ).labeltype != LABEL_BIBLIO
1390 && layout.labeltype == LABEL_BIBLIO)) {
1391 for (int i = 0; i < 10; ++i) {
1392 par->setCounter(i, par->previous()->getFirstCounter(i));
1394 par->params().appendix(par->previous()->params().appendix());
1395 if (!par->params().appendix() && par->params().startOfAppendix()) {
1396 par->params().appendix(true);
1397 for (int i = 0; i < 10; ++i) {
1398 par->setCounter(i, 0);
1401 par->enumdepth = par->previous()->enumdepth;
1402 par->itemdepth = par->previous()->itemdepth;
1404 for (int i = 0; i < 10; ++i) {
1405 par->setCounter(i, 0);
1407 par->params().appendix(par->params().startOfAppendix());
1412 /* Maybe we have to increment the enumeration depth.
1413 * BUT, enumeration in a footnote is considered in isolation from its
1414 * surrounding paragraph so don't increment if this is the
1415 * first line of the footnote
1416 * AND, bibliographies can't have their depth changed ie. they
1417 * are always of depth 0
1420 && par->previous()->getDepth() < par->getDepth()
1421 && textclasslist.Style(buf->params.textclass,
1422 par->previous()->getLayout()
1423 ).labeltype == LABEL_COUNTER_ENUMI
1424 && par->enumdepth < 3
1425 && layout.labeltype != LABEL_BIBLIO) {
1429 /* Maybe we have to decrement the enumeration depth, see note above */
1431 && par->previous()->getDepth() > par->getDepth()
1432 && layout.labeltype != LABEL_BIBLIO) {
1433 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1434 par->setCounter(6 + par->enumdepth,
1435 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1436 /* reset the counters.
1437 * A depth change is like a breaking layout
1439 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1440 par->setCounter(i, 0);
1443 if (!par->params().labelString().empty()) {
1444 par->params().labelString(string());
1447 if (layout.margintype == MARGIN_MANUAL) {
1448 if (par->params().labelWidthString().empty()) {
1449 par->setLabelWidthString(layout.labelstring());
1452 par->setLabelWidthString(string());
1455 /* is it a layout that has an automatic label ? */
1456 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1458 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1459 if (i >= 0 && i<= buf->params.secnumdepth) {
1460 par->incCounter(i); // increment the counter
1462 // Is there a label? Useful for Chapter layout
1463 if (!par->params().appendix()) {
1464 if (!layout.labelstring().empty())
1465 par->params().labelString(layout.labelstring());
1467 par->params().labelString(string());
1469 if (!layout.labelstring_appendix().empty())
1470 par->params().labelString(layout.labelstring_appendix());
1472 par->params().labelString(string());
1475 std::ostringstream s;
1477 if (!par->params().appendix()) {
1478 switch (2 * LABEL_COUNTER_CHAPTER -
1479 textclass.maxcounter() + i) {
1480 case LABEL_COUNTER_CHAPTER:
1481 s << par->getCounter(i);
1483 case LABEL_COUNTER_SECTION:
1484 s << par->getCounter(i - 1) << '.'
1485 << par->getCounter(i);
1487 case LABEL_COUNTER_SUBSECTION:
1488 s << par->getCounter(i - 2) << '.'
1489 << par->getCounter(i - 1) << '.'
1490 << par->getCounter(i);
1492 case LABEL_COUNTER_SUBSUBSECTION:
1493 s << par->getCounter(i - 3) << '.'
1494 << par->getCounter(i - 2) << '.'
1495 << par->getCounter(i - 1) << '.'
1496 << par->getCounter(i);
1499 case LABEL_COUNTER_PARAGRAPH:
1500 s << par->getCounter(i - 4) << '.'
1501 << par->getCounter(i - 3) << '.'
1502 << par->getCounter(i - 2) << '.'
1503 << par->getCounter(i - 1) << '.'
1504 << par->getCounter(i);
1506 case LABEL_COUNTER_SUBPARAGRAPH:
1507 s << par->getCounter(i - 5) << '.'
1508 << par->getCounter(i - 4) << '.'
1509 << par->getCounter(i - 3) << '.'
1510 << par->getCounter(i - 2) << '.'
1511 << par->getCounter(i - 1) << '.'
1512 << par->getCounter(i);
1516 // Can this ever be reached? And in the
1517 // case it is, how can this be correct?
1519 s << par->getCounter(i) << '.';
1522 } else { // appendix
1523 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1524 case LABEL_COUNTER_CHAPTER:
1525 if (par->isRightToLeftPar(buf->params))
1526 s << hebrewCounter(par->getCounter(i));
1528 s << alphaCounter(par->getCounter(i));
1530 case LABEL_COUNTER_SECTION:
1531 if (par->isRightToLeftPar(buf->params))
1532 s << hebrewCounter(par->getCounter(i - 1));
1534 s << alphaCounter(par->getCounter(i - 1));
1537 << par->getCounter(i);
1540 case LABEL_COUNTER_SUBSECTION:
1541 if (par->isRightToLeftPar(buf->params))
1542 s << hebrewCounter(par->getCounter(i - 2));
1544 s << alphaCounter(par->getCounter(i - 2));
1547 << par->getCounter(i-1) << '.'
1548 << par->getCounter(i);
1551 case LABEL_COUNTER_SUBSUBSECTION:
1552 if (par->isRightToLeftPar(buf->params))
1553 s << hebrewCounter(par->getCounter(i-3));
1555 s << alphaCounter(par->getCounter(i-3));
1558 << par->getCounter(i-2) << '.'
1559 << par->getCounter(i-1) << '.'
1560 << par->getCounter(i);
1563 case LABEL_COUNTER_PARAGRAPH:
1564 if (par->isRightToLeftPar(buf->params))
1565 s << hebrewCounter(par->getCounter(i-4));
1567 s << alphaCounter(par->getCounter(i-4));
1570 << par->getCounter(i-3) << '.'
1571 << par->getCounter(i-2) << '.'
1572 << par->getCounter(i-1) << '.'
1573 << par->getCounter(i);
1576 case LABEL_COUNTER_SUBPARAGRAPH:
1577 if (par->isRightToLeftPar(buf->params))
1578 s << hebrewCounter(par->getCounter(i-5));
1580 s << alphaCounter(par->getCounter(i-5));
1583 << par->getCounter(i-4) << '.'
1584 << par->getCounter(i-3) << '.'
1585 << par->getCounter(i-2) << '.'
1586 << par->getCounter(i-1) << '.'
1587 << par->getCounter(i);
1591 // Can this ever be reached? And in the
1592 // case it is, how can this be correct?
1594 s << par->getCounter(i) << '.';
1600 par->params().labelString(par->params().labelString() +s.str().c_str());
1601 // We really want to remove the c_str as soon as
1604 for (i++; i < 10; ++i) {
1605 // reset the following counters
1606 par->setCounter(i, 0);
1608 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1609 for (i++; i < 10; ++i) {
1610 // reset the following counters
1611 par->setCounter(i, 0);
1613 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1614 par->incCounter(i + par->enumdepth);
1615 int number = par->getCounter(i + par->enumdepth);
1617 std::ostringstream s;
1619 switch (par->enumdepth) {
1621 if (par->isRightToLeftPar(buf->params))
1623 << hebrewCounter(number)
1627 << loweralphaCounter(number)
1631 if (par->isRightToLeftPar(buf->params))
1632 s << '.' << romanCounter(number);
1634 s << romanCounter(number) << '.';
1637 if (par->isRightToLeftPar(buf->params))
1639 << alphaCounter(number);
1641 s << alphaCounter(number)
1645 if (par->isRightToLeftPar(buf->params))
1652 par->params().labelString(s.str().c_str());
1653 // we really want to get rid of that c_str()
1655 for (i += par->enumdepth + 1; i < 10; ++i)
1656 par->setCounter(i, 0); /* reset the following counters */
1659 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1660 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1662 int number = par->getCounter(i);
1664 InsetCommandParams p( "bibitem" );
1665 par->bibkey = new InsetBibKey(p);
1667 par->bibkey->setCounter(number);
1668 par->params().labelString(layout.labelstring());
1670 // In biblio should't be following counters but...
1672 string s = layout.labelstring();
1674 // the caption hack:
1675 if (layout.labeltype == LABEL_SENSITIVE) {
1676 bool isOK (par->InInset() && par->InInset()->owner() &&
1677 (par->InInset()->owner()->LyxCode() == Inset::FLOAT_CODE));
1680 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1682 = floatList.getType(tmp->type());
1683 // We should get the correct number here too.
1684 s = fl.name() + " #:";
1686 /* par->SetLayout(0);
1687 s = layout->labelstring; */
1688 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1689 ? " :úåòîùî øñç" : "Senseless: ";
1692 par->params().labelString(s);
1694 /* reset the enumeration counter. They are always resetted
1695 * when there is any other layout between */
1696 for (int i = 6 + par->enumdepth; i < 10; ++i)
1697 par->setCounter(i, 0);
1702 /* Updates all counters BEHIND the row. Changed paragraphs
1703 * with a dynamic left margin will be rebroken. */
1704 void LyXText::updateCounters(BufferView * bview, Row * row) const
1712 par = row->par()->next();
1716 while (row->par() != par)
1719 setCounter(bview->buffer(), par);
1721 /* now check for the headline layouts. remember that they
1722 * have a dynamic left margin */
1723 if ((textclasslist.Style(bview->buffer()->params.textclass,
1724 par->layout).margintype == MARGIN_DYNAMIC
1725 || textclasslist.Style(bview->buffer()->params.textclass,
1726 par->layout).labeltype == LABEL_SENSITIVE)) {
1728 /* Rebreak the paragraph */
1729 removeParagraph(row);
1730 appendParagraph(bview, row);
1737 /* insets an inset. */
1738 void LyXText::insertInset(BufferView * bview, Inset * inset)
1740 if (!cursor.par()->insertInsetAllowed(inset))
1742 setUndo(bview->buffer(), Undo::INSERT,
1743 cursor.par()->previous(),
1744 cursor.par()->next());
1745 cursor.par()->insertInset(cursor.pos(), inset);
1746 insertChar(bview, Paragraph::META_INSET); /* just to rebreak and refresh correctly.
1747 * The character will not be inserted a
1750 // If we enter a highly editable inset the cursor should be to before
1751 // the inset. This couldn't happen before as Undo was not handled inside
1752 // inset now after the Undo LyX tries to call inset->Edit(...) again
1753 // and cannot do this as the cursor is behind the inset and GetInset
1754 // does not return the inset!
1755 if (inset->Editable() == Inset::HIGHLY_EDITABLE) {
1756 cursorLeft(bview, true);
1762 void LyXText::copyEnvironmentType()
1764 copylayouttype = cursor.par()->getLayout();
1768 void LyXText::pasteEnvironmentType(BufferView * bview)
1770 setLayout(bview, copylayouttype);
1774 void LyXText::cutSelection(BufferView * bview, bool doclear)
1776 // Stuff what we got on the clipboard. Even if there is no selection.
1778 // There is a problem with having the stuffing here in that the
1779 // larger the selection the slower LyX will get. This can be
1780 // solved by running the line below only when the selection has
1781 // finished. The solution used currently just works, to make it
1782 // faster we need to be more clever and probably also have more
1783 // calls to stuffClipboard. (Lgb)
1784 bview->stuffClipboard(selectionAsString(bview->buffer()));
1786 // This doesn't make sense, if there is no selection
1787 if (!selection.set())
1790 // OK, we have a selection. This is always between selection.start
1791 // and selection.end
1793 // make sure that the depth behind the selection are restored, too
1794 Paragraph * endpar = selection.end.par()->next();
1795 Paragraph * undoendpar = endpar;
1797 if (endpar && endpar->getDepth()) {
1798 while (endpar && endpar->getDepth()) {
1799 endpar = endpar->next();
1800 undoendpar = endpar;
1802 } else if (endpar) {
1803 endpar = endpar->next(); // because of parindents etc.
1806 setUndo(bview->buffer(), Undo::DELETE,
1807 selection.start.par()->previous(),
1812 // there are two cases: cut only within one paragraph or
1813 // more than one paragraph
1814 if (selection.start.par() == selection.end.par()) {
1815 // only within one paragraph
1816 endpar = selection.end.par();
1817 int pos = selection.end.pos();
1818 cap.cutSelection(selection.start.par(), &endpar,
1819 selection.start.pos(), pos,
1820 bview->buffer()->params.textclass, doclear);
1821 selection.end.pos(pos);
1823 endpar = selection.end.par();
1824 int pos = selection.end.pos();
1825 cap.cutSelection(selection.start.par(), &endpar,
1826 selection.start.pos(), pos,
1827 bview->buffer()->params.textclass, doclear);
1829 selection.end.par(endpar);
1830 selection.end.pos(pos);
1831 cursor.pos(selection.end.pos());
1833 endpar = endpar->next();
1835 // sometimes necessary
1837 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1839 redoParagraphs(bview, selection.start, endpar);
1841 // cutSelection can invalidate the cursor so we need to set
1843 cursor = selection.start;
1845 // need a valid cursor. (Lgb)
1846 clearSelection(bview);
1848 setCursor(bview, cursor.par(), cursor.pos());
1849 selection.cursor = cursor;
1850 updateCounters(bview, cursor.row());
1854 void LyXText::copySelection(BufferView * bview)
1856 // Stuff what we got on the clipboard. Even if there is no selection.
1858 // There is a problem with having the stuffing here in that the
1859 // larger the selection the slower LyX will get. This can be
1860 // solved by running the line below only when the selection has
1861 // finished. The solution used currently just works, to make it
1862 // faster we need to be more clever and probably also have more
1863 // calls to stuffClipboard. (Lgb)
1864 bview->stuffClipboard(selectionAsString(bview->buffer()));
1866 // this doesnt make sense, if there is no selection
1867 if (!selection.set())
1870 // ok we have a selection. This is always between selection.start
1871 // and sel_end cursor
1873 // copy behind a space if there is one
1874 while (selection.start.par()->size() > selection.start.pos()
1875 && selection.start.par()->isLineSeparator(selection.start.pos())
1876 && (selection.start.par() != selection.end.par()
1877 || selection.start.pos() < selection.end.pos()))
1878 selection.start.pos(selection.start.pos() + 1);
1882 cap.copySelection(selection.start.par(), selection.end.par(),
1883 selection.start.pos(), selection.end.pos(),
1884 bview->buffer()->params.textclass);
1888 void LyXText::pasteSelection(BufferView * bview)
1892 // this does not make sense, if there is nothing to paste
1893 if (!cap.checkPastePossible(cursor.par()))
1896 setUndo(bview->buffer(), Undo::INSERT,
1897 cursor.par()->previous(),
1898 cursor.par()->next());
1901 Paragraph * actpar = cursor.par();
1903 int pos = cursor.pos();
1904 cap.pasteSelection(&actpar, &endpar, pos,
1905 bview->buffer()->params.textclass);
1907 redoParagraphs(bview, cursor, endpar);
1909 setCursor(bview, cursor.par(), cursor.pos());
1910 clearSelection(bview);
1912 selection.cursor = cursor;
1913 setCursor(bview, actpar, pos);
1914 setSelection(bview);
1915 updateCounters(bview, cursor.row());
1919 // returns a pointer to the very first Paragraph
1920 Paragraph * LyXText::firstParagraph() const
1922 return ownerParagraph();
1926 // sets the selection over the number of characters of string, no check!!
1927 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1932 selection.cursor = cursor;
1933 for (string::size_type i = 0; i < str.length(); ++i)
1935 setSelection(bview);
1939 // simple replacing. The font of the first selected character is used
1940 void LyXText::replaceSelectionWithString(BufferView * bview,
1943 setCursorParUndo(bview->buffer());
1946 if (!selection.set()) { // create a dummy selection
1947 selection.end = cursor;
1948 selection.start = cursor;
1951 // Get font setting before we cut
1952 Paragraph::size_type pos = selection.end.pos();
1953 LyXFont const font = selection.start.par()
1954 ->getFontSettings(bview->buffer()->params,
1955 selection.start.pos());
1957 // Insert the new string
1958 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1959 selection.end.par()->insertChar(pos, (*cit), font);
1963 // Cut the selection
1964 cutSelection(bview);
1970 // needed to insert the selection
1971 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1973 Paragraph * par = cursor.par();
1974 Paragraph::size_type pos = cursor.pos();
1975 Paragraph * endpar = cursor.par()->next();
1977 setCursorParUndo(bview->buffer());
1979 bool isEnvironment =
1980 textclasslist.Style(bview->buffer()->params.textclass,
1981 cursor.par()->getLayout()).isEnvironment();
1983 textclasslist.Style(bview->buffer()->params.textclass,
1984 cursor.par()->getLayout()).free_spacing;
1986 textclasslist.Style(bview->buffer()->params.textclass,
1987 cursor.par()->getLayout()).keepempty;
1989 // only to be sure, should not be neccessary
1990 clearSelection(bview);
1992 // insert the string, don't insert doublespace
1993 bool space_inserted = true;
1994 for(string::const_iterator cit = str.begin();
1995 cit != str.end(); ++cit) {
1997 if (par->size() || keepempty) {
1998 par->breakParagraph(bview->buffer()->params,
1999 pos, isEnvironment);
2002 space_inserted = true;
2006 // do not insert consecutive spaces if !free_spacing
2007 } else if ((*cit == ' ' || *cit == '\t')
2008 && space_inserted && !free_spacing) {
2010 } else if (*cit == '\t') {
2011 if (!free_spacing) {
2012 // tabs are like spaces here
2013 par->insertChar(pos, ' ',
2016 space_inserted = true;
2018 const Paragraph::value_type nb = 8 - pos % 8;
2019 for (Paragraph::size_type a = 0;
2021 par->insertChar(pos, ' ',
2025 space_inserted = true;
2027 } else if (!IsPrintable(*cit)) {
2028 // Ignore unprintables
2031 // just insert the character
2032 par->insertChar(pos, *cit, current_font);
2034 space_inserted = (*cit == ' ');
2039 redoParagraphs(bview, cursor, endpar);
2040 setCursor(bview, cursor.par(), cursor.pos());
2041 selection.cursor = cursor;
2042 setCursor(bview, par, pos);
2043 setSelection(bview);
2047 /* turns double-CR to single CR, others where converted into one
2048 blank. Then InsertStringAsLines is called */
2049 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
2051 string linestr(str);
2052 bool newline_inserted = false;
2053 for (string::size_type i = 0; i < linestr.length(); ++i) {
2054 if (linestr[i] == '\n') {
2055 if (newline_inserted) {
2056 // we know that \r will be ignored by
2057 // InsertStringA. Of course, it is a dirty
2058 // trick, but it works...
2059 linestr[i - 1] = '\r';
2063 newline_inserted = true;
2065 } else if (IsPrintable(linestr[i])) {
2066 newline_inserted = false;
2069 insertStringAsLines(bview, linestr);
2073 bool LyXText::gotoNextInset(BufferView * bview,
2074 std::vector<Inset::Code> const & codes,
2075 string const & contents) const
2077 LyXCursor res = cursor;
2080 if (res.pos() < res.par()->size() - 1) {
2081 res.pos(res.pos() + 1);
2083 res.par(res.par()->next());
2087 } while (res.par() &&
2088 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
2089 && (inset = res.par()->getInset(res.pos())) != 0
2090 && find(codes.begin(), codes.end(), inset->LyxCode())
2092 && (contents.empty() ||
2093 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
2097 setCursor(bview, res.par(), res.pos());
2104 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
2105 Paragraph::size_type pos)
2107 LyXCursor tmpcursor;
2110 Paragraph::size_type z;
2111 Row * row = getRow(par, pos, y);
2113 // is there a break one row above
2114 if (row->previous() && row->previous()->par() == row->par()) {
2115 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
2116 if (z >= row->pos()) {
2117 // set the dimensions of the row above
2118 y -= row->previous()->height();
2120 refresh_row = row->previous();
2121 status = LyXText::NEED_MORE_REFRESH;
2123 breakAgain(bview, row->previous());
2125 // set the cursor again. Otherwise
2126 // dangling pointers are possible
2127 setCursor(bview, cursor.par(), cursor.pos(),
2128 false, cursor.boundary());
2129 selection.cursor = cursor;
2134 int const tmpheight = row->height();
2135 Paragraph::size_type const tmplast = rowLast(row);
2139 breakAgain(bview, row);
2140 if (row->height() == tmpheight && rowLast(row) == tmplast)
2141 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2143 status = LyXText::NEED_MORE_REFRESH;
2145 // check the special right address boxes
2146 if (textclasslist.Style(bview->buffer()->params.textclass,
2147 par->getLayout()).margintype
2148 == MARGIN_RIGHT_ADDRESS_BOX) {
2155 redoDrawingOfParagraph(bview, tmpcursor);
2158 // set the cursor again. Otherwise dangling pointers are possible
2159 // also set the selection
2161 if (selection.set()) {
2163 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2164 false, selection.cursor.boundary());
2165 selection.cursor = cursor;
2166 setCursorIntern(bview, selection.start.par(),
2167 selection.start.pos(),
2168 false, selection.start.boundary());
2169 selection.start = cursor;
2170 setCursorIntern(bview, selection.end.par(),
2171 selection.end.pos(),
2172 false, selection.end.boundary());
2173 selection.end = cursor;
2174 setCursorIntern(bview, last_sel_cursor.par(),
2175 last_sel_cursor.pos(),
2176 false, last_sel_cursor.boundary());
2177 last_sel_cursor = cursor;
2180 setCursorIntern(bview, cursor.par(), cursor.pos(),
2181 false, cursor.boundary());
2185 // returns false if inset wasn't found
2186 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2188 // first check the current paragraph
2189 int pos = cursor.par()->getPositionOfInset(inset);
2191 checkParagraph(bview, cursor.par(), pos);
2195 // check every paragraph
2197 Paragraph * par = firstParagraph();
2199 pos = par->getPositionOfInset(inset);
2201 checkParagraph(bview, par, pos);
2211 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2212 Paragraph::size_type pos,
2213 bool setfont, bool boundary) const
2215 LyXCursor old_cursor = cursor;
2216 setCursorIntern(bview, par, pos, setfont, boundary);
2217 deleteEmptyParagraphMechanism(bview, old_cursor);
2221 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2222 Paragraph::size_type pos, bool boundary) const
2226 cur.boundary(boundary);
2228 /* get the cursor y position in text */
2230 Row * row = getRow(par, pos, y);
2231 /* y is now the beginning of the cursor row */
2232 y += row->baseline();
2233 /* y is now the cursor baseline */
2236 /* now get the cursors x position */
2238 float fill_separator;
2240 float fill_label_hfill;
2241 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2243 Paragraph::size_type cursor_vpos = 0;
2244 Paragraph::size_type last = rowLastPrintable(row);
2246 if (pos > last + 1) // This shouldn't happen.
2248 else if (pos < row->pos())
2251 if (last < row->pos())
2252 cursor_vpos = row->pos();
2253 else if (pos > last && !boundary)
2254 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2255 ? row->pos() : last + 1;
2256 else if (pos > row->pos() &&
2257 (pos > last || boundary))
2258 /// Place cursor after char at (logical) position pos - 1
2259 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2260 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2262 /// Place cursor before char at (logical) position pos
2263 cursor_vpos = (bidi_level(pos) % 2 == 0)
2264 ? log2vis(pos) : log2vis(pos) + 1;
2266 Paragraph::size_type main_body =
2267 beginningOfMainBody(bview->buffer(), row->par());
2268 if ((main_body > 0) &&
2269 ((main_body-1 > last) ||
2270 !row->par()->isLineSeparator(main_body-1)))
2273 for (Paragraph::size_type vpos = row->pos();
2274 vpos < cursor_vpos; ++vpos) {
2275 pos = vis2log(vpos);
2276 if (main_body > 0 && pos == main_body - 1) {
2277 x += fill_label_hfill +
2278 lyxfont::width(textclasslist.Style(
2279 bview->buffer()->params.textclass,
2280 row->par()->getLayout())
2282 getFont(bview->buffer(), row->par(), -2));
2283 if (row->par()->isLineSeparator(main_body-1))
2284 x -= singleWidth(bview, row->par(),main_body-1);
2286 if (hfillExpansion(bview->buffer(), row, pos)) {
2287 x += singleWidth(bview, row->par(), pos);
2288 if (pos >= main_body)
2291 x += fill_label_hfill;
2292 } else if (row->par()->isSeparator(pos)) {
2293 x += singleWidth(bview, row->par(), pos);
2294 if (pos >= main_body)
2295 x += fill_separator;
2297 x += singleWidth(bview, row->par(), pos);
2306 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2307 Paragraph::size_type pos,
2308 bool setfont, bool boundary) const
2310 setCursor(bview, cursor, par, pos, boundary);
2312 setCurrentFont(bview);
2316 void LyXText::setCurrentFont(BufferView * bview) const
2318 Paragraph::size_type pos = cursor.pos();
2319 if (cursor.boundary() && pos > 0)
2323 if (pos == cursor.par()->size())
2325 else // potentional bug... BUG (Lgb)
2326 if (cursor.par()->isSeparator(pos)) {
2327 if (pos > cursor.row()->pos() &&
2328 bidi_level(pos) % 2 ==
2329 bidi_level(pos - 1) % 2)
2331 else if (pos + 1 < cursor.par()->size())
2337 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2338 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2340 if (cursor.pos() == cursor.par()->size() &&
2341 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2342 !cursor.boundary()) {
2343 Language const * lang =
2344 cursor.par()->getParLanguage(bview->buffer()->params);
2345 current_font.setLanguage(lang);
2346 current_font.setNumber(LyXFont::OFF);
2347 real_current_font.setLanguage(lang);
2348 real_current_font.setNumber(LyXFont::OFF);
2353 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2355 LyXCursor old_cursor = cursor;
2357 /* get the row first */
2359 Row * row = getRowNearY(y);
2360 cursor.par(row->par());
2363 int column = getColumnNearX(bview, row, x, bound);
2364 cursor.pos(row->pos() + column);
2366 cursor.y(y + row->baseline());
2368 cursor.boundary(bound);
2369 setCurrentFont(bview);
2370 deleteEmptyParagraphMechanism(bview, old_cursor);
2374 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2377 /* get the row first */
2379 Row * row = getRowNearY(y);
2381 int column = getColumnNearX(bview, row, x, bound);
2383 cur.par(row->par());
2384 cur.pos(row->pos() + column);
2386 cur.y(y + row->baseline());
2388 cur.boundary(bound);
2392 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2394 if (cursor.pos() > 0) {
2395 bool boundary = cursor.boundary();
2396 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2397 if (!internal && !boundary &&
2398 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2399 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2400 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2401 Paragraph * par = cursor.par()->previous();
2402 setCursor(bview, par, par->size());
2407 void LyXText::cursorRight(BufferView * bview, bool internal) const
2409 if (!internal && cursor.boundary() &&
2410 !cursor.par()->isNewline(cursor.pos()))
2411 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2412 else if (cursor.pos() < cursor.par()->size()) {
2413 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2415 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2416 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2417 } else if (cursor.par()->next())
2418 setCursor(bview, cursor.par()->next(), 0);
2422 void LyXText::cursorUp(BufferView * bview) const
2424 setCursorFromCoordinates(bview, cursor.x_fix(),
2425 cursor.y() - cursor.row()->baseline() - 1);
2429 void LyXText::cursorDown(BufferView * bview) const
2431 setCursorFromCoordinates(bview, cursor.x_fix(),
2432 cursor.y() - cursor.row()->baseline()
2433 + cursor.row()->height() + 1);
2437 void LyXText::cursorUpParagraph(BufferView * bview) const
2439 if (cursor.pos() > 0) {
2440 setCursor(bview, cursor.par(), 0);
2442 else if (cursor.par()->previous()) {
2443 setCursor(bview, cursor.par()->previous(), 0);
2448 void LyXText::cursorDownParagraph(BufferView * bview) const
2450 if (cursor.par()->next()) {
2451 setCursor(bview, cursor.par()->next(), 0);
2453 setCursor(bview, cursor.par(), cursor.par()->size());
2458 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2459 LyXCursor const & old_cursor) const
2461 // Would be wrong to delete anything if we have a selection.
2462 if (selection.set()) return;
2464 // We allow all kinds of "mumbo-jumbo" when freespacing.
2465 if (textclasslist.Style(bview->buffer()->params.textclass,
2466 old_cursor.par()->getLayout()).free_spacing)
2469 bool deleted = false;
2471 /* Ok I'll put some comments here about what is missing.
2472 I have fixed BackSpace (and thus Delete) to not delete
2473 double-spaces automagically. I have also changed Cut,
2474 Copy and Paste to hopefully do some sensible things.
2475 There are still some small problems that can lead to
2476 double spaces stored in the document file or space at
2477 the beginning of paragraphs. This happens if you have
2478 the cursor betwenn to spaces and then save. Or if you
2479 cut and paste and the selection have a space at the
2480 beginning and then save right after the paste. I am
2481 sure none of these are very hard to fix, but I will
2482 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2483 that I can get some feedback. (Lgb)
2486 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2487 // delete the LineSeparator.
2490 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2491 // delete the LineSeparator.
2494 // If the pos around the old_cursor were spaces, delete one of them.
2495 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2496 // Only if the cursor has really moved
2498 if (old_cursor.pos() > 0
2499 && old_cursor.pos() < old_cursor.par()->size()
2500 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2501 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2502 old_cursor.par()->erase(old_cursor.pos() - 1);
2503 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2505 if (old_cursor.par() == cursor.par() &&
2506 cursor.pos() > old_cursor.pos()) {
2507 setCursorIntern(bview, cursor.par(),
2510 setCursorIntern(bview, cursor.par(),
2516 // Do not delete empty paragraphs with keepempty set.
2517 if ((textclasslist.Style(bview->buffer()->params.textclass,
2518 old_cursor.par()->getLayout())).keepempty)
2521 LyXCursor tmpcursor;
2523 if (old_cursor.par() != cursor.par()) {
2524 if ((old_cursor.par()->size() == 0
2525 || (old_cursor.par()->size() == 1
2526 && old_cursor.par()->isLineSeparator(0)))) {
2527 // ok, we will delete anything
2529 // make sure that you do not delete any environments
2530 status = LyXText::NEED_MORE_REFRESH;
2533 if (old_cursor.row()->previous()) {
2534 refresh_row = old_cursor.row()->previous();
2535 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2537 cursor = old_cursor; // that undo can restore the right cursor position
2538 Paragraph * endpar = old_cursor.par()->next();
2539 if (endpar && endpar->getDepth()) {
2540 while (endpar && endpar->getDepth()) {
2541 endpar = endpar->next();
2544 setUndo(bview->buffer(), Undo::DELETE,
2545 old_cursor.par()->previous(),
2550 removeRow(old_cursor.row());
2551 if (ownerParagraph() == old_cursor.par()) {
2552 ownerParagraph(ownerParagraph()->next());
2555 delete old_cursor.par();
2557 /* Breakagain the next par. Needed
2558 * because of the parindent that
2559 * can occur or dissappear. The
2560 * next row can change its height,
2561 * if there is another layout before */
2562 if (refresh_row->next()) {
2563 breakAgain(bview, refresh_row->next());
2564 updateCounters(bview, refresh_row);
2566 setHeightOfRow(bview, refresh_row);
2568 refresh_row = old_cursor.row()->next();
2569 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2572 cursor = old_cursor; // that undo can restore the right cursor position
2573 Paragraph * endpar = old_cursor.par()->next();
2574 if (endpar && endpar->getDepth()) {
2575 while (endpar && endpar->getDepth()) {
2576 endpar = endpar->next();
2579 setUndo(bview->buffer(), Undo::DELETE,
2580 old_cursor.par()->previous(),
2585 removeRow(old_cursor.row());
2587 if (ownerParagraph() == old_cursor.par()) {
2588 ownerParagraph(ownerParagraph()->next());
2591 delete old_cursor.par();
2593 /* Breakagain the next par. Needed
2594 because of the parindent that can
2595 occur or dissappear.
2596 The next row can change its height,
2597 if there is another layout before
2600 breakAgain(bview, refresh_row);
2601 updateCounters(bview, refresh_row->previous());
2607 setCursorIntern(bview, cursor.par(), cursor.pos());
2609 if (selection.cursor.par() == old_cursor.par()
2610 && selection.cursor.pos() == selection.cursor.pos()) {
2611 // correct selection
2612 selection.cursor = cursor;
2616 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2617 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2619 setCursorIntern(bview, cursor.par(), cursor.pos());
2620 selection.cursor = cursor;
2627 Paragraph * LyXText::getParFromID(int id)
2629 Paragraph * result = firstParagraph();
2630 while (result && result->id() != id)
2631 result = result->next();
2637 bool LyXText::textUndo(BufferView * bview)
2641 // returns false if no undo possible
2642 Undo * undo = bview->buffer()->undostack.pop();
2646 bview->buffer()->redostack
2647 .push(createUndo(bview->buffer(), undo->kind,
2648 getParFromID(undo->number_of_before_par),
2649 getParFromID(undo->number_of_behind_par)));
2651 return textHandleUndo(bview, undo);
2655 bool LyXText::textRedo(BufferView * bview)
2659 // returns false if no redo possible
2660 Undo * undo = bview->buffer()->redostack.pop();
2664 bview->buffer()->undostack
2665 .push(createUndo(bview->buffer(), undo->kind,
2666 getParFromID(undo->number_of_before_par),
2667 getParFromID(undo->number_of_behind_par)));
2669 return textHandleUndo(bview, undo);
2673 bool LyXText::textHandleUndo(BufferView * bview, Undo * undo)
2677 // returns false if no undo possible
2678 bool result = false;
2680 Paragraph * before =
2681 getParFromID(undo->number_of_before_par);
2682 Paragraph * behind =
2683 getParFromID(undo->number_of_behind_par);
2685 Paragraph * tmppar2;
2687 Paragraph * tmppar5;
2689 // if there's no before take the beginning
2690 // of the document for redoing
2692 setCursorIntern(bview, firstParagraph(), 0);
2694 // replace the paragraphs with the undo informations
2696 Paragraph * tmppar3 = undo->par;
2697 undo->par = 0; // otherwise the undo destructor would delete the paragraph
2698 Paragraph * tmppar4 = tmppar3;
2701 while (tmppar4->next())
2702 tmppar4 = tmppar4->next();
2703 } // get last undo par
2705 // now remove the old text if there is any
2706 if (before != behind || (!behind && !before)) {
2708 tmppar5 = before->next();
2710 tmppar5 = ownerParagraph();
2712 while (tmppar5 && tmppar5 != behind) {
2714 tmppar5 = tmppar5->next();
2715 // a memory optimization for edit: Only layout information
2716 // is stored in the undo. So restore the text informations.
2717 if (undo->kind == Undo::EDIT) {
2718 tmppar2->setContentsFromPar(tmppar);
2719 tmppar->clearContents();
2720 tmppar2 = tmppar2->next();
2725 // put the new stuff in the list if there is one
2728 before->next(tmppar3);
2730 ownerParagraph(tmppar3);
2731 tmppar3->previous(before);
2734 ownerParagraph(behind);
2737 tmppar4->next(behind);
2739 behind->previous(tmppar4);
2743 // Set the cursor for redoing
2745 setCursorIntern(bview, before, 0);
2748 // calculate the endpar for redoing the paragraphs.
2750 endpar = behind->next();
2754 tmppar = getParFromID(undo->number_of_cursor_par);
2755 redoParagraphs(bview, cursor, endpar);
2757 setCursorIntern(bview, tmppar, undo->cursor_pos);
2758 updateCounters(bview, cursor.row());
2768 void LyXText::finishUndo()
2772 // makes sure the next operation will be stored
2773 undo_finished = true;
2777 void LyXText::freezeUndo()
2781 // this is dangerous and for internal use only
2786 void LyXText::unFreezeUndo()
2790 // this is dangerous and for internal use only
2791 undo_frozen = false;
2795 void LyXText::setUndo(Buffer * buf, Undo::undo_kind kind,
2796 Paragraph const * before,
2797 Paragraph const * behind) const
2802 buf->undostack.push(createUndo(buf, kind, before, behind));
2803 buf->redostack.clear();
2807 void LyXText::setRedo(Buffer * buf, Undo::undo_kind kind,
2808 Paragraph const * before, Paragraph const * behind)
2812 buf->redostack.push(createUndo(buf, kind, before, behind));
2816 Undo * LyXText::createUndo(Buffer * buf, Undo::undo_kind kind,
2817 Paragraph const * before,
2818 Paragraph const * behind) const
2823 int before_number = -1;
2824 int behind_number = -1;
2826 before_number = before->id();
2828 behind_number = behind->id();
2829 // Undo::EDIT and Undo::FINISH are
2830 // always finished. (no overlapping there)
2831 // overlapping only with insert and delete inside one paragraph:
2832 // Nobody wants all removed character
2833 // appear one by one when undoing.
2834 // EDIT is special since only layout information, not the
2835 // contents of a paragaph are stored.
2836 if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)){
2837 // check wether storing is needed
2838 if (!buf->undostack.empty() &&
2839 buf->undostack.top()->kind == kind &&
2840 buf->undostack.top()->number_of_before_par == before_number &&
2841 buf->undostack.top()->number_of_behind_par == behind_number ){
2846 // create a new Undo
2847 Paragraph * undopar;
2849 Paragraph * start = 0;
2850 Paragraph * end = 0;
2853 start = const_cast<Paragraph*>(before->next());
2855 start = firstParagraph();
2857 end = const_cast<Paragraph*>(behind->previous());
2859 end = firstParagraph();
2863 if (start && end && (start != end->next()) &&
2864 ((before != behind) || (!before && !behind))) {
2865 Paragraph * tmppar = start;
2866 Paragraph * tmppar2 = new Paragraph(*tmppar);
2867 tmppar2->id(tmppar->id());
2869 // a memory optimization: Just store the layout information
2871 if (kind == Undo::EDIT){
2872 //tmppar2->text.clear();
2873 tmppar2->clearContents();
2878 while (tmppar != end && tmppar->next()) {
2879 tmppar = tmppar->next();
2880 tmppar2->next(new Paragraph(*tmppar));
2881 tmppar2->next()->id(tmppar->id());
2882 // a memory optimization: Just store the layout
2883 // information when only edit
2884 if (kind == Undo::EDIT){
2885 //tmppar2->next->text.clear();
2886 tmppar2->clearContents();
2888 tmppar2->next()->previous(tmppar2);
2889 tmppar2 = tmppar2->next();
2893 undopar = 0; // nothing to replace (undo of delete maybe)
2895 int cursor_par = cursor.par()->id();
2896 int cursor_pos = cursor.pos();
2898 Undo * undo = new Undo(kind,
2899 before_number, behind_number,
2900 cursor_par, cursor_pos,
2903 undo_finished = false;
2908 void LyXText::setCursorParUndo(Buffer * buf)
2912 setUndo(buf, Undo::FINISH,
2913 cursor.par()->previous(),
2914 cursor.par()->next());
2918 void LyXText::toggleAppendix(BufferView * bview)
2920 Paragraph * par = cursor.par();
2921 bool start = !par->params().startOfAppendix();
2923 // ensure that we have only one start_of_appendix in this document
2924 Paragraph * tmp = firstParagraph();
2925 for (; tmp; tmp = tmp->next())
2926 tmp->params().startOfAppendix(false);
2928 par->params().startOfAppendix(start);
2930 // we can set the refreshing parameters now
2931 status = LyXText::NEED_MORE_REFRESH;
2933 refresh_row = 0; // not needed for full update
2934 updateCounters(bview, 0);
2935 setCursor(bview, cursor.par(), cursor.pos());
2939 Paragraph * LyXText::ownerParagraph() const
2942 return inset_owner->par;
2944 return bv_owner->buffer()->paragraph;
2948 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2951 inset_owner->par = p;
2953 bv_owner->buffer()->paragraph = p;