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));
1678 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1680 = floatList.getType(tmp->type());
1681 // We should get the correct number here too.
1682 s = fl.name() + " #:";
1684 /* par->SetLayout(0);
1685 s = layout->labelstring; */
1686 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1687 ? " :úåòîùî øñç" : "Senseless: ";
1690 par->params.labelString(s);
1692 /* reset the enumeration counter. They are always resetted
1693 * when there is any other layout between */
1694 for (int i = 6 + par->enumdepth; i < 10; ++i)
1695 par->setCounter(i, 0);
1700 /* Updates all counters BEHIND the row. Changed paragraphs
1701 * with a dynamic left margin will be rebroken. */
1702 void LyXText::UpdateCounters(BufferView * bview, Row * row) const
1710 par = row->par()->next();
1714 while (row->par() != par)
1717 SetCounter(bview->buffer(), par);
1719 /* now check for the headline layouts. remember that they
1720 * have a dynamic left margin */
1721 if ((textclasslist.Style(bview->buffer()->params.textclass,
1722 par->layout).margintype == MARGIN_DYNAMIC
1723 || textclasslist.Style(bview->buffer()->params.textclass,
1724 par->layout).labeltype == LABEL_SENSITIVE)) {
1726 /* Rebreak the paragraph */
1727 RemoveParagraph(row);
1728 AppendParagraph(bview, row);
1735 /* insets an inset. */
1736 void LyXText::InsertInset(BufferView * bview, Inset * inset)
1738 if (!cursor.par()->InsertInsetAllowed(inset))
1740 SetUndo(bview->buffer(), Undo::INSERT,
1741 cursor.par()->previous(),
1742 cursor.par()->next());
1743 cursor.par()->InsertInset(cursor.pos(), inset);
1744 InsertChar(bview, LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1745 * The character will not be inserted a
1748 // If we enter a highly editable inset the cursor should be to before
1749 // the inset. This couldn't happen before as Undo was not handled inside
1750 // inset now after the Undo LyX tries to call inset->Edit(...) again
1751 // and cannot do this as the cursor is behind the inset and GetInset
1752 // does not return the inset!
1753 if (inset->Editable() == Inset::HIGHLY_EDITABLE) {
1754 CursorLeft(bview, true);
1760 void LyXText::copyEnvironmentType()
1762 copylayouttype = cursor.par()->GetLayout();
1766 void LyXText::pasteEnvironmentType(BufferView * bview)
1768 SetLayout(bview, copylayouttype);
1772 void LyXText::CutSelection(BufferView * bview, bool doclear)
1774 // Stuff what we got on the clipboard. Even if there is no selection.
1776 // There is a problem with having the stuffing here in that the
1777 // larger the selection the slower LyX will get. This can be
1778 // solved by running the line below only when the selection has
1779 // finished. The solution used currently just works, to make it
1780 // faster we need to be more clever and probably also have more
1781 // calls to stuffClipboard. (Lgb)
1782 bview->stuffClipboard(selectionAsString(bview->buffer()));
1784 // This doesn't make sense, if there is no selection
1785 if (!selection.set())
1788 // OK, we have a selection. This is always between selection.start
1789 // and selection.end
1791 // make sure that the depth behind the selection are restored, too
1792 LyXParagraph * endpar = selection.end.par()->next();
1793 LyXParagraph * undoendpar = endpar;
1795 if (endpar && endpar->GetDepth()) {
1796 while (endpar && endpar->GetDepth()) {
1797 endpar = endpar->next();
1798 undoendpar = endpar;
1800 } else if (endpar) {
1801 endpar = endpar->next(); // because of parindents etc.
1804 SetUndo(bview->buffer(), Undo::DELETE,
1805 selection.start.par()->previous(),
1810 // there are two cases: cut only within one paragraph or
1811 // more than one paragraph
1812 if (selection.start.par() == selection.end.par()) {
1813 // only within one paragraph
1814 endpar = selection.end.par();
1815 int pos = selection.end.pos();
1816 cap.cutSelection(selection.start.par(), &endpar,
1817 selection.start.pos(), pos,
1818 bview->buffer()->params.textclass, doclear);
1819 selection.end.pos(pos);
1821 endpar = selection.end.par();
1822 int pos = selection.end.pos();
1823 cap.cutSelection(selection.start.par(), &endpar,
1824 selection.start.pos(), pos,
1825 bview->buffer()->params.textclass, doclear);
1827 selection.end.par(endpar);
1828 selection.end.pos(pos);
1829 cursor.pos(selection.end.pos());
1831 endpar = endpar->next();
1833 // sometimes necessary
1835 selection.start.par()->StripLeadingSpaces(bview->buffer()->params.textclass);
1837 RedoParagraphs(bview, selection.start, endpar);
1839 // cutSelection can invalidate the cursor so we need to set
1841 cursor = selection.start;
1843 // need a valid cursor. (Lgb)
1844 ClearSelection(bview);
1846 SetCursor(bview, cursor.par(), cursor.pos());
1847 selection.cursor = cursor;
1848 UpdateCounters(bview, cursor.row());
1852 void LyXText::CopySelection(BufferView * bview)
1854 // Stuff what we got on the clipboard. Even if there is no selection.
1856 // There is a problem with having the stuffing here in that the
1857 // larger the selection the slower LyX will get. This can be
1858 // solved by running the line below only when the selection has
1859 // finished. The solution used currently just works, to make it
1860 // faster we need to be more clever and probably also have more
1861 // calls to stuffClipboard. (Lgb)
1862 bview->stuffClipboard(selectionAsString(bview->buffer()));
1864 // this doesnt make sense, if there is no selection
1865 if (!selection.set())
1868 // ok we have a selection. This is always between selection.start
1869 // and sel_end cursor
1871 // copy behind a space if there is one
1872 while (selection.start.par()->size() > selection.start.pos()
1873 && selection.start.par()->IsLineSeparator(selection.start.pos())
1874 && (selection.start.par() != selection.end.par()
1875 || selection.start.pos() < selection.end.pos()))
1876 selection.start.pos(selection.start.pos() + 1);
1880 cap.copySelection(selection.start.par(), selection.end.par(),
1881 selection.start.pos(), selection.end.pos(),
1882 bview->buffer()->params.textclass);
1886 void LyXText::PasteSelection(BufferView * bview)
1890 // this does not make sense, if there is nothing to paste
1891 if (!cap.checkPastePossible(cursor.par()))
1894 SetUndo(bview->buffer(), Undo::INSERT,
1895 cursor.par()->previous(),
1896 cursor.par()->next());
1898 LyXParagraph * endpar;
1899 LyXParagraph * actpar = cursor.par();
1901 int pos = cursor.pos();
1902 cap.pasteSelection(&actpar, &endpar, pos,
1903 bview->buffer()->params.textclass);
1905 RedoParagraphs(bview, cursor, endpar);
1907 SetCursor(bview, cursor.par(), cursor.pos());
1908 ClearSelection(bview);
1910 selection.cursor = cursor;
1911 SetCursor(bview, actpar, pos);
1912 SetSelection(bview);
1913 UpdateCounters(bview, cursor.row());
1917 // returns a pointer to the very first LyXParagraph
1918 LyXParagraph * LyXText::FirstParagraph() const
1920 return OwnerParagraph();
1924 // sets the selection over the number of characters of string, no check!!
1925 void LyXText::SetSelectionOverString(BufferView * bview, string const & str)
1927 selection.cursor = cursor;
1928 for (int i = 0; str[i]; ++i)
1930 SetSelection(bview);
1934 // simple replacing. The font of the first selected character is used
1935 void LyXText::ReplaceSelectionWithString(BufferView * bview,
1938 SetCursorParUndo(bview->buffer());
1941 if (!selection.set()) { // create a dummy selection
1942 selection.end = cursor;
1943 selection.start = cursor;
1946 // Get font setting before we cut
1947 LyXParagraph::size_type pos = selection.end.pos();
1948 LyXFont const font = selection.start.par()
1949 ->GetFontSettings(bview->buffer()->params,
1950 selection.start.pos());
1952 // Insert the new string
1953 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1954 selection.end.par()->InsertChar(pos, (*cit), font);
1958 // Cut the selection
1959 CutSelection(bview);
1965 // needed to insert the selection
1966 void LyXText::InsertStringA(BufferView * bview, string const & str)
1968 LyXParagraph * par = cursor.par();
1969 LyXParagraph::size_type pos = cursor.pos();
1970 LyXParagraph::size_type a = 0;
1971 LyXParagraph * endpar = cursor.par()->next();
1973 SetCursorParUndo(bview->buffer());
1976 textclasslist.Style(bview->buffer()->params.textclass,
1977 cursor.par()->GetLayout()).isEnvironment();
1978 // only to be sure, should not be neccessary
1979 ClearSelection(bview);
1981 // insert the string, don't insert doublespace
1982 string::size_type i = 0;
1983 while (i < str.length()) {
1984 if (str[i] != '\n') {
1986 && i + 1 < str.length() && str[i + 1] != ' '
1987 && pos && par->GetChar(pos - 1)!= ' ') {
1988 par->InsertChar(pos, ' ', current_font);
1990 } else if (str[i] == ' ') {
1991 InsetSpecialChar * new_inset =
1992 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
1993 if (par->InsertInsetAllowed(new_inset)) {
1994 par->InsertInset(pos, new_inset,
2000 } else if (str[i] == '\t') {
2001 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2002 InsetSpecialChar * new_inset =
2003 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2004 if (par->InsertInsetAllowed(new_inset)) {
2005 par->InsertInset(pos, new_inset,
2012 } else if (str[i] != 13 &&
2013 // Ignore unprintables
2014 (str[i] & 127) >= ' ') {
2015 par->InsertChar(pos, str[i], current_font);
2019 if (!par->size()) { // par is empty
2020 InsetSpecialChar * new_inset =
2021 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2022 if (par->InsertInsetAllowed(new_inset)) {
2023 par->InsertInset(pos,
2031 par->BreakParagraph(bview->buffer()->params, pos, flag);
2038 RedoParagraphs(bview, cursor, endpar);
2039 SetCursor(bview, cursor.par(), cursor.pos());
2040 selection.cursor = cursor;
2041 SetCursor(bview, par, pos);
2042 SetSelection(bview);
2046 /* turns double-CR to single CR, others where converted into one blank and 13s
2047 * that are ignored .Double spaces are also converted into one. Spaces at
2048 * the beginning of a paragraph are forbidden. tabs are converted into one
2049 * space. then InsertStringA is called */
2050 void LyXText::InsertStringB(BufferView * bview, string const & s)
2053 string::size_type i = 1;
2054 while (i < str.length()) {
2057 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2059 if (str[i] == '\n' && i + 1 < str.length()) {
2060 if (str[i + 1] != '\n') {
2061 if (str[i - 1] != ' ')
2066 while (i + 1 < str.length()
2067 && (str[i + 1] == ' '
2068 || str[i + 1] == '\t'
2069 || str[i + 1] == '\n'
2070 || str[i + 1] == 13)) {
2077 InsertStringA(bview, str);
2081 bool LyXText::GotoNextInset(BufferView * bview,
2082 std::vector<Inset::Code> const & codes,
2083 string const & contents) const
2085 LyXCursor res = cursor;
2088 if (res.pos() < res.par()->size() - 1) {
2089 res.pos(res.pos() + 1);
2091 res.par(res.par()->next());
2095 } while (res.par() &&
2096 !(res.par()->GetChar(res.pos()) == LyXParagraph::META_INSET
2097 && (inset = res.par()->GetInset(res.pos())) != 0
2098 && find(codes.begin(), codes.end(), inset->LyxCode())
2100 && (contents.empty() ||
2101 static_cast<InsetCommand *>(res.par()->GetInset(res.pos()))->getContents()
2105 SetCursor(bview, res.par(), res.pos());
2112 void LyXText::CheckParagraph(BufferView * bview, LyXParagraph * par,
2113 LyXParagraph::size_type pos)
2115 LyXCursor tmpcursor;
2118 LyXParagraph::size_type z;
2119 Row * row = GetRow(par, pos, y);
2121 // is there a break one row above
2122 if (row->previous() && row->previous()->par() == row->par()) {
2123 z = NextBreakPoint(bview, row->previous(), workWidth(bview));
2124 if (z >= row->pos()) {
2125 // set the dimensions of the row above
2126 y -= row->previous()->height();
2128 refresh_row = row->previous();
2129 status = LyXText::NEED_MORE_REFRESH;
2131 BreakAgain(bview, row->previous());
2133 // set the cursor again. Otherwise
2134 // dangling pointers are possible
2135 SetCursor(bview, cursor.par(), cursor.pos(),
2136 false, cursor.boundary());
2137 selection.cursor = cursor;
2142 int const tmpheight = row->height();
2143 LyXParagraph::size_type const tmplast = RowLast(row);
2147 BreakAgain(bview, row);
2148 if (row->height() == tmpheight && RowLast(row) == tmplast)
2149 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2151 status = LyXText::NEED_MORE_REFRESH;
2153 // check the special right address boxes
2154 if (textclasslist.Style(bview->buffer()->params.textclass,
2155 par->GetLayout()).margintype
2156 == MARGIN_RIGHT_ADDRESS_BOX) {
2163 RedoDrawingOfParagraph(bview, tmpcursor);
2166 // set the cursor again. Otherwise dangling pointers are possible
2167 // also set the selection
2169 if (selection.set()) {
2171 SetCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2172 false, selection.cursor.boundary());
2173 selection.cursor = cursor;
2174 SetCursorIntern(bview, selection.start.par(),
2175 selection.start.pos(),
2176 false, selection.start.boundary());
2177 selection.start = cursor;
2178 SetCursorIntern(bview, selection.end.par(),
2179 selection.end.pos(),
2180 false, selection.end.boundary());
2181 selection.end = cursor;
2182 SetCursorIntern(bview, last_sel_cursor.par(),
2183 last_sel_cursor.pos(),
2184 false, last_sel_cursor.boundary());
2185 last_sel_cursor = cursor;
2188 SetCursorIntern(bview, cursor.par(), cursor.pos(),
2189 false, cursor.boundary());
2193 // returns false if inset wasn't found
2194 bool LyXText::UpdateInset(BufferView * bview, Inset * inset)
2196 // first check the current paragraph
2197 int pos = cursor.par()->GetPositionOfInset(inset);
2199 CheckParagraph(bview, cursor.par(), pos);
2203 // check every paragraph
2205 LyXParagraph * par = FirstParagraph();
2207 pos = par->GetPositionOfInset(inset);
2209 CheckParagraph(bview, par, pos);
2219 void LyXText::SetCursor(BufferView * bview, LyXParagraph * par,
2220 LyXParagraph::size_type pos,
2221 bool setfont, bool boundary) const
2223 LyXCursor old_cursor = cursor;
2224 SetCursorIntern(bview, par, pos, setfont, boundary);
2225 DeleteEmptyParagraphMechanism(bview, old_cursor);
2229 void LyXText::SetCursor(BufferView *bview, LyXCursor & cur, LyXParagraph * par,
2230 LyXParagraph::size_type pos, bool boundary) const
2234 cur.boundary(boundary);
2236 /* get the cursor y position in text */
2238 Row * row = GetRow(par, pos, y);
2239 /* y is now the beginning of the cursor row */
2240 y += row->baseline();
2241 /* y is now the cursor baseline */
2244 /* now get the cursors x position */
2246 float fill_separator, fill_hfill, fill_label_hfill;
2247 PrepareToPrint(bview, row, x, fill_separator, fill_hfill,
2249 LyXParagraph::size_type cursor_vpos = 0;
2250 LyXParagraph::size_type last = RowLastPrintable(row);
2252 if (pos > last + 1) // This shouldn't happen.
2254 else if (pos < row->pos())
2257 if (last < row->pos())
2258 cursor_vpos = row->pos();
2259 else if (pos > last && !boundary)
2260 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2261 ? row->pos() : last + 1;
2262 else if (pos > row->pos() &&
2263 (pos > last || boundary))
2264 /// Place cursor after char at (logical) position pos - 1
2265 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2266 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2268 /// Place cursor before char at (logical) position pos
2269 cursor_vpos = (bidi_level(pos) % 2 == 0)
2270 ? log2vis(pos) : log2vis(pos) + 1;
2272 LyXParagraph::size_type main_body =
2273 BeginningOfMainBody(bview->buffer(), row->par());
2274 if ((main_body > 0) &&
2275 ((main_body-1 > last) ||
2276 !row->par()->IsLineSeparator(main_body-1)))
2279 for (LyXParagraph::size_type vpos = row->pos();
2280 vpos < cursor_vpos; ++vpos) {
2281 pos = vis2log(vpos);
2282 if (main_body > 0 && pos == main_body - 1) {
2283 x += fill_label_hfill +
2284 lyxfont::width(textclasslist.Style(
2285 bview->buffer()->params.textclass,
2286 row->par()->GetLayout())
2288 GetFont(bview->buffer(), row->par(), -2));
2289 if (row->par()->IsLineSeparator(main_body-1))
2290 x -= SingleWidth(bview, row->par(),main_body-1);
2292 if (HfillExpansion(bview->buffer(), row, pos)) {
2293 x += SingleWidth(bview, row->par(), pos);
2294 if (pos >= main_body)
2297 x += fill_label_hfill;
2298 } else if (row->par()->IsSeparator(pos)) {
2299 x += SingleWidth(bview, row->par(), pos);
2300 if (pos >= main_body)
2301 x += fill_separator;
2303 x += SingleWidth(bview, row->par(), pos);
2312 void LyXText::SetCursorIntern(BufferView * bview, LyXParagraph * par,
2313 LyXParagraph::size_type pos,
2314 bool setfont, bool boundary) const
2316 SetCursor(bview, cursor, par, pos, boundary);
2318 SetCurrentFont(bview);
2322 void LyXText::SetCurrentFont(BufferView * bview) const
2324 LyXParagraph::size_type pos = cursor.pos();
2325 if (cursor.boundary() && pos > 0)
2329 if (pos == cursor.par()->size())
2331 else // potentional bug... BUG (Lgb)
2332 if (cursor.par()->IsSeparator(pos)) {
2333 if (pos > cursor.row()->pos() &&
2334 bidi_level(pos) % 2 ==
2335 bidi_level(pos - 1) % 2)
2337 else if (pos + 1 < cursor.par()->size())
2343 cursor.par()->GetFontSettings(bview->buffer()->params, pos);
2344 real_current_font = GetFont(bview->buffer(), cursor.par(), pos);
2346 if (cursor.pos() == cursor.par()->size() &&
2347 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2348 !cursor.boundary()) {
2349 Language const * lang =
2350 cursor.par()->getParLanguage(bview->buffer()->params);
2351 current_font.setLanguage(lang);
2352 current_font.setNumber(LyXFont::OFF);
2353 real_current_font.setLanguage(lang);
2354 real_current_font.setNumber(LyXFont::OFF);
2359 void LyXText::SetCursorFromCoordinates(BufferView * bview, int x, int y) const
2361 LyXCursor old_cursor = cursor;
2363 /* get the row first */
2365 Row * row = GetRowNearY(y);
2366 cursor.par(row->par());
2369 int column = GetColumnNearX(bview, row, x, bound);
2370 cursor.pos(row->pos() + column);
2372 cursor.y(y + row->baseline());
2374 cursor.boundary(bound);
2375 SetCurrentFont(bview);
2376 DeleteEmptyParagraphMechanism(bview, old_cursor);
2380 void LyXText::SetCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2383 /* get the row first */
2385 Row * row = GetRowNearY(y);
2387 int column = GetColumnNearX(bview, row, x, bound);
2389 cur.par(row->par());
2390 cur.pos(row->pos() + column);
2392 cur.y(y + row->baseline());
2394 cur.boundary(bound);
2398 void LyXText::CursorLeft(BufferView * bview, bool internal) const
2400 if (cursor.pos() > 0) {
2401 bool boundary = cursor.boundary();
2402 SetCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2403 if (!internal && !boundary &&
2404 IsBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2405 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2406 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2407 LyXParagraph * par = cursor.par()->previous();
2408 SetCursor(bview, par, par->size());
2413 void LyXText::CursorRight(BufferView * bview, bool internal) const
2415 if (!internal && cursor.boundary() &&
2416 !cursor.par()->IsNewline(cursor.pos()))
2417 SetCursor(bview, cursor.par(), cursor.pos(), true, false);
2418 else if (cursor.pos() < cursor.par()->size()) {
2419 SetCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2421 IsBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2422 SetCursor(bview, cursor.par(), cursor.pos(), true, true);
2423 } else if (cursor.par()->next())
2424 SetCursor(bview, cursor.par()->next(), 0);
2428 void LyXText::CursorUp(BufferView * bview) const
2430 SetCursorFromCoordinates(bview, cursor.x_fix(),
2431 cursor.y() - cursor.row()->baseline() - 1);
2435 void LyXText::CursorDown(BufferView * bview) const
2437 SetCursorFromCoordinates(bview, cursor.x_fix(),
2438 cursor.y() - cursor.row()->baseline()
2439 + cursor.row()->height() + 1);
2443 void LyXText::CursorUpParagraph(BufferView * bview) const
2445 if (cursor.pos() > 0) {
2446 SetCursor(bview, cursor.par(), 0);
2448 else if (cursor.par()->previous()) {
2449 SetCursor(bview, cursor.par()->previous(), 0);
2454 void LyXText::CursorDownParagraph(BufferView * bview) const
2456 if (cursor.par()->next()) {
2457 SetCursor(bview, cursor.par()->next(), 0);
2459 SetCursor(bview, cursor.par(), cursor.par()->size());
2464 void LyXText::DeleteEmptyParagraphMechanism(BufferView * bview,
2465 LyXCursor const & old_cursor) const
2467 // Would be wrong to delete anything if we have a selection.
2468 if (selection.set()) return;
2470 // We allow all kinds of "mumbo-jumbo" when freespacing.
2471 if (textclasslist.Style(bview->buffer()->params.textclass,
2472 old_cursor.par()->GetLayout()).free_spacing)
2475 bool deleted = false;
2477 /* Ok I'll put some comments here about what is missing.
2478 I have fixed BackSpace (and thus Delete) to not delete
2479 double-spaces automagically. I have also changed Cut,
2480 Copy and Paste to hopefully do some sensible things.
2481 There are still some small problems that can lead to
2482 double spaces stored in the document file or space at
2483 the beginning of paragraphs. This happens if you have
2484 the cursor betwenn to spaces and then save. Or if you
2485 cut and paste and the selection have a space at the
2486 beginning and then save right after the paste. I am
2487 sure none of these are very hard to fix, but I will
2488 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2489 that I can get some feedback. (Lgb)
2492 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2493 // delete the LineSeparator.
2496 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2497 // delete the LineSeparator.
2500 // If the pos around the old_cursor were spaces, delete one of them.
2501 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2502 // Only if the cursor has really moved
2504 if (old_cursor.pos() > 0
2505 && old_cursor.pos() < old_cursor.par()->size()
2506 && old_cursor.par()->IsLineSeparator(old_cursor.pos())
2507 && old_cursor.par()->IsLineSeparator(old_cursor.pos() - 1)) {
2508 old_cursor.par()->Erase(old_cursor.pos() - 1);
2509 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2511 if (old_cursor.par() == cursor.par() &&
2512 cursor.pos() > old_cursor.pos()) {
2513 SetCursorIntern(bview, cursor.par(),
2516 SetCursorIntern(bview, cursor.par(),
2522 // Do not delete empty paragraphs with keepempty set.
2523 if ((textclasslist.Style(bview->buffer()->params.textclass,
2524 old_cursor.par()->GetLayout())).keepempty)
2527 LyXCursor tmpcursor;
2529 if (old_cursor.par() != cursor.par()) {
2530 if ((old_cursor.par()->size() == 0
2531 || (old_cursor.par()->size() == 1
2532 && old_cursor.par()->IsLineSeparator(0)))) {
2533 // ok, we will delete anything
2535 // make sure that you do not delete any environments
2536 status = LyXText::NEED_MORE_REFRESH;
2539 if (old_cursor.row()->previous()) {
2540 refresh_row = old_cursor.row()->previous();
2541 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2543 cursor = old_cursor; // that undo can restore the right cursor position
2544 LyXParagraph * endpar = old_cursor.par()->next();
2545 if (endpar && endpar->GetDepth()) {
2546 while (endpar && endpar->GetDepth()) {
2547 endpar = endpar->next();
2550 SetUndo(bview->buffer(), Undo::DELETE,
2551 old_cursor.par()->previous(),
2556 RemoveRow(old_cursor.row());
2557 if (OwnerParagraph() == old_cursor.par()) {
2558 OwnerParagraph(OwnerParagraph()->next());
2561 delete old_cursor.par();
2563 /* Breakagain the next par. Needed
2564 * because of the parindent that
2565 * can occur or dissappear. The
2566 * next row can change its height,
2567 * if there is another layout before */
2568 if (refresh_row->next()) {
2569 BreakAgain(bview, refresh_row->next());
2570 UpdateCounters(bview, refresh_row);
2572 SetHeightOfRow(bview, refresh_row);
2574 refresh_row = old_cursor.row()->next();
2575 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2578 cursor = old_cursor; // that undo can restore the right cursor position
2579 LyXParagraph * endpar = old_cursor.par()->next();
2580 if (endpar && endpar->GetDepth()) {
2581 while (endpar && endpar->GetDepth()) {
2582 endpar = endpar->next();
2585 SetUndo(bview->buffer(), Undo::DELETE,
2586 old_cursor.par()->previous(),
2591 RemoveRow(old_cursor.row());
2593 if (OwnerParagraph() == old_cursor.par()) {
2594 OwnerParagraph(OwnerParagraph()->next());
2597 delete old_cursor.par();
2599 /* Breakagain the next par. Needed
2600 because of the parindent that can
2601 occur or dissappear.
2602 The next row can change its height,
2603 if there is another layout before
2606 BreakAgain(bview, refresh_row);
2607 UpdateCounters(bview, refresh_row->previous());
2613 SetCursorIntern(bview, cursor.par(), cursor.pos());
2615 if (selection.cursor.par() == old_cursor.par()
2616 && selection.cursor.pos() == selection.cursor.pos()) {
2617 // correct selection
2618 selection.cursor = cursor;
2622 if (old_cursor.par()->StripLeadingSpaces(bview->buffer()->params.textclass)) {
2623 RedoParagraphs(bview, old_cursor, old_cursor.par()->next());
2625 SetCursorIntern(bview, cursor.par(), cursor.pos());
2626 selection.cursor = cursor;
2633 LyXParagraph * LyXText::GetParFromID(int id)
2635 LyXParagraph * result = FirstParagraph();
2636 while (result && result->id() != id)
2637 result = result->next();
2643 bool LyXText::TextUndo(BufferView * bview)
2647 // returns false if no undo possible
2648 Undo * undo = bview->buffer()->undostack.pop();
2652 bview->buffer()->redostack
2653 .push(CreateUndo(bview->buffer(), undo->kind,
2654 GetParFromID(undo->number_of_before_par),
2655 GetParFromID(undo->number_of_behind_par)));
2657 return TextHandleUndo(bview, undo);
2661 bool LyXText::TextRedo(BufferView * bview)
2665 // returns false if no redo possible
2666 Undo * undo = bview->buffer()->redostack.pop();
2670 bview->buffer()->undostack
2671 .push(CreateUndo(bview->buffer(), undo->kind,
2672 GetParFromID(undo->number_of_before_par),
2673 GetParFromID(undo->number_of_behind_par)));
2675 return TextHandleUndo(bview, undo);
2679 bool LyXText::TextHandleUndo(BufferView * bview, Undo * undo)
2683 // returns false if no undo possible
2684 bool result = false;
2686 LyXParagraph * before =
2687 GetParFromID(undo->number_of_before_par);
2688 LyXParagraph * behind =
2689 GetParFromID(undo->number_of_behind_par);
2690 LyXParagraph * tmppar;
2691 LyXParagraph * tmppar2;
2692 LyXParagraph * endpar;
2693 LyXParagraph * tmppar5;
2695 // if there's no before take the beginning
2696 // of the document for redoing
2698 SetCursorIntern(bview, FirstParagraph(), 0);
2700 // replace the paragraphs with the undo informations
2702 LyXParagraph * tmppar3 = undo->par;
2703 undo->par = 0; // otherwise the undo destructor would delete the paragraph
2704 LyXParagraph * tmppar4 = tmppar3;
2707 while (tmppar4->next())
2708 tmppar4 = tmppar4->next();
2709 } // get last undo par
2711 // now remove the old text if there is any
2712 if (before != behind || (!behind && !before)) {
2714 tmppar5 = before->next();
2716 tmppar5 = OwnerParagraph();
2718 while (tmppar5 && tmppar5 != behind) {
2720 tmppar5 = tmppar5->next();
2721 // a memory optimization for edit: Only layout information
2722 // is stored in the undo. So restore the text informations.
2723 if (undo->kind == Undo::EDIT) {
2724 tmppar2->setContentsFromPar(tmppar);
2725 tmppar->clearContents();
2726 tmppar2 = tmppar2->next();
2731 // put the new stuff in the list if there is one
2734 before->next(tmppar3);
2736 OwnerParagraph(tmppar3);
2737 tmppar3->previous(before);
2740 OwnerParagraph(behind);
2743 tmppar4->next(behind);
2745 behind->previous(tmppar4);
2749 // Set the cursor for redoing
2751 SetCursorIntern(bview, before, 0);
2754 // calculate the endpar for redoing the paragraphs.
2756 endpar = behind->next();
2760 tmppar = GetParFromID(undo->number_of_cursor_par);
2761 RedoParagraphs(bview, cursor, endpar);
2763 SetCursorIntern(bview, tmppar, undo->cursor_pos);
2764 UpdateCounters(bview, cursor.row());
2774 void LyXText::FinishUndo()
2778 // makes sure the next operation will be stored
2779 undo_finished = true;
2783 void LyXText::FreezeUndo()
2787 // this is dangerous and for internal use only
2792 void LyXText::UnFreezeUndo()
2796 // this is dangerous and for internal use only
2797 undo_frozen = false;
2801 void LyXText::SetUndo(Buffer * buf, Undo::undo_kind kind,
2802 LyXParagraph const * before,
2803 LyXParagraph const * behind) const
2808 buf->undostack.push(CreateUndo(buf, kind, before, behind));
2809 buf->redostack.clear();
2813 void LyXText::SetRedo(Buffer * buf, Undo::undo_kind kind,
2814 LyXParagraph const * before, LyXParagraph const * behind)
2818 buf->redostack.push(CreateUndo(buf, kind, before, behind));
2822 Undo * LyXText::CreateUndo(Buffer * buf, Undo::undo_kind kind,
2823 LyXParagraph const * before,
2824 LyXParagraph const * behind) const
2829 int before_number = -1;
2830 int behind_number = -1;
2832 before_number = before->id();
2834 behind_number = behind->id();
2835 // Undo::EDIT and Undo::FINISH are
2836 // always finished. (no overlapping there)
2837 // overlapping only with insert and delete inside one paragraph:
2838 // Nobody wants all removed character
2839 // appear one by one when undoing.
2840 // EDIT is special since only layout information, not the
2841 // contents of a paragaph are stored.
2842 if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)){
2843 // check wether storing is needed
2844 if (!buf->undostack.empty() &&
2845 buf->undostack.top()->kind == kind &&
2846 buf->undostack.top()->number_of_before_par == before_number &&
2847 buf->undostack.top()->number_of_behind_par == behind_number ){
2852 // create a new Undo
2853 LyXParagraph * undopar;
2855 LyXParagraph * start = 0;
2856 LyXParagraph * end = 0;
2859 start = const_cast<LyXParagraph*>(before->next());
2861 start = FirstParagraph();
2863 end = const_cast<LyXParagraph*>(behind->previous());
2865 end = FirstParagraph();
2869 if (start && end && (start != end->next()) &&
2870 ((before != behind) || (!before && !behind))) {
2871 LyXParagraph * tmppar = start;
2872 LyXParagraph * tmppar2 = new LyXParagraph(*tmppar);
2873 tmppar2->id(tmppar->id());
2875 // a memory optimization: Just store the layout information
2877 if (kind == Undo::EDIT){
2878 //tmppar2->text.clear();
2879 tmppar2->clearContents();
2884 while (tmppar != end && tmppar->next()) {
2885 tmppar = tmppar->next();
2886 tmppar2->next(new LyXParagraph(*tmppar));
2887 tmppar2->next()->id(tmppar->id());
2888 // a memory optimization: Just store the layout
2889 // information when only edit
2890 if (kind == Undo::EDIT){
2891 //tmppar2->next->text.clear();
2892 tmppar2->clearContents();
2894 tmppar2->next()->previous(tmppar2);
2895 tmppar2 = tmppar2->next();
2899 undopar = 0; // nothing to replace (undo of delete maybe)
2901 int cursor_par = cursor.par()->id();
2902 int cursor_pos = cursor.pos();
2904 Undo * undo = new Undo(kind,
2905 before_number, behind_number,
2906 cursor_par, cursor_pos,
2909 undo_finished = false;
2914 void LyXText::SetCursorParUndo(Buffer * buf)
2918 SetUndo(buf, Undo::FINISH,
2919 cursor.par()->previous(),
2920 cursor.par()->next());
2924 void LyXText::toggleAppendix(BufferView * bview)
2926 LyXParagraph * par = cursor.par();
2927 bool start = !par->params.startOfAppendix();
2929 // ensure that we have only one start_of_appendix in this document
2930 LyXParagraph * tmp = FirstParagraph();
2931 for (; tmp; tmp = tmp->next())
2932 tmp->params.startOfAppendix(false);
2934 par->params.startOfAppendix(start);
2936 // we can set the refreshing parameters now
2937 status = LyXText::NEED_MORE_REFRESH;
2939 refresh_row = 0; // not needed for full update
2940 UpdateCounters(bview, 0);
2941 SetCursor(bview, cursor.par(), cursor.pos());
2945 LyXParagraph * LyXText::OwnerParagraph() const
2948 return inset_owner->par;
2950 return bv_owner->buffer()->paragraph;
2954 LyXParagraph * LyXText::OwnerParagraph(LyXParagraph * p) const
2957 inset_owner->par = p;
2959 bv_owner->buffer()->paragraph = p;