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 "lyxparagraph.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"
53 LyXText::LyXText(BufferView * bv)
61 LyXText::LyXText(InsetText * inset)
71 the_locking_inset = 0;
79 status = LyXText::UNCHANGED;
81 // set cursor at the very top position
83 selection = true; /* these setting is necessary
84 because of the delete-empty-
85 paragraph mechanism in
88 selection.set(true); /* these setting is necessary
89 because of the delete-empty-
90 paragraph mechanism in
94 LyXParagraph * par = OwnerParagraph();
95 current_font = GetFont(bv_owner->buffer(), par, 0);
97 InsertParagraph(bv_owner, par, lastrow);
100 SetCursor(bv_owner, firstrow->par(), 0);
102 current_font = LyXFont(LyXFont::ALL_SANE);
109 selection.cursor = cursor;
110 selection.set(false);
111 selection.mark(false);
114 // no rebreak necessary
117 undo_finished = true;
120 // Default layouttype for copy environment type
124 // Dump all rowinformation:
125 Row * tmprow = firstrow;
126 lyxerr << "Baseline Paragraph Pos Height Ascent Fill\n";
128 lyxerr << tmprow->baseline() << '\t'
129 << tmprow->par << '\t'
130 << tmprow->pos() << '\t'
131 << tmprow->height << '\t'
132 << tmprow->ascent_of_text << '\t'
133 << tmprow->fill << '\n';
134 tmprow = tmprow->next();
141 void LyXText::init(BufferView * bview)
146 LyXParagraph * par = OwnerParagraph();
147 current_font = GetFont(bview->buffer(), par, 0);
149 InsertParagraph(bview, par, lastrow);
152 SetCursorIntern(bview, firstrow->par(), 0);
156 selection.cursor = cursor;
159 printf("TP = %x\n",inset_owner->owner());
160 // Dump all rowinformation:
161 Row * tmprow = firstrow;
162 lyxerr << "Width = " << width << endl;
163 lyxerr << "Baseline Paragraph Pos Height Ascent Fill\n";
165 lyxerr << tmprow->baseline() << '\t'
166 << tmprow->par() << '\t'
167 << tmprow->pos() << '\t'
168 << tmprow->height() << '\t'
169 << tmprow->ascent_of_text() << '\t'
170 << tmprow->fill() << '\n';
171 tmprow = tmprow->next();
179 // Delete all rows, this does not touch the paragraphs!
180 Row * tmprow = firstrow;
182 tmprow = firstrow->next();
189 // Gets the fully instantiated font at a given position in a paragraph
190 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
191 // The difference is that this one is used for displaying, and thus we
192 // are allowed to make cosmetic improvements. For instance make footnotes
194 // If position is -1, we get the layout font of the paragraph.
195 // If position is -2, we get the font of the manual label of the paragraph.
196 LyXFont const LyXText::GetFont(Buffer const * buf, LyXParagraph * par,
197 LyXParagraph::size_type pos) const
199 LyXLayout const & layout =
200 textclasslist.Style(buf->params.textclass, par->GetLayout());
202 char par_depth = par->GetDepth();
203 // We specialize the 95% common case:
207 if (layout.labeltype == LABEL_MANUAL
208 && pos < BeginningOfMainBody(buf, par)) {
210 LyXFont f = par->GetFontSettings(buf->params,
212 return f.realize(layout.reslabelfont);
214 LyXFont f = par->GetFontSettings(buf->params, pos);
215 return f.realize(layout.resfont);
220 // process layoutfont for pos == -1 and labelfont for pos < -1
222 return layout.resfont;
224 return layout.reslabelfont;
228 // The uncommon case need not be optimized as much
230 LyXFont layoutfont, tmpfont;
234 if (pos < BeginningOfMainBody(buf, par)) {
236 layoutfont = layout.labelfont;
239 layoutfont = layout.font;
241 tmpfont = par->GetFontSettings(buf->params, pos);
242 tmpfont.realize(layoutfont);
245 // process layoutfont for pos == -1 and labelfont for pos < -1
247 tmpfont = layout.font;
249 tmpfont = layout.labelfont;
252 // Resolve against environment font information
253 while (par && par_depth && !tmpfont.resolved()) {
254 par = par->DepthHook(par_depth - 1);
256 tmpfont.realize(textclasslist.
257 Style(buf->params.textclass,
258 par->GetLayout()).font);
259 par_depth = par->GetDepth();
263 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
269 void LyXText::SetCharFont(BufferView * bv, LyXParagraph * par,
270 LyXParagraph::size_type pos, LyXFont const & fnt,
273 Buffer const * buf = bv->buffer();
274 LyXFont font = GetFont(buf, par, pos);
275 font.update(fnt, buf->params.language, toggleall);
276 // Let the insets convert their font
277 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
278 Inset * inset = par->GetInset(pos);
280 if (inset->Editable()==Inset::HIGHLY_EDITABLE) {
281 UpdatableInset * uinset = static_cast<UpdatableInset *>(inset);
282 uinset->SetFont(bv, fnt, toggleall, true);
284 font = inset->ConvertFont(font);
288 LyXLayout const & layout =
289 textclasslist.Style(buf->params.textclass,
292 // Get concrete layout font to reduce against
295 if (pos < BeginningOfMainBody(buf, par))
296 layoutfont = layout.labelfont;
298 layoutfont = layout.font;
300 // Realize against environment font information
301 if (par->GetDepth()){
302 LyXParagraph * tp = par;
303 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
304 tp = tp->DepthHook(tp->GetDepth()-1);
306 layoutfont.realize(textclasslist.
307 Style(buf->params.textclass,
308 tp->GetLayout()).font);
312 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
314 // Now, reduce font against full layout font
315 font.reduce(layoutfont);
317 par->SetFont(pos, font);
320 void LyXText::SetCharFont(Buffer const * buf, LyXParagraph * par,
321 LyXParagraph::size_type pos, LyXFont const & fnt)
324 // Let the insets convert their font
325 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
326 font = par->GetInset(pos)->ConvertFont(font);
329 LyXLayout const & layout =
330 textclasslist.Style(buf->params.textclass,
333 // Get concrete layout font to reduce against
336 if (pos < BeginningOfMainBody(buf, par))
337 layoutfont = layout.labelfont;
339 layoutfont = layout.font;
341 // Realize against environment font information
342 if (par->GetDepth()){
343 LyXParagraph * tp = par;
344 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
345 tp = tp->DepthHook(tp->GetDepth()-1);
347 layoutfont.realize(textclasslist.
348 Style(buf->params.textclass,
349 tp->GetLayout()).font);
353 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
355 // Now, reduce font against full layout font
356 font.reduce(layoutfont);
358 par->SetFont(pos, font);
362 /* inserts a new row behind the specified row, increments
363 * the touched counters */
364 void LyXText::InsertRow(Row * row, LyXParagraph * par,
365 LyXParagraph::size_type pos) const
367 Row * tmprow = new Row;
370 tmprow->next(firstrow);
373 tmprow->previous(row);
374 tmprow->next(row->next());
379 tmprow->next()->previous(tmprow);
381 if (tmprow->previous())
382 tmprow->previous()->next(tmprow);
390 ++number_of_rows; // one more row
394 // removes the row and reset the touched counters
395 void LyXText::RemoveRow(Row * row) const
397 /* this must not happen before the currentrow for clear reasons.
398 so the trick is just to set the current row onto the previous
401 GetRow(row->par(), row->pos(), unused_y);
404 row->next()->previous(row->previous());
405 if (!row->previous()) {
406 firstrow = row->next();
408 row->previous()->next(row->next());
411 lastrow = row->previous();
413 height -= row->height(); // the text becomes smaller
416 --number_of_rows; // one row less
420 // remove all following rows of the paragraph of the specified row.
421 void LyXText::RemoveParagraph(Row * row) const
423 LyXParagraph * tmppar = row->par();
427 while (row && row->par() == tmppar) {
428 tmprow = row->next();
435 // insert the specified paragraph behind the specified row
436 void LyXText::InsertParagraph(BufferView * bview, LyXParagraph * par,
439 InsertRow(row, par, 0); /* insert a new row, starting
442 SetCounter(bview->buffer(), par); // set the counters
444 // and now append the whole paragraph behind the new row
447 AppendParagraph(bview, firstrow);
449 row->next()->height(0);
450 AppendParagraph(bview, row->next());
455 /* used in setlayout */
456 // Asger is not sure we want to do this...
457 void LyXText::MakeFontEntriesLayoutSpecific(Buffer const * buf,
461 LyXLayout const & layout =
462 textclasslist.Style(buf->params.textclass, par->GetLayout());
464 LyXFont layoutfont, tmpfont;
465 for (LyXParagraph::size_type pos = 0;
466 pos < par->size(); ++pos) {
467 if (pos < BeginningOfMainBody(buf, par))
468 layoutfont = layout.labelfont;
470 layoutfont = layout.font;
472 tmpfont = par->GetFontSettings(buf->params, pos);
473 tmpfont.reduce(layoutfont);
474 par->SetFont(pos, tmpfont);
479 LyXParagraph * LyXText::SetLayout(BufferView * bview,
480 LyXCursor & cur, LyXCursor & sstart_cur,
481 LyXCursor & send_cur,
482 LyXTextClass::size_type layout)
484 LyXParagraph * endpar = send_cur.par()->next();
485 LyXParagraph * undoendpar = endpar;
487 if (endpar && endpar->GetDepth()) {
488 while (endpar && endpar->GetDepth()) {
489 endpar = endpar->next();
493 endpar = endpar->next(); // because of parindents etc.
496 SetUndo(bview->buffer(), Undo::EDIT,
497 sstart_cur.par()->previous(),
500 /* ok we have a selection. This is always between sstart_cur
501 * and sel_end cursor */
504 LyXLayout const & lyxlayout =
505 textclasslist.Style(bview->buffer()->params.textclass, layout);
507 while (cur.par() != send_cur.par()) {
508 cur.par()->SetLayout(layout);
509 MakeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
510 LyXParagraph * fppar = cur.par();
511 fppar->params.spaceTop(lyxlayout.fill_top ?
512 VSpace(VSpace::VFILL)
513 : VSpace(VSpace::NONE));
514 fppar->params.spaceBottom(lyxlayout.fill_bottom ?
515 VSpace(VSpace::VFILL)
516 : VSpace(VSpace::NONE));
517 if (lyxlayout.margintype == MARGIN_MANUAL)
518 cur.par()->SetLabelWidthString(lyxlayout.labelstring());
519 if (lyxlayout.labeltype != LABEL_BIBLIO
521 delete fppar->bibkey;
524 cur.par(cur.par()->next());
526 cur.par()->SetLayout(layout);
527 MakeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
528 LyXParagraph * fppar = cur.par();
529 fppar->params.spaceTop(lyxlayout.fill_top ?
530 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
531 fppar->params.spaceBottom(lyxlayout.fill_bottom ?
532 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
533 if (lyxlayout.margintype == MARGIN_MANUAL)
534 cur.par()->SetLabelWidthString(lyxlayout.labelstring());
535 if (lyxlayout.labeltype != LABEL_BIBLIO
537 delete fppar->bibkey;
544 // set layout over selection and make a total rebreak of those paragraphs
545 void LyXText::SetLayout(BufferView * bview, LyXTextClass::size_type layout)
548 LyXCursor tmpcursor = cursor; /* store the current cursor */
550 // if there is no selection just set the layout
551 // of the current paragraph */
553 sel_start_cursor = cursor; // dummy selection
554 sel_end_cursor = cursor;
557 endpar = SetLayout(bview, cursor, sel_start_cursor,
558 sel_end_cursor, layout);
559 RedoParagraphs(bview, sel_start_cursor, endpar);
561 // we have to reset the selection, because the
562 // geometry could have changed
563 SetCursor(bview, sel_start_cursor.par(),
564 sel_start_cursor.pos(), false);
566 SetCursor(bview, sel_end_cursor.par(), sel_end_cursor.pos(),
568 UpdateCounters(bview, cursor.row());
569 ClearSelection(bview);
571 SetCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
573 LyXCursor tmpcursor = cursor; /* store the current cursor */
575 // if there is no selection just set the layout
576 // of the current paragraph */
577 if (!selection.set()) {
578 selection.start = cursor; // dummy selection
579 selection.end = cursor;
581 LyXParagraph * endpar = SetLayout(bview, cursor, selection.start,
582 selection.end, layout);
583 RedoParagraphs(bview, selection.start, endpar);
585 // we have to reset the selection, because the
586 // geometry could have changed
587 SetCursor(bview, selection.start.par(),
588 selection.start.pos(), false);
589 selection.cursor = cursor;
590 SetCursor(bview, selection.end.par(), selection.end.pos(),
592 UpdateCounters(bview, cursor.row());
593 ClearSelection(bview);
595 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
607 sel_start_cursor = cursor; // dummy selection
608 sel_end_cursor = cursor;
611 // We end at the next paragraph with depth 0
612 LyXParagraph * endpar = sel_end_cursor.par()->next();
614 LyXParagraph * undoendpar = endpar;
616 if (endpar && endpar->GetDepth()) {
617 while (endpar && endpar->GetDepth()) {
618 endpar = endpar->next();
623 endpar = endpar->next(); // because of parindents etc.
626 SetUndo(bview->buffer(), Undo::EDIT,
627 sel_start_cursor.par()->previous(),
630 LyXCursor tmpcursor = cursor; // store the current cursor
632 // ok we have a selection. This is always between sel_start_cursor
633 // and sel_end cursor
634 cursor = sel_start_cursor;
636 bool anything_changed = false;
639 // NOTE: you can't change the depth of a bibliography entry
641 textclasslist.Style(bview->buffer()->params.textclass,
642 cursor.par()->GetLayout()
643 ).labeltype != LABEL_BIBLIO) {
644 LyXParagraph * prev = cursor.par()->previous();
647 && (prev->GetDepth() - cursor.par()->GetDepth() > 0
648 || (prev->GetDepth() == cursor.par()->GetDepth()
649 && textclasslist.Style(bview->buffer()->params.textclass,
650 prev->GetLayout()).isEnvironment()))) {
651 cursor.par()->params.depth(cursor.par()->params.depth() + 1);
652 anything_changed = true;
655 if (cursor.par() == sel_end_cursor.par())
657 cursor.par(cursor.par()->next());
660 // if nothing changed set all depth to 0
661 if (!anything_changed) {
662 cursor = sel_start_cursor;
663 while (cursor.par() != sel_end_cursor.par()) {
664 cursor.par()->params.depth(0);
665 cursor.par(cursor.par()->next());
667 cursor.par()->params.depth(0);
670 RedoParagraphs(bview, sel_start_cursor, endpar);
672 // we have to reset the selection, because the
673 // geometry could have changed
674 SetCursor(bview, sel_start_cursor.par(),
675 sel_start_cursor.pos());
677 SetCursor(bview, sel_end_cursor.par(), sel_end_cursor.pos());
678 UpdateCounters(bview, cursor.row());
679 ClearSelection(bview);
681 SetCursor(bview, tmpcursor.par(), tmpcursor.pos());
684 // increment depth over selection and
685 // make a total rebreak of those paragraphs
686 void LyXText::IncDepth(BufferView * bview)
688 // If there is no selection, just use the current paragraph
689 if (!selection.set()) {
690 selection.start = cursor; // dummy selection
691 selection.end = cursor;
694 // We end at the next paragraph with depth 0
695 LyXParagraph * endpar = selection.end.par()->next();
697 LyXParagraph * undoendpar = endpar;
699 if (endpar && endpar->GetDepth()) {
700 while (endpar && endpar->GetDepth()) {
701 endpar = endpar->next();
706 endpar = endpar->next(); // because of parindents etc.
709 SetUndo(bview->buffer(), Undo::EDIT,
710 selection.start.par()->previous(),
713 LyXCursor tmpcursor = cursor; // store the current cursor
715 // ok we have a selection. This is always between sel_start_cursor
716 // and sel_end cursor
717 cursor = selection.start;
719 bool anything_changed = false;
722 // NOTE: you can't change the depth of a bibliography entry
724 textclasslist.Style(bview->buffer()->params.textclass,
725 cursor.par()->GetLayout()
726 ).labeltype != LABEL_BIBLIO) {
727 LyXParagraph * prev = cursor.par()->previous();
730 && (prev->GetDepth() - cursor.par()->GetDepth() > 0
731 || (prev->GetDepth() == cursor.par()->GetDepth()
732 && textclasslist.Style(bview->buffer()->params.textclass,
733 prev->GetLayout()).isEnvironment()))) {
734 cursor.par()->params.depth(cursor.par()->params.depth() + 1);
735 anything_changed = true;
738 if (cursor.par() == selection.end.par())
740 cursor.par(cursor.par()->next());
743 // if nothing changed set all depth to 0
744 if (!anything_changed) {
745 cursor = selection.start;
746 while (cursor.par() != selection.end.par()) {
747 cursor.par()->params.depth(0);
748 cursor.par(cursor.par()->next());
750 cursor.par()->params.depth(0);
753 RedoParagraphs(bview, selection.start, endpar);
755 // we have to reset the selection, because the
756 // geometry could have changed
757 SetCursor(bview, selection.start.par(), selection.start.pos());
758 selection.cursor = cursor;
759 SetCursor(bview, selection.end.par(), selection.end.pos());
760 UpdateCounters(bview, cursor.row());
761 ClearSelection(bview);
763 SetCursor(bview, tmpcursor.par(), tmpcursor.pos());
768 // decrement depth over selection and
769 // make a total rebreak of those paragraphs
770 void LyXText::DecDepth(BufferView * bview)
772 // if there is no selection just set the layout
773 // of the current paragraph
774 if (!selection.set()) {
775 selection.start = cursor; // dummy selection
776 selection.end = cursor;
778 LyXParagraph * endpar = selection.end.par()->next();
779 LyXParagraph * undoendpar = endpar;
781 if (endpar && endpar->GetDepth()) {
782 while (endpar && endpar->GetDepth()) {
783 endpar = endpar->next();
787 endpar = endpar->next(); // because of parindents etc.
790 SetUndo(bview->buffer(), Undo::EDIT,
791 selection.start.par()->previous(),
794 LyXCursor tmpcursor = cursor; // store the current cursor
796 // ok we have a selection. This is always between sel_start_cursor
797 // and sel_end cursor
798 cursor = selection.start;
801 if (cursor.par()->params.depth())
802 cursor.par()->params.depth(cursor.par()->params.depth() - 1);
803 if (cursor.par() == selection.end.par())
805 cursor.par(cursor.par()->next());
808 RedoParagraphs(bview, selection.start, endpar);
810 // we have to reset the selection, because the
811 // geometry could have changed
812 SetCursor(bview, selection.start.par(),
813 selection.start.pos());
814 selection.cursor = cursor;
815 SetCursor(bview, selection.end.par(), selection.end.pos());
816 UpdateCounters(bview, cursor.row());
817 ClearSelection(bview);
819 SetCursor(bview, tmpcursor.par(), tmpcursor.pos());
823 // set font over selection and make a total rebreak of those paragraphs
824 void LyXText::SetFont(BufferView * bview, LyXFont const & font, bool toggleall)
826 // if there is no selection just set the current_font
827 if (!selection.set()) {
828 // Determine basis font
830 if (cursor.pos() < BeginningOfMainBody(bview->buffer(),
832 layoutfont = GetFont(bview->buffer(), cursor.par(),-2);
834 layoutfont = GetFont(bview->buffer(), cursor.par(),-1);
835 // Update current font
836 real_current_font.update(font,
837 bview->buffer()->params.language,
840 // Reduce to implicit settings
841 current_font = real_current_font;
842 current_font.reduce(layoutfont);
843 // And resolve it completely
844 real_current_font.realize(layoutfont);
848 LyXCursor tmpcursor = cursor; // store the current cursor
850 // ok we have a selection. This is always between sel_start_cursor
851 // and sel_end cursor
853 SetUndo(bview->buffer(), Undo::EDIT,
854 selection.start.par()->previous(),
855 selection.end.par()->next());
857 cursor = selection.start;
858 while (cursor.par() != selection.end.par() ||
859 (cursor.pos() < selection.end.pos())) {
860 if (cursor.pos() < cursor.par()->size()) {
861 // an open footnote should behave
863 SetCharFont(bview, cursor.par(), cursor.pos(), font, toggleall);
864 cursor.pos(cursor.pos() + 1);
867 cursor.par(cursor.par()->next());
872 RedoParagraphs(bview, selection.start, selection.end.par()->next());
874 // we have to reset the selection, because the
875 // geometry could have changed
876 SetCursor(bview, selection.start.par(), selection.start.pos());
877 selection.cursor = cursor;
878 SetCursor(bview, selection.end.par(), selection.end.pos());
879 ClearSelection(bview);
881 SetCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
882 tmpcursor.boundary());
886 void LyXText::RedoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
888 Row * tmprow = cur.row();
889 int y = cur.y() - tmprow->baseline();
891 SetHeightOfRow(bview, tmprow);
892 LyXParagraph * first_phys_par = tmprow->par();
894 // find the first row of the paragraph
895 if (first_phys_par != tmprow->par())
896 while (tmprow->previous()
897 && tmprow->previous()->par() != first_phys_par) {
898 tmprow = tmprow->previous();
899 y -= tmprow->height();
900 SetHeightOfRow(bview, tmprow);
902 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
903 tmprow = tmprow->previous();
904 y -= tmprow->height();
905 SetHeightOfRow(bview, tmprow);
908 // we can set the refreshing parameters now
909 status = LyXText::NEED_MORE_REFRESH;
911 refresh_row = tmprow;
912 SetCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
916 void LyXText::RedoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
918 Row * tmprow = cur.row();
920 int y = cur.y() - tmprow->baseline();
921 SetHeightOfRow(bview, tmprow);
922 LyXParagraph * first_phys_par = tmprow->par();
924 // find the first row of the paragraph
925 if (first_phys_par != tmprow->par())
926 while (tmprow->previous() && tmprow->previous()->par() != first_phys_par) {
927 tmprow = tmprow->previous();
928 y -= tmprow->height();
930 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
931 tmprow = tmprow->previous();
932 y -= tmprow->height();
935 // we can set the refreshing parameters now
936 if (status == LyXText::UNCHANGED || y < refresh_y) {
938 refresh_row = tmprow;
940 status = LyXText::NEED_MORE_REFRESH;
941 SetCursor(bview, cur.par(), cur.pos());
945 /* deletes and inserts again all paragaphs between the cursor
946 * and the specified par
947 * This function is needed after SetLayout and SetFont etc. */
948 void LyXText::RedoParagraphs(BufferView * bview, LyXCursor const & cur,
949 LyXParagraph const * endpar) const
952 LyXParagraph * tmppar = 0, * first_phys_par = 0;
954 Row * tmprow = cur.row();
956 int y = cur.y() - tmprow->baseline();
958 if (!tmprow->previous()){
959 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
961 first_phys_par = tmprow->par();
962 // find the first row of the paragraph
963 if (first_phys_par != tmprow->par())
964 while (tmprow->previous() &&
965 (tmprow->previous()->par() != first_phys_par)) {
966 tmprow = tmprow->previous();
967 y -= tmprow->height();
969 while (tmprow->previous()
970 && tmprow->previous()->par() == first_phys_par) {
971 tmprow = tmprow->previous();
972 y -= tmprow->height();
976 // we can set the refreshing parameters now
977 status = LyXText::NEED_MORE_REFRESH;
979 refresh_row = tmprow->previous(); /* the real refresh row will
980 be deleted, so I store
984 tmppar = tmprow->next()->par();
987 while (tmppar != endpar) {
988 RemoveRow(tmprow->next());
990 tmppar = tmprow->next()->par();
995 // remove the first one
996 tmprow2 = tmprow; /* this is because tmprow->previous()
998 tmprow = tmprow->previous();
1001 tmppar = first_phys_par;
1005 InsertParagraph(bview, tmppar, tmprow);
1008 while (tmprow->next() && tmprow->next()->par() == tmppar)
1009 tmprow = tmprow->next();
1010 tmppar = tmppar->next();
1012 } while (tmppar != endpar);
1014 // this is because of layout changes
1016 refresh_y -= refresh_row->height();
1017 SetHeightOfRow(bview, refresh_row);
1019 refresh_row = firstrow;
1021 SetHeightOfRow(bview, refresh_row);
1024 if (tmprow && tmprow->next())
1025 SetHeightOfRow(bview, tmprow->next());
1029 bool LyXText::FullRebreak(BufferView * bview)
1035 if (need_break_row) {
1036 BreakAgain(bview, need_break_row);
1044 /* important for the screen */
1047 /* the cursor set functions have a special mechanism. When they
1048 * realize, that you left an empty paragraph, they will delete it.
1049 * They also delete the corresponding row */
1051 // need the selection cursor:
1052 void LyXText::SetSelection(BufferView * bview)
1054 bool const lsel = selection.set();
1056 if (!selection.set()) {
1057 last_sel_cursor = selection.cursor;
1058 selection.start = selection.cursor;
1059 selection.end = selection.cursor;
1062 selection.set(true);
1064 // first the toggling area
1065 if (cursor.y() < last_sel_cursor.y()
1066 || (cursor.y() == last_sel_cursor.y()
1067 && cursor.x() < last_sel_cursor.x())) {
1068 toggle_end_cursor = last_sel_cursor;
1069 toggle_cursor = cursor;
1071 toggle_end_cursor = cursor;
1072 toggle_cursor = last_sel_cursor;
1075 last_sel_cursor = cursor;
1077 // and now the whole selection
1079 if (selection.cursor.par() == cursor.par())
1080 if (selection.cursor.pos() < cursor.pos()) {
1081 selection.end = cursor;
1082 selection.start = selection.cursor;
1084 selection.end = selection.cursor;
1085 selection.start = cursor;
1087 else if (selection.cursor.y() < cursor.y() ||
1088 (selection.cursor.y() == cursor.y() && selection.cursor.x() < cursor.x())) {
1089 selection.end = cursor;
1090 selection.start = selection.cursor;
1093 selection.end = selection.cursor;
1094 selection.start = cursor;
1097 // a selection with no contents is not a selection
1098 if (selection.start.par() == selection.end.par() &&
1099 selection.start.pos() == selection.end.pos())
1100 selection.set(false);
1102 if (inset_owner && (selection.set() || lsel))
1103 inset_owner->SetUpdateStatus(bview, InsetText::SELECTION);
1107 string const LyXText::selectionAsString(Buffer const * buffer) const
1109 if (!selection.set()) return string();
1112 // Special handling if the whole selection is within one paragraph
1113 if (selection.start.par() == selection.end.par()) {
1114 result += selection.start.par()->String(buffer,
1115 selection.start.pos(),
1116 selection.end.pos());
1120 // The selection spans more than one paragraph
1122 // First paragraph in selection
1123 result += selection.start.par()->String(buffer,
1124 selection.start.pos(),
1125 selection.start.par()->size())
1128 // The paragraphs in between (if any)
1129 LyXCursor tmpcur(selection.start);
1130 tmpcur.par(tmpcur.par()->next());
1131 while (tmpcur.par() != selection.end.par()) {
1132 result += tmpcur.par()->String(buffer, 0, tmpcur.par()->size()) + "\n\n";
1133 tmpcur.par(tmpcur.par()->next()); // Or NextAfterFootnote??
1136 // Last paragraph in selection
1137 result += selection.end.par()->String(buffer, 0, selection.end.pos());
1143 void LyXText::ClearSelection(BufferView * /*bview*/) const
1145 selection.set(false);
1146 selection.mark(false);
1147 selection.end = selection.start = cursor;
1151 void LyXText::CursorHome(BufferView * bview) const
1153 SetCursor(bview, cursor.par(), cursor.row()->pos());
1157 void LyXText::CursorEnd(BufferView * bview) const
1159 if (!cursor.row()->next() || cursor.row()->next()->par() != cursor.row()->par())
1160 SetCursor(bview, cursor.par(), RowLast(cursor.row()) + 1);
1162 if (cursor.par()->size() &&
1163 (cursor.par()->GetChar(RowLast(cursor.row())) == ' '
1164 || cursor.par()->IsNewline(RowLast(cursor.row()))))
1165 SetCursor(bview, cursor.par(), RowLast(cursor.row()));
1167 SetCursor(bview,cursor.par(), RowLast(cursor.row()) + 1);
1172 void LyXText::CursorTop(BufferView * bview) const
1174 while (cursor.par()->previous())
1175 cursor.par(cursor.par()->previous());
1176 SetCursor(bview, cursor.par(), 0);
1180 void LyXText::CursorBottom(BufferView * bview) const
1182 while (cursor.par()->next())
1183 cursor.par(cursor.par()->next());
1184 SetCursor(bview, cursor.par(), cursor.par()->size());
1188 void LyXText::ToggleFree(BufferView * bview,
1189 LyXFont const & font, bool toggleall)
1191 // If the mask is completely neutral, tell user
1192 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1193 // Could only happen with user style
1194 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1198 // Try implicit word selection
1199 // If there is a change in the language the implicit word selection
1201 LyXCursor resetCursor = cursor;
1202 bool implicitSelection = (font.language() == ignore_language
1203 && font.number() == LyXFont::IGNORE)
1204 ? SelectWordWhenUnderCursor(bview) : false;
1207 SetFont(bview, font, toggleall);
1209 /* Implicit selections are cleared afterwards and cursor is set to the
1210 original position. */
1211 if (implicitSelection) {
1212 ClearSelection(bview);
1213 cursor = resetCursor;
1214 SetCursor(bview, cursor.par(), cursor.pos());
1215 selection.cursor = cursor;
1218 inset_owner->SetUpdateStatus(bview, InsetText::CURSOR_PAR);
1222 LyXParagraph::size_type
1223 LyXText::BeginningOfMainBody(Buffer const * buf,
1224 LyXParagraph const * par) const
1226 if (textclasslist.Style(buf->params.textclass,
1227 par->GetLayout()).labeltype != LABEL_MANUAL)
1230 return par->BeginningOfMainBody();
1234 /* the DTP switches for paragraphs. LyX will store them in the
1235 * first physicla paragraph. When a paragraph is broken, the top settings
1236 * rest, the bottom settings are given to the new one. So I can make shure,
1237 * they do not duplicate themself and you cannnot make dirty things with
1240 void LyXText::SetParagraph(BufferView * bview,
1241 bool line_top, bool line_bottom,
1242 bool pagebreak_top, bool pagebreak_bottom,
1243 VSpace const & space_top,
1244 VSpace const & space_bottom,
1246 string labelwidthstring,
1249 LyXCursor tmpcursor = cursor;
1250 if (!selection.set()) {
1251 selection.start = cursor;
1252 selection.end = cursor;
1255 // make sure that the depth behind the selection are restored, too
1256 LyXParagraph * endpar = selection.end.par()->next();
1257 LyXParagraph * undoendpar = endpar;
1259 if (endpar && endpar->GetDepth()) {
1260 while (endpar && endpar->GetDepth()) {
1261 endpar = endpar->next();
1262 undoendpar = endpar;
1266 endpar = endpar->next(); // because of parindents etc.
1269 SetUndo(bview->buffer(), Undo::EDIT,
1270 selection.start.par()->previous(),
1274 LyXParagraph * tmppar = selection.end.par();
1275 while (tmppar != selection.start.par()->previous()) {
1276 SetCursor(bview, tmppar, 0);
1277 status = LyXText::NEED_MORE_REFRESH;
1278 refresh_row = cursor.row();
1279 refresh_y = cursor.y() - cursor.row()->baseline();
1280 cursor.par()->params.lineTop(line_top);
1281 cursor.par()->params.lineBottom(line_bottom);
1282 cursor.par()->params.pagebreakTop(pagebreak_top);
1283 cursor.par()->params.pagebreakBottom(pagebreak_bottom);
1284 cursor.par()->params.spaceTop(space_top);
1285 cursor.par()->params.spaceBottom(space_bottom);
1286 // does the layout allow the new alignment?
1287 if (align == LYX_ALIGN_LAYOUT)
1288 align = textclasslist
1289 .Style(bview->buffer()->params.textclass,
1290 cursor.par()->GetLayout()).align;
1291 if (align & textclasslist
1292 .Style(bview->buffer()->params.textclass,
1293 cursor.par()->GetLayout()).alignpossible) {
1294 if (align == textclasslist
1295 .Style(bview->buffer()->params.textclass,
1296 cursor.par()->GetLayout()).align)
1297 cursor.par()->params.align(LYX_ALIGN_LAYOUT);
1299 cursor.par()->params.align(align);
1301 cursor.par()->SetLabelWidthString(labelwidthstring);
1302 cursor.par()->params.noindent(noindent);
1303 tmppar = cursor.par()->previous();
1306 RedoParagraphs(bview, selection.start, endpar);
1308 ClearSelection(bview);
1309 SetCursor(bview, selection.start.par(), selection.start.pos());
1310 selection.cursor = cursor;
1311 SetCursor(bview, selection.end.par(), selection.end.pos());
1312 SetSelection(bview);
1313 SetCursor(bview, tmpcursor.par(), tmpcursor.pos());
1315 bview->updateInset(inset_owner, true);
1319 char loweralphaCounter(int n)
1321 if (n < 1 || n > 26)
1331 char alphaCounter(int n)
1333 if (n < 1 || n > 26)
1341 char hebrewCounter(int n)
1343 static const char hebrew[22] = {
1344 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1345 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1346 '÷', 'ø', 'ù', 'ú'
1348 if (n < 1 || n > 22)
1356 string const romanCounter(int n)
1358 static char const * roman[20] = {
1359 "i", "ii", "iii", "iv", "v",
1360 "vi", "vii", "viii", "ix", "x",
1361 "xi", "xii", "xiii", "xiv", "xv",
1362 "xvi", "xvii", "xviii", "xix", "xx"
1364 if (n < 1 || n > 20)
1373 // set the counter of a paragraph. This includes the labels
1374 void LyXText::SetCounter(Buffer const * buf, LyXParagraph * par) const
1376 LyXLayout const & layout =
1377 textclasslist.Style(buf->params.textclass,
1380 LyXTextClass const & textclass =
1381 textclasslist.TextClass(buf->params.textclass);
1383 /* copy the prev-counters to this one, unless this is the start of a
1384 footnote or of a bibliography or the very first paragraph */
1386 && !(textclasslist.Style(buf->params.textclass,
1387 par->previous()->GetLayout()
1388 ).labeltype != LABEL_BIBLIO
1389 && layout.labeltype == LABEL_BIBLIO)) {
1390 for (int i = 0; i < 10; ++i) {
1391 par->setCounter(i, par->previous()->GetFirstCounter(i));
1393 par->params.appendix(par->previous()->params.appendix());
1394 if (!par->params.appendix() && par->params.startOfAppendix()) {
1395 par->params.appendix(true);
1396 for (int i = 0; i < 10; ++i) {
1397 par->setCounter(i, 0);
1400 par->enumdepth = par->previous()->enumdepth;
1401 par->itemdepth = par->previous()->itemdepth;
1403 for (int i = 0; i < 10; ++i) {
1404 par->setCounter(i, 0);
1406 par->params.appendix(par->params.startOfAppendix());
1411 /* Maybe we have to increment the enumeration depth.
1412 * BUT, enumeration in a footnote is considered in isolation from its
1413 * surrounding paragraph so don't increment if this is the
1414 * first line of the footnote
1415 * AND, bibliographies can't have their depth changed ie. they
1416 * are always of depth 0
1419 && par->previous()->GetDepth() < par->GetDepth()
1420 && textclasslist.Style(buf->params.textclass,
1421 par->previous()->GetLayout()
1422 ).labeltype == LABEL_COUNTER_ENUMI
1423 && par->enumdepth < 3
1424 && layout.labeltype != LABEL_BIBLIO) {
1428 /* Maybe we have to decrement the enumeration depth, see note above */
1430 && par->previous()->GetDepth() > par->GetDepth()
1431 && layout.labeltype != LABEL_BIBLIO) {
1432 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1433 par->setCounter(6 + par->enumdepth,
1434 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1435 /* reset the counters.
1436 * A depth change is like a breaking layout
1438 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1439 par->setCounter(i, 0);
1442 if (!par->params.labelString().empty()) {
1443 par->params.labelString(string());
1446 if (layout.margintype == MARGIN_MANUAL) {
1447 if (par->params.labelWidthString().empty()) {
1448 par->SetLabelWidthString(layout.labelstring());
1451 par->SetLabelWidthString(string());
1454 /* is it a layout that has an automatic label ? */
1455 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1457 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1458 if (i >= 0 && i<= buf->params.secnumdepth) {
1459 par->incCounter(i); // increment the counter
1461 // Is there a label? Useful for Chapter layout
1462 if (!par->params.appendix()) {
1463 if (!layout.labelstring().empty())
1464 par->params.labelString(layout.labelstring());
1466 par->params.labelString(string());
1468 if (!layout.labelstring_appendix().empty())
1469 par->params.labelString(layout.labelstring_appendix());
1471 par->params.labelString(string());
1474 std::ostringstream s;
1476 if (!par->params.appendix()) {
1477 switch (2 * LABEL_COUNTER_CHAPTER -
1478 textclass.maxcounter() + i) {
1479 case LABEL_COUNTER_CHAPTER:
1480 s << par->getCounter(i);
1482 case LABEL_COUNTER_SECTION:
1483 s << par->getCounter(i - 1) << '.'
1484 << par->getCounter(i);
1486 case LABEL_COUNTER_SUBSECTION:
1487 s << par->getCounter(i - 2) << '.'
1488 << par->getCounter(i - 1) << '.'
1489 << par->getCounter(i);
1491 case LABEL_COUNTER_SUBSUBSECTION:
1492 s << par->getCounter(i - 3) << '.'
1493 << par->getCounter(i - 2) << '.'
1494 << par->getCounter(i - 1) << '.'
1495 << par->getCounter(i);
1498 case LABEL_COUNTER_PARAGRAPH:
1499 s << par->getCounter(i - 4) << '.'
1500 << par->getCounter(i - 3) << '.'
1501 << par->getCounter(i - 2) << '.'
1502 << par->getCounter(i - 1) << '.'
1503 << par->getCounter(i);
1505 case LABEL_COUNTER_SUBPARAGRAPH:
1506 s << par->getCounter(i - 5) << '.'
1507 << par->getCounter(i - 4) << '.'
1508 << par->getCounter(i - 3) << '.'
1509 << par->getCounter(i - 2) << '.'
1510 << par->getCounter(i - 1) << '.'
1511 << par->getCounter(i);
1515 // Can this ever be reached? And in the
1516 // case it is, how can this be correct?
1518 s << par->getCounter(i) << '.';
1521 } else { // appendix
1522 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1523 case LABEL_COUNTER_CHAPTER:
1524 if (par->isRightToLeftPar(buf->params))
1525 s << hebrewCounter(par->getCounter(i));
1527 s << alphaCounter(par->getCounter(i));
1529 case LABEL_COUNTER_SECTION:
1530 if (par->isRightToLeftPar(buf->params))
1531 s << hebrewCounter(par->getCounter(i - 1));
1533 s << alphaCounter(par->getCounter(i - 1));
1536 << par->getCounter(i);
1539 case LABEL_COUNTER_SUBSECTION:
1540 if (par->isRightToLeftPar(buf->params))
1541 s << hebrewCounter(par->getCounter(i - 2));
1543 s << alphaCounter(par->getCounter(i - 2));
1546 << par->getCounter(i-1) << '.'
1547 << par->getCounter(i);
1550 case LABEL_COUNTER_SUBSUBSECTION:
1551 if (par->isRightToLeftPar(buf->params))
1552 s << hebrewCounter(par->getCounter(i-3));
1554 s << alphaCounter(par->getCounter(i-3));
1557 << par->getCounter(i-2) << '.'
1558 << par->getCounter(i-1) << '.'
1559 << par->getCounter(i);
1562 case LABEL_COUNTER_PARAGRAPH:
1563 if (par->isRightToLeftPar(buf->params))
1564 s << hebrewCounter(par->getCounter(i-4));
1566 s << alphaCounter(par->getCounter(i-4));
1569 << par->getCounter(i-3) << '.'
1570 << par->getCounter(i-2) << '.'
1571 << par->getCounter(i-1) << '.'
1572 << par->getCounter(i);
1575 case LABEL_COUNTER_SUBPARAGRAPH:
1576 if (par->isRightToLeftPar(buf->params))
1577 s << hebrewCounter(par->getCounter(i-5));
1579 s << alphaCounter(par->getCounter(i-5));
1582 << par->getCounter(i-4) << '.'
1583 << par->getCounter(i-3) << '.'
1584 << par->getCounter(i-2) << '.'
1585 << par->getCounter(i-1) << '.'
1586 << par->getCounter(i);
1590 // Can this ever be reached? And in the
1591 // case it is, how can this be correct?
1593 s << par->getCounter(i) << '.';
1599 par->params.labelString(par->params.labelString() +s.str().c_str());
1600 // We really want to remove the c_str as soon as
1603 for (i++; i < 10; ++i) {
1604 // reset the following counters
1605 par->setCounter(i, 0);
1607 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1608 for (i++; i < 10; ++i) {
1609 // reset the following counters
1610 par->setCounter(i, 0);
1612 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1613 par->incCounter(i + par->enumdepth);
1614 int number = par->getCounter(i + par->enumdepth);
1616 std::ostringstream s;
1618 switch (par->enumdepth) {
1620 if (par->isRightToLeftPar(buf->params))
1622 << hebrewCounter(number)
1626 << loweralphaCounter(number)
1630 if (par->isRightToLeftPar(buf->params))
1631 s << '.' << romanCounter(number);
1633 s << romanCounter(number) << '.';
1636 if (par->isRightToLeftPar(buf->params))
1638 << alphaCounter(number);
1640 s << alphaCounter(number)
1644 if (par->isRightToLeftPar(buf->params))
1651 par->params.labelString(s.str().c_str());
1652 // we really want to get rid of that c_str()
1654 for (i += par->enumdepth + 1; i < 10; ++i)
1655 par->setCounter(i, 0); /* reset the following counters */
1658 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1659 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1661 int number = par->getCounter(i);
1663 InsetCommandParams p( "bibitem" );
1664 par->bibkey = new InsetBibKey(p);
1666 par->bibkey->setCounter(number);
1667 par->params.labelString(layout.labelstring());
1669 // In biblio should't be following counters but...
1671 string s = layout.labelstring();
1673 // the caption hack:
1674 if (layout.labeltype == LABEL_SENSITIVE) {
1675 bool isOK (par->InInset() && par->InInset()->owner() &&
1676 (par->InInset()->owner()->LyxCode() == Inset::FLOAT_CODE));
1679 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1681 = floatList.getType(tmp->type());
1682 // We should get the correct number here too.
1683 s = fl.name() + " #:";
1685 /* par->SetLayout(0);
1686 s = layout->labelstring; */
1687 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1688 ? " :úåòîùî øñç" : "Senseless: ";
1691 par->params.labelString(s);
1693 /* reset the enumeration counter. They are always resetted
1694 * when there is any other layout between */
1695 for (int i = 6 + par->enumdepth; i < 10; ++i)
1696 par->setCounter(i, 0);
1701 /* Updates all counters BEHIND the row. Changed paragraphs
1702 * with a dynamic left margin will be rebroken. */
1703 void LyXText::UpdateCounters(BufferView * bview, Row * row) const
1711 par = row->par()->next();
1715 while (row->par() != par)
1718 SetCounter(bview->buffer(), par);
1720 /* now check for the headline layouts. remember that they
1721 * have a dynamic left margin */
1722 if ((textclasslist.Style(bview->buffer()->params.textclass,
1723 par->layout).margintype == MARGIN_DYNAMIC
1724 || textclasslist.Style(bview->buffer()->params.textclass,
1725 par->layout).labeltype == LABEL_SENSITIVE)) {
1727 /* Rebreak the paragraph */
1728 RemoveParagraph(row);
1729 AppendParagraph(bview, row);
1736 /* insets an inset. */
1737 void LyXText::InsertInset(BufferView * bview, Inset * inset)
1739 if (!cursor.par()->InsertInsetAllowed(inset))
1741 SetUndo(bview->buffer(), Undo::INSERT,
1742 cursor.par()->previous(),
1743 cursor.par()->next());
1744 cursor.par()->InsertInset(cursor.pos(), inset);
1745 InsertChar(bview, LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1746 * The character will not be inserted a
1749 // If we enter a highly editable inset the cursor should be to before
1750 // the inset. This couldn't happen before as Undo was not handled inside
1751 // inset now after the Undo LyX tries to call inset->Edit(...) again
1752 // and cannot do this as the cursor is behind the inset and GetInset
1753 // does not return the inset!
1754 if (inset->Editable() == Inset::HIGHLY_EDITABLE) {
1755 CursorLeft(bview, true);
1761 void LyXText::copyEnvironmentType()
1763 copylayouttype = cursor.par()->GetLayout();
1767 void LyXText::pasteEnvironmentType(BufferView * bview)
1769 SetLayout(bview, copylayouttype);
1773 void LyXText::CutSelection(BufferView * bview, bool doclear)
1775 // Stuff what we got on the clipboard. Even if there is no selection.
1777 // There is a problem with having the stuffing here in that the
1778 // larger the selection the slower LyX will get. This can be
1779 // solved by running the line below only when the selection has
1780 // finished. The solution used currently just works, to make it
1781 // faster we need to be more clever and probably also have more
1782 // calls to stuffClipboard. (Lgb)
1783 bview->stuffClipboard(selectionAsString(bview->buffer()));
1785 // This doesn't make sense, if there is no selection
1786 if (!selection.set())
1789 // OK, we have a selection. This is always between selection.start
1790 // and selection.end
1792 // make sure that the depth behind the selection are restored, too
1793 LyXParagraph * endpar = selection.end.par()->next();
1794 LyXParagraph * undoendpar = endpar;
1796 if (endpar && endpar->GetDepth()) {
1797 while (endpar && endpar->GetDepth()) {
1798 endpar = endpar->next();
1799 undoendpar = endpar;
1801 } else if (endpar) {
1802 endpar = endpar->next(); // because of parindents etc.
1805 SetUndo(bview->buffer(), Undo::DELETE,
1806 selection.start.par()->previous(),
1811 // there are two cases: cut only within one paragraph or
1812 // more than one paragraph
1813 if (selection.start.par() == selection.end.par()) {
1814 // only within one paragraph
1815 endpar = selection.end.par();
1816 int pos = selection.end.pos();
1817 cap.cutSelection(selection.start.par(), &endpar,
1818 selection.start.pos(), pos,
1819 bview->buffer()->params.textclass, doclear);
1820 selection.end.pos(pos);
1822 endpar = selection.end.par();
1823 int pos = selection.end.pos();
1824 cap.cutSelection(selection.start.par(), &endpar,
1825 selection.start.pos(), pos,
1826 bview->buffer()->params.textclass, doclear);
1828 selection.end.par(endpar);
1829 selection.end.pos(pos);
1830 cursor.pos(selection.end.pos());
1832 endpar = endpar->next();
1834 // sometimes necessary
1836 selection.start.par()->StripLeadingSpaces(bview->buffer()->params.textclass);
1838 RedoParagraphs(bview, selection.start, endpar);
1840 // cutSelection can invalidate the cursor so we need to set
1842 cursor = selection.start;
1844 // need a valid cursor. (Lgb)
1845 ClearSelection(bview);
1847 SetCursor(bview, cursor.par(), cursor.pos());
1848 selection.cursor = cursor;
1849 UpdateCounters(bview, cursor.row());
1853 void LyXText::CopySelection(BufferView * bview)
1855 // Stuff what we got on the clipboard. Even if there is no selection.
1857 // There is a problem with having the stuffing here in that the
1858 // larger the selection the slower LyX will get. This can be
1859 // solved by running the line below only when the selection has
1860 // finished. The solution used currently just works, to make it
1861 // faster we need to be more clever and probably also have more
1862 // calls to stuffClipboard. (Lgb)
1863 bview->stuffClipboard(selectionAsString(bview->buffer()));
1865 // this doesnt make sense, if there is no selection
1866 if (!selection.set())
1869 // ok we have a selection. This is always between selection.start
1870 // and sel_end cursor
1872 // copy behind a space if there is one
1873 while (selection.start.par()->size() > selection.start.pos()
1874 && selection.start.par()->IsLineSeparator(selection.start.pos())
1875 && (selection.start.par() != selection.end.par()
1876 || selection.start.pos() < selection.end.pos()))
1877 selection.start.pos(selection.start.pos() + 1);
1881 cap.copySelection(selection.start.par(), selection.end.par(),
1882 selection.start.pos(), selection.end.pos(),
1883 bview->buffer()->params.textclass);
1887 void LyXText::PasteSelection(BufferView * bview)
1891 // this does not make sense, if there is nothing to paste
1892 if (!cap.checkPastePossible(cursor.par()))
1895 SetUndo(bview->buffer(), Undo::INSERT,
1896 cursor.par()->previous(),
1897 cursor.par()->next());
1899 LyXParagraph * endpar;
1900 LyXParagraph * actpar = cursor.par();
1902 int pos = cursor.pos();
1903 cap.pasteSelection(&actpar, &endpar, pos,
1904 bview->buffer()->params.textclass);
1906 RedoParagraphs(bview, cursor, endpar);
1908 SetCursor(bview, cursor.par(), cursor.pos());
1909 ClearSelection(bview);
1911 selection.cursor = cursor;
1912 SetCursor(bview, actpar, pos);
1913 SetSelection(bview);
1914 UpdateCounters(bview, cursor.row());
1918 // returns a pointer to the very first LyXParagraph
1919 LyXParagraph * LyXText::FirstParagraph() const
1921 return OwnerParagraph();
1925 // sets the selection over the number of characters of string, no check!!
1926 void LyXText::SetSelectionOverString(BufferView * bview, string const & str)
1931 selection.cursor = cursor;
1932 for (string::size_type i = 0; i < str.length(); ++i)
1934 SetSelection(bview);
1938 // simple replacing. The font of the first selected character is used
1939 void LyXText::ReplaceSelectionWithString(BufferView * bview,
1942 SetCursorParUndo(bview->buffer());
1945 if (!selection.set()) { // create a dummy selection
1946 selection.end = cursor;
1947 selection.start = cursor;
1950 // Get font setting before we cut
1951 LyXParagraph::size_type pos = selection.end.pos();
1952 LyXFont const font = selection.start.par()
1953 ->GetFontSettings(bview->buffer()->params,
1954 selection.start.pos());
1956 // Insert the new string
1957 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1958 selection.end.par()->InsertChar(pos, (*cit), font);
1962 // Cut the selection
1963 CutSelection(bview);
1969 // needed to insert the selection
1970 void LyXText::InsertStringA(BufferView * bview, string const & str)
1972 LyXParagraph * par = cursor.par();
1973 LyXParagraph::size_type pos = cursor.pos();
1974 LyXParagraph::size_type a = 0;
1975 LyXParagraph * endpar = cursor.par()->next();
1977 SetCursorParUndo(bview->buffer());
1980 textclasslist.Style(bview->buffer()->params.textclass,
1981 cursor.par()->GetLayout()).isEnvironment();
1982 // only to be sure, should not be neccessary
1983 ClearSelection(bview);
1985 // insert the string, don't insert doublespace
1986 string::size_type i = 0;
1987 while (i < str.length()) {
1988 if (str[i] != '\n') {
1990 && i + 1 < str.length() && str[i + 1] != ' '
1991 && pos && par->GetChar(pos - 1)!= ' ') {
1992 par->InsertChar(pos, ' ', current_font);
1994 } else if (str[i] == ' ') {
1995 InsetSpecialChar * new_inset =
1996 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1997 if (par->InsertInsetAllowed(new_inset)) {
1998 par->InsertInset(pos, new_inset,
2004 } else if (str[i] == '\t') {
2005 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2006 InsetSpecialChar * new_inset =
2007 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2008 if (par->InsertInsetAllowed(new_inset)) {
2009 par->InsertInset(pos, new_inset,
2016 } else if (str[i] != 13 &&
2017 // Ignore unprintables
2018 (str[i] & 127) >= ' ') {
2019 par->InsertChar(pos, str[i], current_font);
2023 if (!par->size()) { // par is empty
2024 InsetSpecialChar * new_inset =
2025 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2026 if (par->InsertInsetAllowed(new_inset)) {
2027 par->InsertInset(pos,
2035 par->BreakParagraph(bview->buffer()->params, pos, flag);
2042 RedoParagraphs(bview, cursor, endpar);
2043 SetCursor(bview, cursor.par(), cursor.pos());
2044 selection.cursor = cursor;
2045 SetCursor(bview, par, pos);
2046 SetSelection(bview);
2050 /* turns double-CR to single CR, others where converted into one blank and 13s
2051 * that are ignored .Double spaces are also converted into one. Spaces at
2052 * the beginning of a paragraph are forbidden. tabs are converted into one
2053 * space. then InsertStringA is called */
2054 void LyXText::InsertStringB(BufferView * bview, string const & s)
2057 string::size_type i = 1;
2058 while (i < str.length()) {
2061 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2063 if (str[i] == '\n' && i + 1 < str.length()) {
2064 if (str[i + 1] != '\n') {
2065 if (str[i - 1] != ' ')
2070 while (i + 1 < str.length()
2071 && (str[i + 1] == ' '
2072 || str[i + 1] == '\t'
2073 || str[i + 1] == '\n'
2074 || str[i + 1] == 13)) {
2081 InsertStringA(bview, str);
2085 bool LyXText::GotoNextInset(BufferView * bview,
2086 std::vector<Inset::Code> const & codes,
2087 string const & contents) const
2089 LyXCursor res = cursor;
2092 if (res.pos() < res.par()->size() - 1) {
2093 res.pos(res.pos() + 1);
2095 res.par(res.par()->next());
2099 } while (res.par() &&
2100 !(res.par()->GetChar(res.pos()) == LyXParagraph::META_INSET
2101 && (inset = res.par()->GetInset(res.pos())) != 0
2102 && find(codes.begin(), codes.end(), inset->LyxCode())
2104 && (contents.empty() ||
2105 static_cast<InsetCommand *>(res.par()->GetInset(res.pos()))->getContents()
2109 SetCursor(bview, res.par(), res.pos());
2116 void LyXText::CheckParagraph(BufferView * bview, LyXParagraph * par,
2117 LyXParagraph::size_type pos)
2119 LyXCursor tmpcursor;
2122 LyXParagraph::size_type z;
2123 Row * row = GetRow(par, pos, y);
2125 // is there a break one row above
2126 if (row->previous() && row->previous()->par() == row->par()) {
2127 z = NextBreakPoint(bview, row->previous(), workWidth(bview));
2128 if (z >= row->pos()) {
2129 // set the dimensions of the row above
2130 y -= row->previous()->height();
2132 refresh_row = row->previous();
2133 status = LyXText::NEED_MORE_REFRESH;
2135 BreakAgain(bview, row->previous());
2137 // set the cursor again. Otherwise
2138 // dangling pointers are possible
2139 SetCursor(bview, cursor.par(), cursor.pos(),
2140 false, cursor.boundary());
2141 selection.cursor = cursor;
2146 int const tmpheight = row->height();
2147 LyXParagraph::size_type const tmplast = RowLast(row);
2151 BreakAgain(bview, row);
2152 if (row->height() == tmpheight && RowLast(row) == tmplast)
2153 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2155 status = LyXText::NEED_MORE_REFRESH;
2157 // check the special right address boxes
2158 if (textclasslist.Style(bview->buffer()->params.textclass,
2159 par->GetLayout()).margintype
2160 == MARGIN_RIGHT_ADDRESS_BOX) {
2167 RedoDrawingOfParagraph(bview, tmpcursor);
2170 // set the cursor again. Otherwise dangling pointers are possible
2171 // also set the selection
2173 if (selection.set()) {
2175 SetCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2176 false, selection.cursor.boundary());
2177 selection.cursor = cursor;
2178 SetCursorIntern(bview, selection.start.par(),
2179 selection.start.pos(),
2180 false, selection.start.boundary());
2181 selection.start = cursor;
2182 SetCursorIntern(bview, selection.end.par(),
2183 selection.end.pos(),
2184 false, selection.end.boundary());
2185 selection.end = cursor;
2186 SetCursorIntern(bview, last_sel_cursor.par(),
2187 last_sel_cursor.pos(),
2188 false, last_sel_cursor.boundary());
2189 last_sel_cursor = cursor;
2192 SetCursorIntern(bview, cursor.par(), cursor.pos(),
2193 false, cursor.boundary());
2197 // returns false if inset wasn't found
2198 bool LyXText::UpdateInset(BufferView * bview, Inset * inset)
2200 // first check the current paragraph
2201 int pos = cursor.par()->GetPositionOfInset(inset);
2203 CheckParagraph(bview, cursor.par(), pos);
2207 // check every paragraph
2209 LyXParagraph * par = FirstParagraph();
2211 pos = par->GetPositionOfInset(inset);
2213 CheckParagraph(bview, par, pos);
2223 void LyXText::SetCursor(BufferView * bview, LyXParagraph * par,
2224 LyXParagraph::size_type pos,
2225 bool setfont, bool boundary) const
2227 LyXCursor old_cursor = cursor;
2228 SetCursorIntern(bview, par, pos, setfont, boundary);
2229 DeleteEmptyParagraphMechanism(bview, old_cursor);
2233 void LyXText::SetCursor(BufferView *bview, LyXCursor & cur, LyXParagraph * par,
2234 LyXParagraph::size_type pos, bool boundary) const
2238 cur.boundary(boundary);
2240 /* get the cursor y position in text */
2242 Row * row = GetRow(par, pos, y);
2243 /* y is now the beginning of the cursor row */
2244 y += row->baseline();
2245 /* y is now the cursor baseline */
2248 /* now get the cursors x position */
2250 float fill_separator, fill_hfill, fill_label_hfill;
2251 PrepareToPrint(bview, row, x, fill_separator, fill_hfill,
2253 LyXParagraph::size_type cursor_vpos = 0;
2254 LyXParagraph::size_type last = RowLastPrintable(row);
2256 if (pos > last + 1) // This shouldn't happen.
2258 else if (pos < row->pos())
2261 if (last < row->pos())
2262 cursor_vpos = row->pos();
2263 else if (pos > last && !boundary)
2264 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2265 ? row->pos() : last + 1;
2266 else if (pos > row->pos() &&
2267 (pos > last || boundary))
2268 /// Place cursor after char at (logical) position pos - 1
2269 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2270 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2272 /// Place cursor before char at (logical) position pos
2273 cursor_vpos = (bidi_level(pos) % 2 == 0)
2274 ? log2vis(pos) : log2vis(pos) + 1;
2276 LyXParagraph::size_type main_body =
2277 BeginningOfMainBody(bview->buffer(), row->par());
2278 if ((main_body > 0) &&
2279 ((main_body-1 > last) ||
2280 !row->par()->IsLineSeparator(main_body-1)))
2283 for (LyXParagraph::size_type vpos = row->pos();
2284 vpos < cursor_vpos; ++vpos) {
2285 pos = vis2log(vpos);
2286 if (main_body > 0 && pos == main_body - 1) {
2287 x += fill_label_hfill +
2288 lyxfont::width(textclasslist.Style(
2289 bview->buffer()->params.textclass,
2290 row->par()->GetLayout())
2292 GetFont(bview->buffer(), row->par(), -2));
2293 if (row->par()->IsLineSeparator(main_body-1))
2294 x -= SingleWidth(bview, row->par(),main_body-1);
2296 if (HfillExpansion(bview->buffer(), row, pos)) {
2297 x += SingleWidth(bview, row->par(), pos);
2298 if (pos >= main_body)
2301 x += fill_label_hfill;
2302 } else if (row->par()->IsSeparator(pos)) {
2303 x += SingleWidth(bview, row->par(), pos);
2304 if (pos >= main_body)
2305 x += fill_separator;
2307 x += SingleWidth(bview, row->par(), pos);
2316 void LyXText::SetCursorIntern(BufferView * bview, LyXParagraph * par,
2317 LyXParagraph::size_type pos,
2318 bool setfont, bool boundary) const
2320 SetCursor(bview, cursor, par, pos, boundary);
2322 SetCurrentFont(bview);
2326 void LyXText::SetCurrentFont(BufferView * bview) const
2328 LyXParagraph::size_type pos = cursor.pos();
2329 if (cursor.boundary() && pos > 0)
2333 if (pos == cursor.par()->size())
2335 else // potentional bug... BUG (Lgb)
2336 if (cursor.par()->IsSeparator(pos)) {
2337 if (pos > cursor.row()->pos() &&
2338 bidi_level(pos) % 2 ==
2339 bidi_level(pos - 1) % 2)
2341 else if (pos + 1 < cursor.par()->size())
2347 cursor.par()->GetFontSettings(bview->buffer()->params, pos);
2348 real_current_font = GetFont(bview->buffer(), cursor.par(), pos);
2350 if (cursor.pos() == cursor.par()->size() &&
2351 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2352 !cursor.boundary()) {
2353 Language const * lang =
2354 cursor.par()->getParLanguage(bview->buffer()->params);
2355 current_font.setLanguage(lang);
2356 current_font.setNumber(LyXFont::OFF);
2357 real_current_font.setLanguage(lang);
2358 real_current_font.setNumber(LyXFont::OFF);
2363 void LyXText::SetCursorFromCoordinates(BufferView * bview, int x, int y) const
2365 LyXCursor old_cursor = cursor;
2367 /* get the row first */
2369 Row * row = GetRowNearY(y);
2370 cursor.par(row->par());
2373 int column = GetColumnNearX(bview, row, x, bound);
2374 cursor.pos(row->pos() + column);
2376 cursor.y(y + row->baseline());
2378 cursor.boundary(bound);
2379 SetCurrentFont(bview);
2380 DeleteEmptyParagraphMechanism(bview, old_cursor);
2384 void LyXText::SetCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2387 /* get the row first */
2389 Row * row = GetRowNearY(y);
2391 int column = GetColumnNearX(bview, row, x, bound);
2393 cur.par(row->par());
2394 cur.pos(row->pos() + column);
2396 cur.y(y + row->baseline());
2398 cur.boundary(bound);
2402 void LyXText::CursorLeft(BufferView * bview, bool internal) const
2404 if (cursor.pos() > 0) {
2405 bool boundary = cursor.boundary();
2406 SetCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2407 if (!internal && !boundary &&
2408 IsBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2409 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2410 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2411 LyXParagraph * par = cursor.par()->previous();
2412 SetCursor(bview, par, par->size());
2417 void LyXText::CursorRight(BufferView * bview, bool internal) const
2419 if (!internal && cursor.boundary() &&
2420 !cursor.par()->IsNewline(cursor.pos()))
2421 SetCursor(bview, cursor.par(), cursor.pos(), true, false);
2422 else if (cursor.pos() < cursor.par()->size()) {
2423 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2425 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2426 SetCursor(bview, cursor.par(), cursor.pos(), true, true);
2427 } else if (cursor.par()->next())
2428 SetCursor(bview, cursor.par()->next(), 0);
2432 void LyXText::CursorUp(BufferView * bview) const
2434 SetCursorFromCoordinates(bview, cursor.x_fix(),
2435 cursor.y() - cursor.row()->baseline() - 1);
2439 void LyXText::CursorDown(BufferView * bview) const
2441 SetCursorFromCoordinates(bview, cursor.x_fix(),
2442 cursor.y() - cursor.row()->baseline()
2443 + cursor.row()->height() + 1);
2447 void LyXText::CursorUpParagraph(BufferView * bview) const
2449 if (cursor.pos() > 0) {
2450 SetCursor(bview, cursor.par(), 0);
2452 else if (cursor.par()->previous()) {
2453 SetCursor(bview, cursor.par()->previous(), 0);
2458 void LyXText::CursorDownParagraph(BufferView * bview) const
2460 if (cursor.par()->next()) {
2461 SetCursor(bview, cursor.par()->next(), 0);
2463 SetCursor(bview, cursor.par(), cursor.par()->size());
2468 void LyXText::DeleteEmptyParagraphMechanism(BufferView * bview,
2469 LyXCursor const & old_cursor) const
2471 // Would be wrong to delete anything if we have a selection.
2472 if (selection.set()) return;
2474 // We allow all kinds of "mumbo-jumbo" when freespacing.
2475 if (textclasslist.Style(bview->buffer()->params.textclass,
2476 old_cursor.par()->GetLayout()).free_spacing)
2479 bool deleted = false;
2481 /* Ok I'll put some comments here about what is missing.
2482 I have fixed BackSpace (and thus Delete) to not delete
2483 double-spaces automagically. I have also changed Cut,
2484 Copy and Paste to hopefully do some sensible things.
2485 There are still some small problems that can lead to
2486 double spaces stored in the document file or space at
2487 the beginning of paragraphs. This happens if you have
2488 the cursor betwenn to spaces and then save. Or if you
2489 cut and paste and the selection have a space at the
2490 beginning and then save right after the paste. I am
2491 sure none of these are very hard to fix, but I will
2492 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2493 that I can get some feedback. (Lgb)
2496 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2497 // delete the LineSeparator.
2500 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2501 // delete the LineSeparator.
2504 // If the pos around the old_cursor were spaces, delete one of them.
2505 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2506 // Only if the cursor has really moved
2508 if (old_cursor.pos() > 0
2509 && old_cursor.pos() < old_cursor.par()->size()
2510 && old_cursor.par()->IsLineSeparator(old_cursor.pos())
2511 && old_cursor.par()->IsLineSeparator(old_cursor.pos() - 1)) {
2512 old_cursor.par()->Erase(old_cursor.pos() - 1);
2513 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2515 if (old_cursor.par() == cursor.par() &&
2516 cursor.pos() > old_cursor.pos()) {
2517 SetCursorIntern(bview, cursor.par(),
2520 SetCursorIntern(bview, cursor.par(),
2526 // Do not delete empty paragraphs with keepempty set.
2527 if ((textclasslist.Style(bview->buffer()->params.textclass,
2528 old_cursor.par()->GetLayout())).keepempty)
2531 LyXCursor tmpcursor;
2533 if (old_cursor.par() != cursor.par()) {
2534 if ((old_cursor.par()->size() == 0
2535 || (old_cursor.par()->size() == 1
2536 && old_cursor.par()->IsLineSeparator(0)))) {
2537 // ok, we will delete anything
2539 // make sure that you do not delete any environments
2540 status = LyXText::NEED_MORE_REFRESH;
2543 if (old_cursor.row()->previous()) {
2544 refresh_row = old_cursor.row()->previous();
2545 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2547 cursor = old_cursor; // that undo can restore the right cursor position
2548 LyXParagraph * endpar = old_cursor.par()->next();
2549 if (endpar && endpar->GetDepth()) {
2550 while (endpar && endpar->GetDepth()) {
2551 endpar = endpar->next();
2554 SetUndo(bview->buffer(), Undo::DELETE,
2555 old_cursor.par()->previous(),
2560 RemoveRow(old_cursor.row());
2561 if (OwnerParagraph() == old_cursor.par()) {
2562 OwnerParagraph(OwnerParagraph()->next());
2565 delete old_cursor.par();
2567 /* Breakagain the next par. Needed
2568 * because of the parindent that
2569 * can occur or dissappear. The
2570 * next row can change its height,
2571 * if there is another layout before */
2572 if (refresh_row->next()) {
2573 BreakAgain(bview, refresh_row->next());
2574 UpdateCounters(bview, refresh_row);
2576 SetHeightOfRow(bview, refresh_row);
2578 refresh_row = old_cursor.row()->next();
2579 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2582 cursor = old_cursor; // that undo can restore the right cursor position
2583 LyXParagraph * endpar = old_cursor.par()->next();
2584 if (endpar && endpar->GetDepth()) {
2585 while (endpar && endpar->GetDepth()) {
2586 endpar = endpar->next();
2589 SetUndo(bview->buffer(), Undo::DELETE,
2590 old_cursor.par()->previous(),
2595 RemoveRow(old_cursor.row());
2597 if (OwnerParagraph() == old_cursor.par()) {
2598 OwnerParagraph(OwnerParagraph()->next());
2601 delete old_cursor.par();
2603 /* Breakagain the next par. Needed
2604 because of the parindent that can
2605 occur or dissappear.
2606 The next row can change its height,
2607 if there is another layout before
2610 BreakAgain(bview, refresh_row);
2611 UpdateCounters(bview, refresh_row->previous());
2617 SetCursorIntern(bview, cursor.par(), cursor.pos());
2619 if (selection.cursor.par() == old_cursor.par()
2620 && selection.cursor.pos() == selection.cursor.pos()) {
2621 // correct selection
2622 selection.cursor = cursor;
2626 if (old_cursor.par()->StripLeadingSpaces(bview->buffer()->params.textclass)) {
2627 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2629 SetCursorIntern(bview, cursor.par(), cursor.pos());
2630 selection.cursor = cursor;
2637 LyXParagraph * LyXText::GetParFromID(int id)
2639 LyXParagraph * result = FirstParagraph();
2640 while (result && result->id() != id)
2641 result = result->next();
2647 bool LyXText::TextUndo(BufferView * bview)
2651 // returns false if no undo possible
2652 Undo * undo = bview->buffer()->undostack.pop();
2656 bview->buffer()->redostack
2657 .push(CreateUndo(bview->buffer(), undo->kind,
2658 GetParFromID(undo->number_of_before_par),
2659 GetParFromID(undo->number_of_behind_par)));
2661 return TextHandleUndo(bview, undo);
2665 bool LyXText::TextRedo(BufferView * bview)
2669 // returns false if no redo possible
2670 Undo * undo = bview->buffer()->redostack.pop();
2674 bview->buffer()->undostack
2675 .push(CreateUndo(bview->buffer(), undo->kind,
2676 GetParFromID(undo->number_of_before_par),
2677 GetParFromID(undo->number_of_behind_par)));
2679 return TextHandleUndo(bview, undo);
2683 bool LyXText::TextHandleUndo(BufferView * bview, Undo * undo)
2687 // returns false if no undo possible
2688 bool result = false;
2690 LyXParagraph * before =
2691 GetParFromID(undo->number_of_before_par);
2692 LyXParagraph * behind =
2693 GetParFromID(undo->number_of_behind_par);
2694 LyXParagraph * tmppar;
2695 LyXParagraph * tmppar2;
2696 LyXParagraph * endpar;
2697 LyXParagraph * tmppar5;
2699 // if there's no before take the beginning
2700 // of the document for redoing
2702 SetCursorIntern(bview, FirstParagraph(), 0);
2704 // replace the paragraphs with the undo informations
2706 LyXParagraph * tmppar3 = undo->par;
2707 undo->par = 0; // otherwise the undo destructor would delete the paragraph
2708 LyXParagraph * tmppar4 = tmppar3;
2711 while (tmppar4->next())
2712 tmppar4 = tmppar4->next();
2713 } // get last undo par
2715 // now remove the old text if there is any
2716 if (before != behind || (!behind && !before)) {
2718 tmppar5 = before->next();
2720 tmppar5 = OwnerParagraph();
2722 while (tmppar5 && tmppar5 != behind) {
2724 tmppar5 = tmppar5->next();
2725 // a memory optimization for edit: Only layout information
2726 // is stored in the undo. So restore the text informations.
2727 if (undo->kind == Undo::EDIT) {
2728 tmppar2->setContentsFromPar(tmppar);
2729 tmppar->clearContents();
2730 tmppar2 = tmppar2->next();
2735 // put the new stuff in the list if there is one
2738 before->next(tmppar3);
2740 OwnerParagraph(tmppar3);
2741 tmppar3->previous(before);
2744 OwnerParagraph(behind);
2747 tmppar4->next(behind);
2749 behind->previous(tmppar4);
2753 // Set the cursor for redoing
2755 SetCursorIntern(bview, before, 0);
2758 // calculate the endpar for redoing the paragraphs.
2760 endpar = behind->next();
2764 tmppar = GetParFromID(undo->number_of_cursor_par);
2765 RedoParagraphs(bview, cursor, endpar);
2767 SetCursorIntern(bview, tmppar, undo->cursor_pos);
2768 UpdateCounters(bview, cursor.row());
2778 void LyXText::FinishUndo()
2782 // makes sure the next operation will be stored
2783 undo_finished = true;
2787 void LyXText::FreezeUndo()
2791 // this is dangerous and for internal use only
2796 void LyXText::UnFreezeUndo()
2800 // this is dangerous and for internal use only
2801 undo_frozen = false;
2805 void LyXText::SetUndo(Buffer * buf, Undo::undo_kind kind,
2806 LyXParagraph const * before,
2807 LyXParagraph const * behind) const
2812 buf->undostack.push(CreateUndo(buf, kind, before, behind));
2813 buf->redostack.clear();
2817 void LyXText::SetRedo(Buffer * buf, Undo::undo_kind kind,
2818 LyXParagraph const * before, LyXParagraph const * behind)
2822 buf->redostack.push(CreateUndo(buf, kind, before, behind));
2826 Undo * LyXText::CreateUndo(Buffer * buf, Undo::undo_kind kind,
2827 LyXParagraph const * before,
2828 LyXParagraph const * behind) const
2833 int before_number = -1;
2834 int behind_number = -1;
2836 before_number = before->id();
2838 behind_number = behind->id();
2839 // Undo::EDIT and Undo::FINISH are
2840 // always finished. (no overlapping there)
2841 // overlapping only with insert and delete inside one paragraph:
2842 // Nobody wants all removed character
2843 // appear one by one when undoing.
2844 // EDIT is special since only layout information, not the
2845 // contents of a paragaph are stored.
2846 if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)){
2847 // check wether storing is needed
2848 if (!buf->undostack.empty() &&
2849 buf->undostack.top()->kind == kind &&
2850 buf->undostack.top()->number_of_before_par == before_number &&
2851 buf->undostack.top()->number_of_behind_par == behind_number ){
2856 // create a new Undo
2857 LyXParagraph * undopar;
2859 LyXParagraph * start = 0;
2860 LyXParagraph * end = 0;
2863 start = const_cast<LyXParagraph*>(before->next());
2865 start = FirstParagraph();
2867 end = const_cast<LyXParagraph*>(behind->previous());
2869 end = FirstParagraph();
2873 if (start && end && (start != end->next()) &&
2874 ((before != behind) || (!before && !behind))) {
2875 LyXParagraph * tmppar = start;
2876 LyXParagraph * tmppar2 = new LyXParagraph(*tmppar);
2877 tmppar2->id(tmppar->id());
2879 // a memory optimization: Just store the layout information
2881 if (kind == Undo::EDIT){
2882 //tmppar2->text.clear();
2883 tmppar2->clearContents();
2888 while (tmppar != end && tmppar->next()) {
2889 tmppar = tmppar->next();
2890 tmppar2->next(new LyXParagraph(*tmppar));
2891 tmppar2->next()->id(tmppar->id());
2892 // a memory optimization: Just store the layout
2893 // information when only edit
2894 if (kind == Undo::EDIT){
2895 //tmppar2->next->text.clear();
2896 tmppar2->clearContents();
2898 tmppar2->next()->previous(tmppar2);
2899 tmppar2 = tmppar2->next();
2903 undopar = 0; // nothing to replace (undo of delete maybe)
2905 int cursor_par = cursor.par()->id();
2906 int cursor_pos = cursor.pos();
2908 Undo * undo = new Undo(kind,
2909 before_number, behind_number,
2910 cursor_par, cursor_pos,
2913 undo_finished = false;
2918 void LyXText::SetCursorParUndo(Buffer * buf)
2922 SetUndo(buf, Undo::FINISH,
2923 cursor.par()->previous(),
2924 cursor.par()->next());
2928 void LyXText::toggleAppendix(BufferView * bview)
2930 LyXParagraph * par = cursor.par();
2931 bool start = !par->params.startOfAppendix();
2933 // ensure that we have only one start_of_appendix in this document
2934 LyXParagraph * tmp = FirstParagraph();
2935 for (; tmp; tmp = tmp->next())
2936 tmp->params.startOfAppendix(false);
2938 par->params.startOfAppendix(start);
2940 // we can set the refreshing parameters now
2941 status = LyXText::NEED_MORE_REFRESH;
2943 refresh_row = 0; // not needed for full update
2944 UpdateCounters(bview, 0);
2945 SetCursor(bview, cursor.par(), cursor.pos());
2949 LyXParagraph * LyXText::OwnerParagraph() const
2952 return inset_owner->par;
2954 return bv_owner->buffer()->paragraph;
2958 LyXParagraph * LyXText::OwnerParagraph(LyXParagraph * p) const
2961 inset_owner->par = p;
2963 bv_owner->buffer()->paragraph = p;