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)
1928 selection.cursor = cursor;
1929 for (int i = 0; str[i]; ++i)
1931 SetSelection(bview);
1935 // simple replacing. The font of the first selected character is used
1936 void LyXText::ReplaceSelectionWithString(BufferView * bview,
1939 SetCursorParUndo(bview->buffer());
1942 if (!selection.set()) { // create a dummy selection
1943 selection.end = cursor;
1944 selection.start = cursor;
1947 // Get font setting before we cut
1948 LyXParagraph::size_type pos = selection.end.pos();
1949 LyXFont const font = selection.start.par()
1950 ->GetFontSettings(bview->buffer()->params,
1951 selection.start.pos());
1953 // Insert the new string
1954 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1955 selection.end.par()->InsertChar(pos, (*cit), font);
1959 // Cut the selection
1960 CutSelection(bview);
1966 // needed to insert the selection
1967 void LyXText::InsertStringA(BufferView * bview, string const & str)
1969 LyXParagraph * par = cursor.par();
1970 LyXParagraph::size_type pos = cursor.pos();
1971 LyXParagraph::size_type a = 0;
1972 LyXParagraph * endpar = cursor.par()->next();
1974 SetCursorParUndo(bview->buffer());
1977 textclasslist.Style(bview->buffer()->params.textclass,
1978 cursor.par()->GetLayout()).isEnvironment();
1979 // only to be sure, should not be neccessary
1980 ClearSelection(bview);
1982 // insert the string, don't insert doublespace
1983 string::size_type i = 0;
1984 while (i < str.length()) {
1985 if (str[i] != '\n') {
1987 && i + 1 < str.length() && str[i + 1] != ' '
1988 && pos && par->GetChar(pos - 1)!= ' ') {
1989 par->InsertChar(pos, ' ', current_font);
1991 } else if (str[i] == ' ') {
1992 InsetSpecialChar * new_inset =
1993 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1994 if (par->InsertInsetAllowed(new_inset)) {
1995 par->InsertInset(pos, new_inset,
2001 } else if (str[i] == '\t') {
2002 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2003 InsetSpecialChar * new_inset =
2004 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2005 if (par->InsertInsetAllowed(new_inset)) {
2006 par->InsertInset(pos, new_inset,
2013 } else if (str[i] != 13 &&
2014 // Ignore unprintables
2015 (str[i] & 127) >= ' ') {
2016 par->InsertChar(pos, str[i], current_font);
2020 if (!par->size()) { // par is empty
2021 InsetSpecialChar * new_inset =
2022 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2023 if (par->InsertInsetAllowed(new_inset)) {
2024 par->InsertInset(pos,
2032 par->BreakParagraph(bview->buffer()->params, pos, flag);
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 blank and 13s
2048 * that are ignored .Double spaces are also converted into one. Spaces at
2049 * the beginning of a paragraph are forbidden. tabs are converted into one
2050 * space. then InsertStringA is called */
2051 void LyXText::InsertStringB(BufferView * bview, string const & s)
2054 string::size_type i = 1;
2055 while (i < str.length()) {
2058 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2060 if (str[i] == '\n' && i + 1 < str.length()) {
2061 if (str[i + 1] != '\n') {
2062 if (str[i - 1] != ' ')
2067 while (i + 1 < str.length()
2068 && (str[i + 1] == ' '
2069 || str[i + 1] == '\t'
2070 || str[i + 1] == '\n'
2071 || str[i + 1] == 13)) {
2078 InsertStringA(bview, str);
2082 bool LyXText::GotoNextInset(BufferView * bview,
2083 std::vector<Inset::Code> const & codes,
2084 string const & contents) const
2086 LyXCursor res = cursor;
2089 if (res.pos() < res.par()->size() - 1) {
2090 res.pos(res.pos() + 1);
2092 res.par(res.par()->next());
2096 } while (res.par() &&
2097 !(res.par()->GetChar(res.pos()) == LyXParagraph::META_INSET
2098 && (inset = res.par()->GetInset(res.pos())) != 0
2099 && find(codes.begin(), codes.end(), inset->LyxCode())
2101 && (contents.empty() ||
2102 static_cast<InsetCommand *>(res.par()->GetInset(res.pos()))->getContents()
2106 SetCursor(bview, res.par(), res.pos());
2113 void LyXText::CheckParagraph(BufferView * bview, LyXParagraph * par,
2114 LyXParagraph::size_type pos)
2116 LyXCursor tmpcursor;
2119 LyXParagraph::size_type z;
2120 Row * row = GetRow(par, pos, y);
2122 // is there a break one row above
2123 if (row->previous() && row->previous()->par() == row->par()) {
2124 z = NextBreakPoint(bview, row->previous(), workWidth(bview));
2125 if (z >= row->pos()) {
2126 // set the dimensions of the row above
2127 y -= row->previous()->height();
2129 refresh_row = row->previous();
2130 status = LyXText::NEED_MORE_REFRESH;
2132 BreakAgain(bview, row->previous());
2134 // set the cursor again. Otherwise
2135 // dangling pointers are possible
2136 SetCursor(bview, cursor.par(), cursor.pos(),
2137 false, cursor.boundary());
2138 selection.cursor = cursor;
2143 int const tmpheight = row->height();
2144 LyXParagraph::size_type const tmplast = RowLast(row);
2148 BreakAgain(bview, row);
2149 if (row->height() == tmpheight && RowLast(row) == tmplast)
2150 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2152 status = LyXText::NEED_MORE_REFRESH;
2154 // check the special right address boxes
2155 if (textclasslist.Style(bview->buffer()->params.textclass,
2156 par->GetLayout()).margintype
2157 == MARGIN_RIGHT_ADDRESS_BOX) {
2164 RedoDrawingOfParagraph(bview, tmpcursor);
2167 // set the cursor again. Otherwise dangling pointers are possible
2168 // also set the selection
2170 if (selection.set()) {
2172 SetCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2173 false, selection.cursor.boundary());
2174 selection.cursor = cursor;
2175 SetCursorIntern(bview, selection.start.par(),
2176 selection.start.pos(),
2177 false, selection.start.boundary());
2178 selection.start = cursor;
2179 SetCursorIntern(bview, selection.end.par(),
2180 selection.end.pos(),
2181 false, selection.end.boundary());
2182 selection.end = cursor;
2183 SetCursorIntern(bview, last_sel_cursor.par(),
2184 last_sel_cursor.pos(),
2185 false, last_sel_cursor.boundary());
2186 last_sel_cursor = cursor;
2189 SetCursorIntern(bview, cursor.par(), cursor.pos(),
2190 false, cursor.boundary());
2194 // returns false if inset wasn't found
2195 bool LyXText::UpdateInset(BufferView * bview, Inset * inset)
2197 // first check the current paragraph
2198 int pos = cursor.par()->GetPositionOfInset(inset);
2200 CheckParagraph(bview, cursor.par(), pos);
2204 // check every paragraph
2206 LyXParagraph * par = FirstParagraph();
2208 pos = par->GetPositionOfInset(inset);
2210 CheckParagraph(bview, par, pos);
2220 void LyXText::SetCursor(BufferView * bview, LyXParagraph * par,
2221 LyXParagraph::size_type pos,
2222 bool setfont, bool boundary) const
2224 LyXCursor old_cursor = cursor;
2225 SetCursorIntern(bview, par, pos, setfont, boundary);
2226 DeleteEmptyParagraphMechanism(bview, old_cursor);
2230 void LyXText::SetCursor(BufferView *bview, LyXCursor & cur, LyXParagraph * par,
2231 LyXParagraph::size_type pos, bool boundary) const
2235 cur.boundary(boundary);
2237 /* get the cursor y position in text */
2239 Row * row = GetRow(par, pos, y);
2240 /* y is now the beginning of the cursor row */
2241 y += row->baseline();
2242 /* y is now the cursor baseline */
2245 /* now get the cursors x position */
2247 float fill_separator, fill_hfill, fill_label_hfill;
2248 PrepareToPrint(bview, row, x, fill_separator, fill_hfill,
2250 LyXParagraph::size_type cursor_vpos = 0;
2251 LyXParagraph::size_type last = RowLastPrintable(row);
2253 if (pos > last + 1) // This shouldn't happen.
2255 else if (pos < row->pos())
2258 if (last < row->pos())
2259 cursor_vpos = row->pos();
2260 else if (pos > last && !boundary)
2261 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2262 ? row->pos() : last + 1;
2263 else if (pos > row->pos() &&
2264 (pos > last || boundary))
2265 /// Place cursor after char at (logical) position pos - 1
2266 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2267 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2269 /// Place cursor before char at (logical) position pos
2270 cursor_vpos = (bidi_level(pos) % 2 == 0)
2271 ? log2vis(pos) : log2vis(pos) + 1;
2273 LyXParagraph::size_type main_body =
2274 BeginningOfMainBody(bview->buffer(), row->par());
2275 if ((main_body > 0) &&
2276 ((main_body-1 > last) ||
2277 !row->par()->IsLineSeparator(main_body-1)))
2280 for (LyXParagraph::size_type vpos = row->pos();
2281 vpos < cursor_vpos; ++vpos) {
2282 pos = vis2log(vpos);
2283 if (main_body > 0 && pos == main_body - 1) {
2284 x += fill_label_hfill +
2285 lyxfont::width(textclasslist.Style(
2286 bview->buffer()->params.textclass,
2287 row->par()->GetLayout())
2289 GetFont(bview->buffer(), row->par(), -2));
2290 if (row->par()->IsLineSeparator(main_body-1))
2291 x -= SingleWidth(bview, row->par(),main_body-1);
2293 if (HfillExpansion(bview->buffer(), row, pos)) {
2294 x += SingleWidth(bview, row->par(), pos);
2295 if (pos >= main_body)
2298 x += fill_label_hfill;
2299 } else if (row->par()->IsSeparator(pos)) {
2300 x += SingleWidth(bview, row->par(), pos);
2301 if (pos >= main_body)
2302 x += fill_separator;
2304 x += SingleWidth(bview, row->par(), pos);
2313 void LyXText::SetCursorIntern(BufferView * bview, LyXParagraph * par,
2314 LyXParagraph::size_type pos,
2315 bool setfont, bool boundary) const
2317 SetCursor(bview, cursor, par, pos, boundary);
2319 SetCurrentFont(bview);
2323 void LyXText::SetCurrentFont(BufferView * bview) const
2325 LyXParagraph::size_type pos = cursor.pos();
2326 if (cursor.boundary() && pos > 0)
2330 if (pos == cursor.par()->size())
2332 else // potentional bug... BUG (Lgb)
2333 if (cursor.par()->IsSeparator(pos)) {
2334 if (pos > cursor.row()->pos() &&
2335 bidi_level(pos) % 2 ==
2336 bidi_level(pos - 1) % 2)
2338 else if (pos + 1 < cursor.par()->size())
2344 cursor.par()->GetFontSettings(bview->buffer()->params, pos);
2345 real_current_font = GetFont(bview->buffer(), cursor.par(), pos);
2347 if (cursor.pos() == cursor.par()->size() &&
2348 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2349 !cursor.boundary()) {
2350 Language const * lang =
2351 cursor.par()->getParLanguage(bview->buffer()->params);
2352 current_font.setLanguage(lang);
2353 current_font.setNumber(LyXFont::OFF);
2354 real_current_font.setLanguage(lang);
2355 real_current_font.setNumber(LyXFont::OFF);
2360 void LyXText::SetCursorFromCoordinates(BufferView * bview, int x, int y) const
2362 LyXCursor old_cursor = cursor;
2364 /* get the row first */
2366 Row * row = GetRowNearY(y);
2367 cursor.par(row->par());
2370 int column = GetColumnNearX(bview, row, x, bound);
2371 cursor.pos(row->pos() + column);
2373 cursor.y(y + row->baseline());
2375 cursor.boundary(bound);
2376 SetCurrentFont(bview);
2377 DeleteEmptyParagraphMechanism(bview, old_cursor);
2381 void LyXText::SetCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2384 /* get the row first */
2386 Row * row = GetRowNearY(y);
2388 int column = GetColumnNearX(bview, row, x, bound);
2390 cur.par(row->par());
2391 cur.pos(row->pos() + column);
2393 cur.y(y + row->baseline());
2395 cur.boundary(bound);
2399 void LyXText::CursorLeft(BufferView * bview, bool internal) const
2401 if (cursor.pos() > 0) {
2402 bool boundary = cursor.boundary();
2403 SetCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2404 if (!internal && !boundary &&
2405 IsBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2406 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2407 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2408 LyXParagraph * par = cursor.par()->previous();
2409 SetCursor(bview, par, par->size());
2414 void LyXText::CursorRight(BufferView * bview, bool internal) const
2416 if (!internal && cursor.boundary() &&
2417 !cursor.par()->IsNewline(cursor.pos()))
2418 SetCursor(bview, cursor.par(), cursor.pos(), true, false);
2419 else if (cursor.pos() < cursor.par()->size()) {
2420 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2422 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2423 SetCursor(bview, cursor.par(), cursor.pos(), true, true);
2424 } else if (cursor.par()->next())
2425 SetCursor(bview, cursor.par()->next(), 0);
2429 void LyXText::CursorUp(BufferView * bview) const
2431 SetCursorFromCoordinates(bview, cursor.x_fix(),
2432 cursor.y() - cursor.row()->baseline() - 1);
2436 void LyXText::CursorDown(BufferView * bview) const
2438 SetCursorFromCoordinates(bview, cursor.x_fix(),
2439 cursor.y() - cursor.row()->baseline()
2440 + cursor.row()->height() + 1);
2444 void LyXText::CursorUpParagraph(BufferView * bview) const
2446 if (cursor.pos() > 0) {
2447 SetCursor(bview, cursor.par(), 0);
2449 else if (cursor.par()->previous()) {
2450 SetCursor(bview, cursor.par()->previous(), 0);
2455 void LyXText::CursorDownParagraph(BufferView * bview) const
2457 if (cursor.par()->next()) {
2458 SetCursor(bview, cursor.par()->next(), 0);
2460 SetCursor(bview, cursor.par(), cursor.par()->size());
2465 void LyXText::DeleteEmptyParagraphMechanism(BufferView * bview,
2466 LyXCursor const & old_cursor) const
2468 // Would be wrong to delete anything if we have a selection.
2469 if (selection.set()) return;
2471 // We allow all kinds of "mumbo-jumbo" when freespacing.
2472 if (textclasslist.Style(bview->buffer()->params.textclass,
2473 old_cursor.par()->GetLayout()).free_spacing)
2476 bool deleted = false;
2478 /* Ok I'll put some comments here about what is missing.
2479 I have fixed BackSpace (and thus Delete) to not delete
2480 double-spaces automagically. I have also changed Cut,
2481 Copy and Paste to hopefully do some sensible things.
2482 There are still some small problems that can lead to
2483 double spaces stored in the document file or space at
2484 the beginning of paragraphs. This happens if you have
2485 the cursor betwenn to spaces and then save. Or if you
2486 cut and paste and the selection have a space at the
2487 beginning and then save right after the paste. I am
2488 sure none of these are very hard to fix, but I will
2489 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2490 that I can get some feedback. (Lgb)
2493 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2494 // delete the LineSeparator.
2497 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2498 // delete the LineSeparator.
2501 // If the pos around the old_cursor were spaces, delete one of them.
2502 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2503 // Only if the cursor has really moved
2505 if (old_cursor.pos() > 0
2506 && old_cursor.pos() < old_cursor.par()->size()
2507 && old_cursor.par()->IsLineSeparator(old_cursor.pos())
2508 && old_cursor.par()->IsLineSeparator(old_cursor.pos() - 1)) {
2509 old_cursor.par()->Erase(old_cursor.pos() - 1);
2510 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2512 if (old_cursor.par() == cursor.par() &&
2513 cursor.pos() > old_cursor.pos()) {
2514 SetCursorIntern(bview, cursor.par(),
2517 SetCursorIntern(bview, cursor.par(),
2523 // Do not delete empty paragraphs with keepempty set.
2524 if ((textclasslist.Style(bview->buffer()->params.textclass,
2525 old_cursor.par()->GetLayout())).keepempty)
2528 LyXCursor tmpcursor;
2530 if (old_cursor.par() != cursor.par()) {
2531 if ((old_cursor.par()->size() == 0
2532 || (old_cursor.par()->size() == 1
2533 && old_cursor.par()->IsLineSeparator(0)))) {
2534 // ok, we will delete anything
2536 // make sure that you do not delete any environments
2537 status = LyXText::NEED_MORE_REFRESH;
2540 if (old_cursor.row()->previous()) {
2541 refresh_row = old_cursor.row()->previous();
2542 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2544 cursor = old_cursor; // that undo can restore the right cursor position
2545 LyXParagraph * endpar = old_cursor.par()->next();
2546 if (endpar && endpar->GetDepth()) {
2547 while (endpar && endpar->GetDepth()) {
2548 endpar = endpar->next();
2551 SetUndo(bview->buffer(), Undo::DELETE,
2552 old_cursor.par()->previous(),
2557 RemoveRow(old_cursor.row());
2558 if (OwnerParagraph() == old_cursor.par()) {
2559 OwnerParagraph(OwnerParagraph()->next());
2562 delete old_cursor.par();
2564 /* Breakagain the next par. Needed
2565 * because of the parindent that
2566 * can occur or dissappear. The
2567 * next row can change its height,
2568 * if there is another layout before */
2569 if (refresh_row->next()) {
2570 BreakAgain(bview, refresh_row->next());
2571 UpdateCounters(bview, refresh_row);
2573 SetHeightOfRow(bview, refresh_row);
2575 refresh_row = old_cursor.row()->next();
2576 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2579 cursor = old_cursor; // that undo can restore the right cursor position
2580 LyXParagraph * endpar = old_cursor.par()->next();
2581 if (endpar && endpar->GetDepth()) {
2582 while (endpar && endpar->GetDepth()) {
2583 endpar = endpar->next();
2586 SetUndo(bview->buffer(), Undo::DELETE,
2587 old_cursor.par()->previous(),
2592 RemoveRow(old_cursor.row());
2594 if (OwnerParagraph() == old_cursor.par()) {
2595 OwnerParagraph(OwnerParagraph()->next());
2598 delete old_cursor.par();
2600 /* Breakagain the next par. Needed
2601 because of the parindent that can
2602 occur or dissappear.
2603 The next row can change its height,
2604 if there is another layout before
2607 BreakAgain(bview, refresh_row);
2608 UpdateCounters(bview, refresh_row->previous());
2614 SetCursorIntern(bview, cursor.par(), cursor.pos());
2616 if (selection.cursor.par() == old_cursor.par()
2617 && selection.cursor.pos() == selection.cursor.pos()) {
2618 // correct selection
2619 selection.cursor = cursor;
2623 if (old_cursor.par()->StripLeadingSpaces(bview->buffer()->params.textclass)) {
2624 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2626 SetCursorIntern(bview, cursor.par(), cursor.pos());
2627 selection.cursor = cursor;
2634 LyXParagraph * LyXText::GetParFromID(int id)
2636 LyXParagraph * result = FirstParagraph();
2637 while (result && result->id() != id)
2638 result = result->next();
2644 bool LyXText::TextUndo(BufferView * bview)
2648 // returns false if no undo possible
2649 Undo * undo = bview->buffer()->undostack.pop();
2653 bview->buffer()->redostack
2654 .push(CreateUndo(bview->buffer(), undo->kind,
2655 GetParFromID(undo->number_of_before_par),
2656 GetParFromID(undo->number_of_behind_par)));
2658 return TextHandleUndo(bview, undo);
2662 bool LyXText::TextRedo(BufferView * bview)
2666 // returns false if no redo possible
2667 Undo * undo = bview->buffer()->redostack.pop();
2671 bview->buffer()->undostack
2672 .push(CreateUndo(bview->buffer(), undo->kind,
2673 GetParFromID(undo->number_of_before_par),
2674 GetParFromID(undo->number_of_behind_par)));
2676 return TextHandleUndo(bview, undo);
2680 bool LyXText::TextHandleUndo(BufferView * bview, Undo * undo)
2684 // returns false if no undo possible
2685 bool result = false;
2687 LyXParagraph * before =
2688 GetParFromID(undo->number_of_before_par);
2689 LyXParagraph * behind =
2690 GetParFromID(undo->number_of_behind_par);
2691 LyXParagraph * tmppar;
2692 LyXParagraph * tmppar2;
2693 LyXParagraph * endpar;
2694 LyXParagraph * tmppar5;
2696 // if there's no before take the beginning
2697 // of the document for redoing
2699 SetCursorIntern(bview, FirstParagraph(), 0);
2701 // replace the paragraphs with the undo informations
2703 LyXParagraph * tmppar3 = undo->par;
2704 undo->par = 0; // otherwise the undo destructor would delete the paragraph
2705 LyXParagraph * tmppar4 = tmppar3;
2708 while (tmppar4->next())
2709 tmppar4 = tmppar4->next();
2710 } // get last undo par
2712 // now remove the old text if there is any
2713 if (before != behind || (!behind && !before)) {
2715 tmppar5 = before->next();
2717 tmppar5 = OwnerParagraph();
2719 while (tmppar5 && tmppar5 != behind) {
2721 tmppar5 = tmppar5->next();
2722 // a memory optimization for edit: Only layout information
2723 // is stored in the undo. So restore the text informations.
2724 if (undo->kind == Undo::EDIT) {
2725 tmppar2->setContentsFromPar(tmppar);
2726 tmppar->clearContents();
2727 tmppar2 = tmppar2->next();
2732 // put the new stuff in the list if there is one
2735 before->next(tmppar3);
2737 OwnerParagraph(tmppar3);
2738 tmppar3->previous(before);
2741 OwnerParagraph(behind);
2744 tmppar4->next(behind);
2746 behind->previous(tmppar4);
2750 // Set the cursor for redoing
2752 SetCursorIntern(bview, before, 0);
2755 // calculate the endpar for redoing the paragraphs.
2757 endpar = behind->next();
2761 tmppar = GetParFromID(undo->number_of_cursor_par);
2762 RedoParagraphs(bview, cursor, endpar);
2764 SetCursorIntern(bview, tmppar, undo->cursor_pos);
2765 UpdateCounters(bview, cursor.row());
2775 void LyXText::FinishUndo()
2779 // makes sure the next operation will be stored
2780 undo_finished = true;
2784 void LyXText::FreezeUndo()
2788 // this is dangerous and for internal use only
2793 void LyXText::UnFreezeUndo()
2797 // this is dangerous and for internal use only
2798 undo_frozen = false;
2802 void LyXText::SetUndo(Buffer * buf, Undo::undo_kind kind,
2803 LyXParagraph const * before,
2804 LyXParagraph const * behind) const
2809 buf->undostack.push(CreateUndo(buf, kind, before, behind));
2810 buf->redostack.clear();
2814 void LyXText::SetRedo(Buffer * buf, Undo::undo_kind kind,
2815 LyXParagraph const * before, LyXParagraph const * behind)
2819 buf->redostack.push(CreateUndo(buf, kind, before, behind));
2823 Undo * LyXText::CreateUndo(Buffer * buf, Undo::undo_kind kind,
2824 LyXParagraph const * before,
2825 LyXParagraph const * behind) const
2830 int before_number = -1;
2831 int behind_number = -1;
2833 before_number = before->id();
2835 behind_number = behind->id();
2836 // Undo::EDIT and Undo::FINISH are
2837 // always finished. (no overlapping there)
2838 // overlapping only with insert and delete inside one paragraph:
2839 // Nobody wants all removed character
2840 // appear one by one when undoing.
2841 // EDIT is special since only layout information, not the
2842 // contents of a paragaph are stored.
2843 if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)){
2844 // check wether storing is needed
2845 if (!buf->undostack.empty() &&
2846 buf->undostack.top()->kind == kind &&
2847 buf->undostack.top()->number_of_before_par == before_number &&
2848 buf->undostack.top()->number_of_behind_par == behind_number ){
2853 // create a new Undo
2854 LyXParagraph * undopar;
2856 LyXParagraph * start = 0;
2857 LyXParagraph * end = 0;
2860 start = const_cast<LyXParagraph*>(before->next());
2862 start = FirstParagraph();
2864 end = const_cast<LyXParagraph*>(behind->previous());
2866 end = FirstParagraph();
2870 if (start && end && (start != end->next()) &&
2871 ((before != behind) || (!before && !behind))) {
2872 LyXParagraph * tmppar = start;
2873 LyXParagraph * tmppar2 = new LyXParagraph(*tmppar);
2874 tmppar2->id(tmppar->id());
2876 // a memory optimization: Just store the layout information
2878 if (kind == Undo::EDIT){
2879 //tmppar2->text.clear();
2880 tmppar2->clearContents();
2885 while (tmppar != end && tmppar->next()) {
2886 tmppar = tmppar->next();
2887 tmppar2->next(new LyXParagraph(*tmppar));
2888 tmppar2->next()->id(tmppar->id());
2889 // a memory optimization: Just store the layout
2890 // information when only edit
2891 if (kind == Undo::EDIT){
2892 //tmppar2->next->text.clear();
2893 tmppar2->clearContents();
2895 tmppar2->next()->previous(tmppar2);
2896 tmppar2 = tmppar2->next();
2900 undopar = 0; // nothing to replace (undo of delete maybe)
2902 int cursor_par = cursor.par()->id();
2903 int cursor_pos = cursor.pos();
2905 Undo * undo = new Undo(kind,
2906 before_number, behind_number,
2907 cursor_par, cursor_pos,
2910 undo_finished = false;
2915 void LyXText::SetCursorParUndo(Buffer * buf)
2919 SetUndo(buf, Undo::FINISH,
2920 cursor.par()->previous(),
2921 cursor.par()->next());
2925 void LyXText::toggleAppendix(BufferView * bview)
2927 LyXParagraph * par = cursor.par();
2928 bool start = !par->params.startOfAppendix();
2930 // ensure that we have only one start_of_appendix in this document
2931 LyXParagraph * tmp = FirstParagraph();
2932 for (; tmp; tmp = tmp->next())
2933 tmp->params.startOfAppendix(false);
2935 par->params.startOfAppendix(start);
2937 // we can set the refreshing parameters now
2938 status = LyXText::NEED_MORE_REFRESH;
2940 refresh_row = 0; // not needed for full update
2941 UpdateCounters(bview, 0);
2942 SetCursor(bview, cursor.par(), cursor.pos());
2946 LyXParagraph * LyXText::OwnerParagraph() const
2949 return inset_owner->par;
2951 return bv_owner->buffer()->paragraph;
2955 LyXParagraph * LyXText::OwnerParagraph(LyXParagraph * p) const
2958 inset_owner->par = p;
2960 bv_owner->buffer()->paragraph = p;