1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include FORMS_H_LOCATION
18 #pragma implementation "lyxtext.h"
22 #include "lyxparagraph.h"
23 #include "insets/inseterror.h"
26 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
38 #define FIX_DOUBLE_SPACE 1
40 extern BufferView * current_view;
44 LyXText::LyXText(int pw, Buffer * p)
51 parameters = &p->params;
55 status = LyXText::UNCHANGED;
56 LyXParagraph * par = p->paragraph;
57 current_font = GetFont(par, 0);
62 InsertParagraph(par, lastrow);
65 // set cursor at the very top position
66 selection = true; /* these setting is necessary
67 because of the delete-empty-
68 paragraph mechanism in
70 SetCursor(firstrow->par, 0);
75 // no rebreak necessary
81 // Default layouttype for copy environment type
88 // Delete all rows, this does not touch the paragraphs!
89 Row * tmprow = firstrow;
91 tmprow = firstrow->next;
98 // Gets the fully instantiated font at a given position in a paragraph
99 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
100 // The difference is that this one is used for displaying, and thus we
101 // are allowed to make cosmetic improvements. For instance make footnotes
103 // If position is -1, we get the layout font of the paragraph.
104 // If position is -2, we get the font of the manual label of the paragraph.
105 LyXFont LyXText::GetFont(LyXParagraph * par,
106 LyXParagraph::size_type pos) const
108 LyXLayout const & layout =
109 textclasslist.Style(parameters->textclass, par->GetLayout());
111 char par_depth = par->GetDepth();
112 // We specialize the 95% common case:
113 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
116 if (layout.labeltype == LABEL_MANUAL
117 && pos < BeginningOfMainBody(par)) {
119 return par->GetFontSettings(pos).
120 realize(layout.reslabelfont);
122 return par->GetFontSettings(pos).
123 realize(layout.resfont);
126 // process layoutfont for pos == -1 and labelfont for pos < -1
128 return layout.resfont;
130 return layout.reslabelfont;
134 // The uncommon case need not be optimized as much
136 LyXFont layoutfont, tmpfont;
140 if (pos < BeginningOfMainBody(par)) {
142 layoutfont = layout.labelfont;
145 layoutfont = layout.font;
147 tmpfont = par->GetFontSettings(pos);
148 tmpfont.realize(layoutfont);
151 // process layoutfont for pos == -1 and labelfont for pos < -1
153 tmpfont = layout.font;
155 tmpfont = layout.labelfont;
158 // Resolve against environment font information
159 while (par && par_depth && !tmpfont.resolved()) {
160 par = par->DepthHook(par_depth - 1);
162 tmpfont.realize(textclasslist.
163 Style(parameters->textclass,
164 par->GetLayout()).font);
165 par_depth = par->GetDepth();
169 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
171 // Cosmetic improvement: If this is an open footnote, make the font
173 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
174 && par->footnotekind == LyXParagraph::FOOTNOTE) {
182 void LyXText::SetCharFont(LyXParagraph * par,
183 LyXParagraph::size_type pos,
187 // Let the insets convert their font
188 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
189 if (par->GetInset(pos))
190 font = par->GetInset(pos)->ConvertFont(font);
193 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
196 // Get concrete layout font to reduce against
199 if (pos < BeginningOfMainBody(par))
200 layoutfont = layout.labelfont;
202 layoutfont = layout.font;
204 // Realize against environment font information
205 if (par->GetDepth()){
206 LyXParagraph * tp = par;
207 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
208 tp = tp->DepthHook(tp->GetDepth()-1);
210 layoutfont.realize(textclasslist.
211 Style(parameters->textclass,
212 tp->GetLayout()).font);
216 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
218 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
219 && par->footnotekind == LyXParagraph::FOOTNOTE) {
220 layoutfont.decSize();
223 // Now, reduce font against full layout font
224 font.reduce(layoutfont);
226 par->SetFont(pos, font);
230 /* inserts a new row behind the specified row, increments
231 * the touched counters */
232 void LyXText::InsertRow(Row * row, LyXParagraph * par,
233 LyXParagraph::size_type pos) const
235 Row * tmprow = new Row;
237 tmprow->previous = 0;
238 tmprow->next = firstrow;
242 tmprow->previous = row;
243 tmprow->next = row->next;
248 tmprow->next->previous = tmprow;
250 if (tmprow->previous)
251 tmprow->previous->next = tmprow;
259 ++number_of_rows; // one more row
263 // removes the row and reset the touched counters
264 void LyXText::RemoveRow(Row * row) const
266 /* this must not happen before the currentrow for clear reasons.
267 so the trick is just to set the current row onto the previous
270 GetRow(row->par, row->pos, unused_y);
271 currentrow = currentrow->previous;
273 currentrow_y -= currentrow->height;
278 row->next->previous = row->previous;
279 if (!row->previous) {
280 firstrow = row->next;
283 row->previous->next = row->next;
286 lastrow = row->previous;
288 height -= row->height; // the text becomes smaller
291 --number_of_rows; // one row less
295 // remove all following rows of the paragraph of the specified row.
296 void LyXText::RemoveParagraph(Row * row) const
298 LyXParagraph * tmppar = row->par;
302 while (row && row->par == tmppar) {
310 // insert the specified paragraph behind the specified row
311 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
313 InsertRow(row, par, 0); /* insert a new row, starting
316 SetCounter(par); // set the counters
318 // and now append the whole paragraph behind the new row
320 firstrow->height = 0;
321 AppendParagraph(firstrow);
324 row->next->height = 0;
325 AppendParagraph(row->next);
330 void LyXText::ToggleFootnote()
332 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
334 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
336 current_view->owner()->getMiniBuffer()->Set(_("Opened float"));
338 current_view->owner()->getMiniBuffer()->Set(_("Closed float"));
344 void LyXText::OpenStuff()
346 if (cursor.pos == 0 && cursor.par->bibkey){
347 cursor.par->bibkey->Edit(0, 0);
349 else if (cursor.pos < cursor.par->Last()
350 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
351 && cursor.par->GetInset(cursor.pos)->Editable()) {
352 current_view->owner()->getMiniBuffer()
353 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
354 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
356 cursor.par->GetInset(cursor.pos)->Edit(0, 0);
363 void LyXText::CloseFootnote()
365 LyXParagraph * tmppar;
366 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
368 // if the cursor is not in an open footnote, or
369 // there is no open footnote in this paragraph, just return.
370 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
373 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
374 current_view->owner()->getMiniBuffer()
375 ->Set(_("Nothing to do"));
379 // ok, move the cursor right before the footnote
380 // just a little faster than using CursorRight()
382 cursor.par->ParFromPos(cursor.pos) != par;
386 // now the cursor is at the beginning of the physical par
387 SetCursor(cursor.par,
389 cursor.par->ParFromPos(cursor.pos)->text.size());
391 /* we are in a footnote, so let us move at the beginning */
392 /* this is just faster than using just CursorLeft() */
395 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
396 // just a little bit faster than movin the cursor
397 tmppar = tmppar->Previous();
399 SetCursor(tmppar, tmppar->Last());
402 // the cursor must be exactly before the footnote
403 par = cursor.par->ParFromPos(cursor.pos);
405 status = LyXText::NEED_MORE_REFRESH;
406 refresh_row = cursor.row;
407 refresh_y = cursor.y - cursor.row->baseline;
410 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
411 Row * row = cursor.row;
413 tmppar->CloseFootnote(cursor.pos);
415 while (tmppar != endpar) {
416 RemoveRow(row->next);
418 tmppar = row->next->par;
423 AppendParagraph(cursor.row);
425 SetCursor(cursor.par, cursor.pos);
429 if (cursor.row->next)
430 SetHeightOfRow(cursor.row->next);
434 /* used in setlayout */
435 // Asger is not sure we want to do this...
436 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
438 LyXFont layoutfont, tmpfont;
440 LyXLayout const & layout =
441 textclasslist.Style(parameters->textclass, par->GetLayout());
443 for (LyXParagraph::size_type pos = 0;
444 pos < par->Last(); ++pos) {
445 if (pos < BeginningOfMainBody(par))
446 layoutfont = layout.labelfont;
448 layoutfont = layout.font;
450 tmpfont = par->GetFontSettings(pos);
451 tmpfont.reduce(layoutfont);
452 par->SetFont(pos, tmpfont);
457 // set layout over selection and make a total rebreak of those paragraphs
458 void LyXText::SetLayout(char layout)
462 // if there is no selection just set the layout
463 // of the current paragraph */
465 sel_start_cursor = cursor; // dummy selection
466 sel_end_cursor = cursor;
469 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
470 LyXParagraph * undoendpar = endpar;
472 if (endpar && endpar->GetDepth()) {
473 while (endpar && endpar->GetDepth()) {
474 endpar = endpar->LastPhysicalPar()->Next();
479 endpar = endpar->Next(); // because of parindents etc.
483 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
486 tmpcursor = cursor; /* store the current cursor */
488 /* ok we have a selection. This is always between sel_start_cursor
489 * and sel_end cursor */
490 cursor = sel_start_cursor;
492 LyXLayout const & lyxlayout =
493 textclasslist.Style(parameters->textclass, layout);
495 while (cursor.par != sel_end_cursor.par) {
496 if (cursor.par->footnoteflag ==
497 sel_start_cursor.par->footnoteflag) {
498 cursor.par->SetLayout(layout);
499 MakeFontEntriesLayoutSpecific(cursor.par);
500 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
501 fppar->added_space_top = lyxlayout.fill_top ?
502 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
503 fppar->added_space_bottom = lyxlayout.fill_bottom ?
504 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
505 if (lyxlayout.margintype == MARGIN_MANUAL)
506 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
507 if (lyxlayout.labeltype != LABEL_BIBLIO
509 delete fppar->bibkey;
513 cursor.par = cursor.par->Next();
515 if (cursor.par->footnoteflag ==
516 sel_start_cursor.par->footnoteflag) {
517 cursor.par->SetLayout(layout);
518 MakeFontEntriesLayoutSpecific(cursor.par);
519 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
520 fppar->added_space_top = lyxlayout.fill_top ?
521 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
522 fppar->added_space_bottom = lyxlayout.fill_bottom ?
523 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
524 if (lyxlayout.margintype == MARGIN_MANUAL)
525 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
526 if (lyxlayout.labeltype != LABEL_BIBLIO
528 delete fppar->bibkey;
533 RedoParagraphs(sel_start_cursor, endpar);
535 // we have to reset the selection, because the
536 // geometry could have changed */
537 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
539 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
540 UpdateCounters(cursor.row);
543 SetCursor(tmpcursor.par, tmpcursor.pos);
547 // increment depth over selection and
548 // make a total rebreak of those paragraphs
549 void LyXText::IncDepth()
551 // If there is no selection, just use the current paragraph
553 sel_start_cursor = cursor; // dummy selection
554 sel_end_cursor = cursor;
557 // We end at the next paragraph with depth 0
558 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
559 LyXParagraph * undoendpar = endpar;
561 if (endpar && endpar->GetDepth()) {
562 while (endpar && endpar->GetDepth()) {
563 endpar = endpar->LastPhysicalPar()->Next();
568 endpar = endpar->Next(); // because of parindents etc.
573 .par->ParFromPos(sel_start_cursor.pos)->previous,
576 LyXCursor tmpcursor = cursor; // store the current cursor
578 // ok we have a selection. This is always between sel_start_cursor
579 // and sel_end cursor
580 cursor = sel_start_cursor;
582 bool anything_changed = false;
585 // NOTE: you can't change the depth of a bibliography entry
586 if (cursor.par->footnoteflag ==
587 sel_start_cursor.par->footnoteflag
588 && textclasslist.Style(parameters->textclass,
589 cursor.par->GetLayout()
590 ).labeltype != LABEL_BIBLIO) {
591 LyXParagraph * prev =
592 cursor.par->FirstPhysicalPar()->Previous();
594 && (prev->GetDepth() - cursor.par->GetDepth() > 0
595 || (prev->GetDepth() == cursor.par->GetDepth()
596 && textclasslist.Style(parameters->textclass,
597 prev->GetLayout()).isEnvironment()))) {
598 cursor.par->FirstPhysicalPar()->depth++;
599 anything_changed = true;
602 if (cursor.par == sel_end_cursor.par)
604 cursor.par = cursor.par->Next();
607 // if nothing changed set all depth to 0
608 if (!anything_changed) {
609 cursor = sel_start_cursor;
610 while (cursor.par != sel_end_cursor.par) {
611 cursor.par->FirstPhysicalPar()->depth = 0;
612 cursor.par = cursor.par->Next();
614 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
615 cursor.par->FirstPhysicalPar()->depth = 0;
618 RedoParagraphs(sel_start_cursor, endpar);
620 // we have to reset the selection, because the
621 // geometry could have changed
622 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
624 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
625 UpdateCounters(cursor.row);
628 SetCursor(tmpcursor.par, tmpcursor.pos);
632 // decrement depth over selection and
633 // make a total rebreak of those paragraphs
634 void LyXText::DecDepth()
636 // if there is no selection just set the layout
637 // of the current paragraph
639 sel_start_cursor = cursor; // dummy selection
640 sel_end_cursor = cursor;
643 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
644 LyXParagraph * undoendpar = endpar;
646 if (endpar && endpar->GetDepth()) {
647 while (endpar && endpar->GetDepth()) {
648 endpar = endpar->LastPhysicalPar()->Next();
653 endpar = endpar->Next(); // because of parindents etc.
658 .par->ParFromPos(sel_start_cursor.pos)->previous,
661 LyXCursor tmpcursor = cursor; // store the current cursor
663 // ok we have a selection. This is always between sel_start_cursor
664 // and sel_end cursor
665 cursor = sel_start_cursor;
668 if (cursor.par->footnoteflag ==
669 sel_start_cursor.par->footnoteflag) {
670 if (cursor.par->FirstPhysicalPar()->depth)
671 cursor.par->FirstPhysicalPar()->depth--;
673 if (cursor.par == sel_end_cursor.par)
675 cursor.par = cursor.par->Next();
678 RedoParagraphs(sel_start_cursor, endpar);
680 // we have to reset the selection, because the
681 // geometry could have changed
682 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
684 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
685 UpdateCounters(cursor.row);
688 SetCursor(tmpcursor.par, tmpcursor.pos);
692 // set font over selection and make a total rebreak of those paragraphs
693 void LyXText::SetFont(LyXFont const & font, bool toggleall)
695 // if there is no selection just set the current_font
697 // Determine basis font
699 if (cursor.pos < BeginningOfMainBody(cursor.par))
700 layoutfont = GetFont(cursor.par, -2);
702 layoutfont = GetFont(cursor.par, -1);
703 // Update current font
704 real_current_font.update(font, toggleall);
706 // Reduce to implicit settings
707 current_font = real_current_font;
708 current_font.reduce(layoutfont);
709 // And resolve it completely
710 real_current_font.realize(layoutfont);
714 LyXCursor tmpcursor = cursor; // store the current cursor
716 // ok we have a selection. This is always between sel_start_cursor
717 // and sel_end cursor
720 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
721 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
722 cursor = sel_start_cursor;
723 while (cursor.par != sel_end_cursor.par ||
724 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
725 && cursor.pos < sel_end_cursor.pos))
727 if (cursor.pos < cursor.par->Last()
728 && cursor.par->footnoteflag
729 == sel_start_cursor.par->footnoteflag) {
730 // an open footnote should behave
732 LyXFont newfont = GetFont(cursor.par, cursor.pos);
733 newfont.update(font, toggleall);
734 SetCharFont(cursor.par, cursor.pos, newfont);
738 cursor.par = cursor.par->Next();
742 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
744 // we have to reset the selection, because the
745 // geometry could have changed
746 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
748 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
751 SetCursor(tmpcursor.par, tmpcursor.pos);
755 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
757 Row * tmprow = cur.row;
758 long y = cur.y - tmprow->baseline;
760 SetHeightOfRow(tmprow);
761 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
762 // find the first row of the paragraph
763 if (first_phys_par != tmprow->par)
764 while (tmprow->previous
765 && tmprow->previous->par != first_phys_par) {
766 tmprow = tmprow->previous;
768 SetHeightOfRow(tmprow);
770 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
771 tmprow = tmprow->previous;
773 SetHeightOfRow(tmprow);
776 // we can set the refreshing parameters now
777 status = LyXText::NEED_MORE_REFRESH;
779 refresh_row = tmprow;
780 SetCursor(cur.par, cur.pos);
784 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
786 Row * tmprow = cur.row;
788 long y = cur.y - tmprow->baseline;
789 SetHeightOfRow(tmprow);
790 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
791 // find the first row of the paragraph
792 if (first_phys_par != tmprow->par)
793 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
794 tmprow = tmprow->previous;
797 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
798 tmprow = tmprow->previous;
802 // we can set the refreshing parameters now
803 if (status == LyXText::UNCHANGED || y < refresh_y) {
805 refresh_row = tmprow;
807 status = LyXText::NEED_MORE_REFRESH;
808 SetCursor(cur.par, cur.pos);
812 /* deletes and inserts again all paragaphs between the cursor
813 * and the specified par
814 * This function is needed after SetLayout and SetFont etc. */
815 void LyXText::RedoParagraphs(LyXCursor const & cur,
816 LyXParagraph const * endpar) const
819 LyXParagraph * tmppar, * first_phys_par;
821 Row * tmprow = cur.row;
823 long y = cur.y - tmprow->baseline;
825 if (!tmprow->previous){
826 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
828 first_phys_par = tmprow->par->FirstPhysicalPar();
829 // find the first row of the paragraph
830 if (first_phys_par != tmprow->par)
831 while (tmprow->previous
832 && tmprow->previous->par != first_phys_par) {
833 tmprow = tmprow->previous;
836 while (tmprow->previous
837 && tmprow->previous->par == first_phys_par) {
838 tmprow = tmprow->previous;
843 // we can set the refreshing parameters now
844 status = LyXText::NEED_MORE_REFRESH;
846 refresh_row = tmprow->previous; /* the real refresh row will
847 be deleted, so I store
851 tmppar = tmprow->next->par;
854 while (tmppar != endpar) {
855 RemoveRow(tmprow->next);
857 tmppar = tmprow->next->par;
862 // remove the first one
863 tmprow2 = tmprow; /* this is because tmprow->previous
865 tmprow = tmprow->previous;
868 tmppar = first_phys_par;
872 InsertParagraph(tmppar, tmprow);
875 while (tmprow->next && tmprow->next->par == tmppar)
876 tmprow = tmprow->next;
877 tmppar = tmppar->Next();
879 } while (tmppar != endpar);
881 // this is because of layout changes
883 refresh_y -= refresh_row->height;
884 SetHeightOfRow(refresh_row);
886 refresh_row = firstrow;
888 SetHeightOfRow(refresh_row);
891 if (tmprow && tmprow->next)
892 SetHeightOfRow(tmprow->next);
896 int LyXText::FullRebreak()
898 if (need_break_row) {
899 BreakAgain(need_break_row);
907 /* important for the screen */
910 /* the cursor set functions have a special mechanism. When they
911 * realize, that you left an empty paragraph, they will delete it.
912 * They also delet the corresponding row */
914 // need the selection cursor:
915 void LyXText::SetSelection()
918 last_sel_cursor = sel_cursor;
919 sel_start_cursor = sel_cursor;
920 sel_end_cursor = sel_cursor;
925 // first the toggling area
926 if (cursor.y < last_sel_cursor.y ||
927 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
928 toggle_end_cursor = last_sel_cursor;
929 toggle_cursor = cursor;
932 toggle_end_cursor = cursor;
933 toggle_cursor = last_sel_cursor;
936 last_sel_cursor = cursor;
938 // and now the whole selection
940 if (sel_cursor.y < cursor.y ||
941 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
942 sel_end_cursor = cursor;
943 sel_start_cursor = sel_cursor;
946 sel_end_cursor = sel_cursor;
947 sel_start_cursor = cursor;
950 // a selection with no contents is not a selection
951 if (sel_start_cursor.x == sel_end_cursor.x &&
952 sel_start_cursor.y == sel_end_cursor.y)
957 void LyXText::ClearSelection() const
964 void LyXText::CursorHome() const
966 SetCursor(cursor.par, cursor.row->pos);
970 void LyXText::CursorEnd() const
972 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
973 SetCursor(cursor.par, RowLast(cursor.row) + 1);
975 if (cursor.par->Last() &&
976 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
977 || cursor.par->IsNewline(RowLast(cursor.row))))
978 SetCursor(cursor.par, RowLast(cursor.row));
980 SetCursor(cursor.par, RowLast(cursor.row) + 1);
982 if (cursor.par->table) {
983 int cell = NumberOfCell(cursor.par, cursor.pos);
984 if (cursor.par->table->RowHasContRow(cell) &&
985 cursor.par->table->CellHasContRow(cell)<0) {
986 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
987 SetCursor(cursor.par, RowLast(cursor.row) + 1);
989 if (cursor.par->Last() &&
990 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
991 || cursor.par->IsNewline(RowLast(cursor.row))))
992 SetCursor(cursor.par, RowLast(cursor.row));
994 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1001 void LyXText::CursorTop() const
1003 while (cursor.par->Previous())
1004 cursor.par = cursor.par->Previous();
1005 SetCursor(cursor.par, 0);
1009 void LyXText::CursorBottom() const
1011 while (cursor.par->Next())
1012 cursor.par = cursor.par->Next();
1013 SetCursor(cursor.par, cursor.par->Last());
1017 /* returns a pointer to the row near the specified y-coordinate
1018 * (relative to the whole text). y is set to the real beginning
1020 Row * LyXText::GetRowNearY(long & y) const
1026 tmprow = currentrow;
1027 tmpy = currentrow_y;
1034 while (tmprow->next && tmpy + tmprow->height <= y) {
1035 tmpy += tmprow->height;
1036 tmprow = tmprow->next;
1039 while (tmprow->previous && tmpy > y) {
1040 tmprow = tmprow->previous;
1041 tmpy -= tmprow->height;
1044 currentrow = tmprow;
1045 currentrow_y = tmpy;
1047 y = tmpy; // return the real y
1052 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1054 // If the mask is completely neutral, tell user
1055 if (font == LyXFont(LyXFont::ALL_IGNORE)){
1056 // Could only happen with user style
1057 current_view->owner()->getMiniBuffer()
1058 ->Set(_("No font change defined. Use Character under"
1059 " the Layout menu to define font change."));
1063 // Try implicit word selection
1064 LyXCursor resetCursor = cursor;
1065 int implicitSelection = SelectWordWhenUnderCursor();
1068 SetFont(font, toggleall);
1070 /* Implicit selections are cleared afterwards and cursor is set to the
1071 original position. */
1072 if (implicitSelection) {
1074 cursor = resetCursor;
1075 SetCursor( cursor.par, cursor.pos );
1076 sel_cursor = cursor;
1081 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1083 if (textclasslist.Style(parameters->textclass,
1084 par->GetLayout()).labeltype != LABEL_MANUAL)
1087 return par->BeginningOfMainBody();
1091 /* if there is a selection, reset every environment you can find
1092 * in the selection, otherwise just the environment you are in */
1093 void LyXText::MeltFootnoteEnvironment()
1095 LyXParagraph * tmppar, * firsttmppar;
1099 /* is is only allowed, if the cursor is IN an open footnote.
1100 * Otherwise it is too dangerous */
1101 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1104 SetUndo(Undo::FINISH,
1105 cursor.par->PreviousBeforeFootnote()->previous,
1106 cursor.par->NextAfterFootnote()->next);
1108 /* ok, move to the beginning of the footnote. */
1109 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1110 cursor.par = cursor.par->Previous();
1112 SetCursor(cursor.par, cursor.par->Last());
1113 /* this is just faster than using CursorLeft(); */
1115 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1116 tmppar = firsttmppar;
1117 /* tmppar is now the paragraph right before the footnote */
1119 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1122 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1123 tmppar = tmppar->next; /* I use next instead of Next(),
1124 * because there cannot be any
1125 * footnotes in a footnote
1127 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1129 /* remember the captions and empty paragraphs */
1130 if ((textclasslist.Style(parameters->textclass,
1131 tmppar->GetLayout())
1132 .labeltype == LABEL_SENSITIVE)
1134 tmppar->SetLayout(0);
1137 // now we will paste the ex-footnote, if the layouts allow it
1138 // first restore the layout of the paragraph right behind
1141 tmppar->next->MakeSameLayout(cursor.par);
1144 if ((!tmppar->GetLayout() && !tmppar->table)
1146 && (!tmppar->Next()->Last()
1147 || tmppar->Next()->HasSameLayout(tmppar)))) {
1148 if (tmppar->Next()->Last()
1149 && tmppar->Next()->IsLineSeparator(0))
1150 tmppar->Next()->Erase(0);
1151 tmppar->PasteParagraph();
1154 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1155 * by the pasting of the beginning */
1157 /* then the beginning */
1158 /* if there is no space between the text and the footnote, so we insert
1160 * (only if the previous par and the footnotepar are not empty!) */
1161 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1162 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1163 if (firsttmppar->text.size()
1164 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1165 && first_footnote_par_is_not_empty) {
1166 firsttmppar->next->InsertChar(0, ' ');
1168 firsttmppar->PasteParagraph();
1171 /* now redo the paragaphs */
1172 RedoParagraphs(cursor, tmppar);
1174 SetCursor(cursor.par, cursor.pos);
1176 /* sometimes it can happen, that there is a counter change */
1177 Row * row = cursor.row;
1178 while (row->next && row->par != tmppar && row->next->par != tmppar)
1180 UpdateCounters(row);
1187 /* the DTP switches for paragraphs. LyX will store them in the
1188 * first physicla paragraph. When a paragraph is broken, the top settings
1189 * rest, the bottom settings are given to the new one. So I can make shure,
1190 * they do not duplicate themself and you cannnot make dirty things with
1193 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1194 bool pagebreak_top, bool pagebreak_bottom,
1195 VSpace const & space_top,
1196 VSpace const & space_bottom,
1198 string labelwidthstring,
1201 LyXCursor tmpcursor = cursor;
1203 sel_start_cursor = cursor;
1204 sel_end_cursor = cursor;
1207 // make sure that the depth behind the selection are restored, too
1208 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1209 LyXParagraph * undoendpar = endpar;
1211 if (endpar && endpar->GetDepth()) {
1212 while (endpar && endpar->GetDepth()) {
1213 endpar = endpar->LastPhysicalPar()->Next();
1214 undoendpar = endpar;
1218 endpar = endpar->Next(); // because of parindents etc.
1223 .par->ParFromPos(sel_start_cursor.pos)->previous,
1227 LyXParagraph * tmppar = sel_end_cursor.par;
1228 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1229 SetCursor(tmppar->FirstPhysicalPar(), 0);
1230 status = LyXText::NEED_MORE_REFRESH;
1231 refresh_row = cursor.row;
1232 refresh_y = cursor.y - cursor.row->baseline;
1233 if (cursor.par->footnoteflag ==
1234 sel_start_cursor.par->footnoteflag) {
1235 cursor.par->line_top = line_top;
1236 cursor.par->line_bottom = line_bottom;
1237 cursor.par->pagebreak_top = pagebreak_top;
1238 cursor.par->pagebreak_bottom = pagebreak_bottom;
1239 cursor.par->added_space_top = space_top;
1240 cursor.par->added_space_bottom = space_bottom;
1241 // does the layout allow the new alignment?
1242 if (align == LYX_ALIGN_LAYOUT)
1243 align = textclasslist
1244 .Style(parameters->textclass,
1245 cursor.par->GetLayout()).align;
1246 if (align & textclasslist
1247 .Style(parameters->textclass,
1248 cursor.par->GetLayout()).alignpossible) {
1249 if (align == textclasslist
1250 .Style(parameters->textclass,
1251 cursor.par->GetLayout()).align)
1252 cursor.par->align = LYX_ALIGN_LAYOUT;
1254 cursor.par->align = align;
1256 cursor.par->SetLabelWidthString(labelwidthstring);
1257 cursor.par->noindent = noindent;
1260 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1263 RedoParagraphs(sel_start_cursor, endpar);
1266 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1267 sel_cursor = cursor;
1268 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1270 SetCursor(tmpcursor.par, tmpcursor.pos);
1274 void LyXText::SetParagraphExtraOpt(int type,
1276 char const * widthp,
1277 int alignment, bool hfill,
1278 bool start_minipage)
1280 LyXCursor tmpcursor = cursor;
1281 LyXParagraph * tmppar;
1283 sel_start_cursor = cursor;
1284 sel_end_cursor = cursor;
1287 // make sure that the depth behind the selection are restored, too
1288 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1289 LyXParagraph * undoendpar = endpar;
1291 if (endpar && endpar->GetDepth()) {
1292 while (endpar && endpar->GetDepth()) {
1293 endpar = endpar->LastPhysicalPar()->Next();
1294 undoendpar = endpar;
1298 endpar = endpar->Next(); // because of parindents etc.
1303 .par->ParFromPos(sel_start_cursor.pos)->previous,
1306 tmppar = sel_end_cursor.par;
1307 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1308 SetCursor(tmppar->FirstPhysicalPar(), 0);
1309 status = LyXText::NEED_MORE_REFRESH;
1310 refresh_row = cursor.row;
1311 refresh_y = cursor.y - cursor.row->baseline;
1312 if (cursor.par->footnoteflag ==
1313 sel_start_cursor.par->footnoteflag) {
1314 if (type == LyXParagraph::PEXTRA_NONE) {
1315 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1316 cursor.par->UnsetPExtraType();
1317 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1320 cursor.par->SetPExtraType(type, width, widthp);
1321 cursor.par->pextra_hfill = hfill;
1322 cursor.par->pextra_start_minipage = start_minipage;
1323 cursor.par->pextra_alignment = alignment;
1326 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1328 RedoParagraphs(sel_start_cursor, endpar);
1330 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1331 sel_cursor = cursor;
1332 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1334 SetCursor(tmpcursor.par, tmpcursor.pos);
1338 static char const * alphaCounter(int n) {
1339 static char result[2];
1344 result[0] = 'A' + n;
1352 // set the counter of a paragraph. This includes the labels
1353 void LyXText::SetCounter(LyXParagraph * par) const
1355 // this is only relevant for the beginning of paragraph
1356 par = par->FirstPhysicalPar();
1358 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1361 LyXTextClass const & textclass =
1362 textclasslist.TextClass(parameters->textclass);
1364 /* copy the prev-counters to this one, unless this is the start of a
1365 footnote or of a bibliography or the very first paragraph */
1367 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1368 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1369 && par->footnotekind == LyXParagraph::FOOTNOTE)
1370 && !(textclasslist.Style(parameters->textclass,
1371 par->Previous()->GetLayout()
1372 ).labeltype != LABEL_BIBLIO
1373 && layout.labeltype == LABEL_BIBLIO)) {
1374 for (int i = 0; i < 10; ++i) {
1375 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1377 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1378 if (!par->appendix && par->start_of_appendix){
1379 par->appendix = true;
1380 for (int i = 0; i < 10; ++i) {
1381 par->setCounter(i, 0);
1384 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1385 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1388 for (int i = 0; i < 10; ++i) {
1389 par->setCounter(i, 0);
1391 par->appendix = par->start_of_appendix;
1396 // if this is an open marginnote and this is the first
1397 // entry in the marginnote and the enclosing
1398 // environment is an enum/item then correct for the
1399 // LaTeX behaviour (ARRae)
1400 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1401 && par->footnotekind == LyXParagraph::MARGIN
1403 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1404 && (par->PreviousBeforeFootnote()
1405 && textclasslist.Style(parameters->textclass,
1406 par->PreviousBeforeFootnote()->GetLayout()
1407 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1408 // Any itemize or enumerate environment in a marginnote
1409 // that is embedded in an itemize or enumerate
1410 // paragraph is seen by LaTeX as being at a deeper
1411 // level within that enclosing itemization/enumeration
1412 // even if there is a "standard" layout at the start of
1418 /* Maybe we have to increment the enumeration depth.
1419 * BUT, enumeration in a footnote is considered in isolation from its
1420 * surrounding paragraph so don't increment if this is the
1421 * first line of the footnote
1422 * AND, bibliographies can't have their depth changed ie. they
1423 * are always of depth 0
1426 && par->Previous()->GetDepth() < par->GetDepth()
1427 && textclasslist.Style(parameters->textclass,
1428 par->Previous()->GetLayout()
1429 ).labeltype == LABEL_COUNTER_ENUMI
1430 && par->enumdepth < 3
1431 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1432 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1433 && par->footnotekind == LyXParagraph::FOOTNOTE)
1434 && layout.labeltype != LABEL_BIBLIO) {
1438 /* Maybe we have to decrement the enumeration depth, see note above */
1440 && par->Previous()->GetDepth() > par->GetDepth()
1441 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1442 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1443 && par->footnotekind == LyXParagraph::FOOTNOTE)
1444 && layout.labeltype != LABEL_BIBLIO) {
1445 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1446 par->setCounter(6 + par->enumdepth,
1447 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1448 /* reset the counters.
1449 * A depth change is like a breaking layout
1451 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1452 par->setCounter(i, 0);
1455 if (!par->labelstring.empty()) {
1456 par->labelstring.clear();
1459 if (layout.margintype == MARGIN_MANUAL) {
1460 if (par->labelwidthstring.empty()) {
1461 par->SetLabelWidthString(layout.labelstring());
1465 par->SetLabelWidthString(string());
1468 /* is it a layout that has an automatic label ? */
1469 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1471 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1472 if (i >= 0 && i<= parameters->secnumdepth) {
1473 par->incCounter(i); // increment the counter
1475 char * s = new char[50];
1477 // Is there a label? Useful for Chapter layout
1478 if (!par->appendix){
1479 if (!layout.labelstring().empty())
1480 par->labelstring = layout.labelstring();
1482 par->labelstring.clear();
1484 if (!layout.labelstring_appendix().empty())
1485 par->labelstring = layout.labelstring_appendix();
1487 par->labelstring.clear();
1490 if (!par->appendix){
1491 switch (2 * LABEL_FIRST_COUNTER -
1492 textclass.maxcounter() + i) {
1493 case LABEL_COUNTER_CHAPTER:
1495 par->getCounter(i));
1497 case LABEL_COUNTER_SECTION:
1499 par->getCounter(i - 1),
1500 par->getCounter(i));
1502 case LABEL_COUNTER_SUBSECTION:
1503 sprintf(s, "%d.%d.%d",
1504 par->getCounter(i-2),
1505 par->getCounter(i-1),
1506 par->getCounter(i));
1508 case LABEL_COUNTER_SUBSUBSECTION:
1509 sprintf(s, "%d.%d.%d.%d",
1510 par->getCounter(i-3),
1511 par->getCounter(i-2),
1512 par->getCounter(i-1),
1513 par->getCounter(i));
1515 case LABEL_COUNTER_PARAGRAPH:
1516 sprintf(s, "%d.%d.%d.%d.%d",
1517 par->getCounter(i-4),
1518 par->getCounter(i-3),
1519 par->getCounter(i-2),
1520 par->getCounter(i-1),
1521 par->getCounter(i));
1523 case LABEL_COUNTER_SUBPARAGRAPH:
1524 sprintf(s, "%d.%d.%d.%d.%d.%d",
1525 par->getCounter(i-5),
1526 par->getCounter(i-4),
1527 par->getCounter(i-3),
1528 par->getCounter(i-2),
1529 par->getCounter(i-1),
1530 par->getCounter(i));
1533 sprintf(s, "%d.", par->getCounter(i));
1537 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1538 case LABEL_COUNTER_CHAPTER:
1540 alphaCounter(par->getCounter(i)));
1542 case LABEL_COUNTER_SECTION:
1544 alphaCounter(par->getCounter(i - 1)),
1545 par->getCounter(i));
1547 case LABEL_COUNTER_SUBSECTION:
1548 sprintf(s, "%s.%d.%d",
1549 alphaCounter(par->getCounter(i-2)),
1550 par->getCounter(i-1),
1551 par->getCounter(i));
1553 case LABEL_COUNTER_SUBSUBSECTION:
1554 sprintf(s, "%s.%d.%d.%d",
1555 alphaCounter(par->getCounter(i-3)),
1556 par->getCounter(i-2),
1557 par->getCounter(i-1),
1558 par->getCounter(i));
1560 case LABEL_COUNTER_PARAGRAPH:
1561 sprintf(s, "%s.%d.%d.%d.%d",
1562 alphaCounter(par->getCounter(i-4)),
1563 par->getCounter(i-3),
1564 par->getCounter(i-2),
1565 par->getCounter(i-1),
1566 par->getCounter(i));
1568 case LABEL_COUNTER_SUBPARAGRAPH:
1569 sprintf(s, "%s.%d.%d.%d.%d.%d",
1570 alphaCounter(par->getCounter(i-5)),
1571 par->getCounter(i-4),
1572 par->getCounter(i-3),
1573 par->getCounter(i-2),
1574 par->getCounter(i-1),
1575 par->getCounter(i));
1578 sprintf(s, "%c.", par->getCounter(i));
1583 par->labelstring += s;
1586 for (i++; i < 10; ++i) {
1587 // reset the following counters
1588 par->setCounter(i, 0);
1590 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1591 for (i++; i < 10; ++i) {
1592 // reset the following counters
1593 par->setCounter(i, 0);
1595 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1596 par->incCounter(i + par->enumdepth);
1597 char * s = new char[25];
1598 int number = par->getCounter(i + par->enumdepth);
1599 switch (par->enumdepth) {
1601 sprintf(s, "(%c)", (number % 27) + 'a' - 1);
1605 case 1: sprintf(s, "i."); break;
1606 case 2: sprintf(s, "ii."); break;
1607 case 3: sprintf(s, "iii."); break;
1608 case 4: sprintf(s, "iv."); break;
1609 case 5: sprintf(s, "v."); break;
1610 case 6: sprintf(s, "vi."); break;
1611 case 7: sprintf(s, "vii."); break;
1612 case 8: sprintf(s, "viii."); break;
1613 case 9: sprintf(s, "ix."); break;
1614 case 10: sprintf(s, "x."); break;
1615 case 11: sprintf(s, "xi."); break;
1616 case 12: sprintf(s, "xii."); break;
1617 case 13: sprintf(s, "xiii."); break;
1619 sprintf(s, "\\roman{%d}.", number);
1624 sprintf(s, "%c.", (number % 27) + 'A' - 1);
1627 sprintf(s, "%d.", number);
1630 par->labelstring = s;
1633 for (i += par->enumdepth + 1; i < 10; ++i)
1634 par->setCounter(i, 0); /* reset the following counters */
1637 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1638 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1640 int number = par->getCounter(i);
1642 par->bibkey = new InsetBibKey();
1643 par->bibkey->setCounter(number);
1644 par->labelstring = layout.labelstring();
1646 // In biblio should't be following counters but...
1648 string s = layout.labelstring();
1650 // the caption hack:
1652 if (layout.labeltype == LABEL_SENSITIVE) {
1653 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1654 && (par->footnotekind == LyXParagraph::FIG
1655 || par->footnotekind == LyXParagraph::WIDE_FIG))
1657 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1658 && (par->footnotekind == LyXParagraph::TAB
1659 || par->footnotekind == LyXParagraph::WIDE_TAB))
1661 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1662 && par->footnotekind == LyXParagraph::ALGORITHM)
1665 /* par->SetLayout(0);
1666 s = layout->labelstring; */
1670 par->labelstring = s;
1672 /* reset the enumeration counter. They are always resetted
1673 * when there is any other layout between */
1674 for (int i = 6 + par->enumdepth; i < 10; ++i)
1675 par->setCounter(i, 0);
1680 /* Updates all counters BEHIND the row. Changed paragraphs
1681 * with a dynamic left margin will be rebroken. */
1682 void LyXText::UpdateCounters(Row * row) const
1691 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1692 par = row->par->LastPhysicalPar()->Next();
1694 par = row->par->next;
1699 while (row->par != par)
1704 /* now check for the headline layouts. remember that they
1705 * have a dynamic left margin */
1707 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1708 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1711 /* Rebreak the paragraph */
1712 RemoveParagraph(row);
1713 AppendParagraph(row);
1715 /* think about the damned open footnotes! */
1716 while (par->Next() &&
1717 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1718 || par->Next()->IsDummy())){
1720 if (par->IsDummy()) {
1721 while (row->par != par)
1723 RemoveParagraph(row);
1724 AppendParagraph(row);
1729 par = par->LastPhysicalPar()->Next();
1735 /* insets an inset. */
1736 void LyXText::InsertInset(Inset *inset)
1738 SetUndo(Undo::INSERT,
1739 cursor.par->ParFromPos(cursor.pos)->previous,
1740 cursor.par->ParFromPos(cursor.pos)->next);
1741 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1742 cursor.par->InsertInset(cursor.pos, inset);
1743 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1744 * The character will not be inserted a
1749 // this is for the simple cut and paste mechanism
1750 static LyXParagraph * simple_cut_buffer = 0;
1751 static char simple_cut_buffer_textclass = 0;
1753 void DeleteSimpleCutBuffer()
1755 if (!simple_cut_buffer)
1757 LyXParagraph * tmppar;
1759 while (simple_cut_buffer) {
1760 tmppar = simple_cut_buffer;
1761 simple_cut_buffer = simple_cut_buffer->next;
1764 simple_cut_buffer = 0;
1768 void LyXText::copyEnvironmentType()
1770 copylayouttype = cursor.par->GetLayout();
1774 void LyXText::pasteEnvironmentType()
1776 SetLayout(copylayouttype);
1780 void LyXText::CutSelection(bool doclear)
1782 // This doesn't make sense, if there is no selection
1786 // OK, we have a selection. This is always between sel_start_cursor
1787 // and sel_end cursor
1788 LyXParagraph * tmppar;
1790 // Check whether there are half footnotes in the selection
1791 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1792 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1793 tmppar = sel_start_cursor.par;
1794 while (tmppar != sel_end_cursor.par){
1795 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1796 WriteAlert(_("Impossible operation"),
1797 _("Don't know what to do with half floats."),
1801 tmppar = tmppar->Next();
1805 /* table stuff -- begin */
1806 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1807 if ( sel_start_cursor.par != sel_end_cursor.par) {
1808 WriteAlert(_("Impossible operation"),
1809 _("Don't know what to do with half tables."),
1813 sel_start_cursor.par->table->Reinit();
1815 /* table stuff -- end */
1817 // make sure that the depth behind the selection are restored, too
1818 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1819 LyXParagraph * undoendpar = endpar;
1821 if (endpar && endpar->GetDepth()) {
1822 while (endpar && endpar->GetDepth()) {
1823 endpar = endpar->LastPhysicalPar()->Next();
1824 undoendpar = endpar;
1826 } else if (endpar) {
1827 endpar = endpar->Next(); // because of parindents etc.
1830 SetUndo(Undo::DELETE,
1832 .par->ParFromPos(sel_start_cursor.pos)->previous,
1835 // clear the simple_cut_buffer
1836 DeleteSimpleCutBuffer();
1838 // set the textclass
1839 simple_cut_buffer_textclass = parameters->textclass;
1841 #ifdef WITH_WARNINGS
1842 #warning Asger: Make cut more intelligent here.
1845 White paper for "intelligent" cutting:
1847 Example: "This is our text."
1848 Using " our " as selection, cutting will give "This istext.".
1849 Using "our" as selection, cutting will give "This is text.".
1850 Using " our" as selection, cutting will give "This is text.".
1851 Using "our " as selection, cutting will give "This is text.".
1853 All those four selections will (however) paste identically:
1854 Pasting with the cursor right after the "is" will give the
1855 original text with all four selections.
1857 The rationale is to be intelligent such that words are copied,
1858 cut and pasted in a functional manner.
1860 This is not implemented yet. (Asger)
1862 The changes below sees to do a lot of what you want. However
1863 I have not verified that all cases work as they should:
1865 - cut in multiple row
1867 - cut across footnotes and paragraph
1868 My simplistic tests show that the idea are basically sound but
1869 there are some items to fix up...we only need to find them
1872 As do redo Asger's example above (with | beeing the cursor in the
1873 result after cutting.):
1875 Example: "This is our text."
1876 Using " our " as selection, cutting will give "This is|text.".
1877 Using "our" as selection, cutting will give "This is | text.".
1878 Using " our" as selection, cutting will give "This is| text.".
1879 Using "our " as selection, cutting will give "This is |text.".
1884 #ifndef FIX_DOUBLE_SPACE
1885 bool space_wrapped =
1886 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1887 if (sel_end_cursor.pos > 0
1888 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1889 // please break before a space at the end
1890 sel_end_cursor.pos--;
1891 space_wrapped = true;
1893 // cut behind a space if there is one
1894 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1895 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1896 && (sel_start_cursor.par != sel_end_cursor.par
1897 || sel_start_cursor.pos < sel_end_cursor.pos))
1898 sel_start_cursor.pos++;
1900 // there are two cases: cut only within one paragraph or
1901 // more than one paragraph
1903 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1904 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1905 // only within one paragraph
1906 simple_cut_buffer = new LyXParagraph;
1907 LyXParagraph::size_type i =
1908 sel_start_cursor.pos;
1909 for (; i < sel_end_cursor.pos; ++i) {
1910 /* table stuff -- begin */
1911 if (sel_start_cursor.par->table
1912 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1913 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1914 sel_start_cursor.pos++;
1916 /* table stuff -- end */
1917 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1918 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1920 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1922 #ifndef FIX_DOUBLE_SPACE
1923 // check for double spaces
1924 if (sel_start_cursor.pos &&
1925 sel_start_cursor.par->Last() > sel_start_cursor.pos
1926 && sel_start_cursor.par
1927 ->IsLineSeparator(sel_start_cursor.pos - 1)
1928 && sel_start_cursor.par
1929 ->IsLineSeparator(sel_start_cursor.pos)) {
1930 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1933 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1936 endpar = sel_end_cursor.par->Next();
1938 // cut more than one paragraph
1941 ->BreakParagraphConservative(sel_end_cursor.pos);
1942 #ifndef FIX_DOUBLE_SPACE
1943 // insert a space at the end if there was one
1946 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1948 sel_end_cursor.par = sel_end_cursor.par->Next();
1949 sel_end_cursor.pos = 0;
1951 cursor = sel_end_cursor;
1953 #ifndef FIX_DOUBLE_SPACE
1954 // please break behind a space, if there is one.
1955 // The space should be copied too
1956 if (sel_start_cursor.par
1957 ->IsLineSeparator(sel_start_cursor.pos))
1958 sel_start_cursor.pos++;
1960 sel_start_cursor.par
1961 ->BreakParagraphConservative(sel_start_cursor.pos);
1962 #ifndef FIX_DOUBLE_SPACE
1963 if (!sel_start_cursor.pos
1964 || sel_start_cursor.par
1965 ->IsLineSeparator(sel_start_cursor.pos - 1)
1966 || sel_start_cursor.par
1967 ->IsNewline(sel_start_cursor.pos - 1)) {
1968 sel_start_cursor.par->Next()->InsertChar(0, ' ');
1971 // store the endparagraph for redoing later
1972 endpar = sel_end_cursor.par->Next(); /* needed because
1977 // store the selection
1978 simple_cut_buffer = sel_start_cursor.par
1979 ->ParFromPos(sel_start_cursor.pos)->next;
1980 simple_cut_buffer->previous = 0;
1981 sel_end_cursor.par->previous->next = 0;
1983 // cut the selection
1984 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
1985 = sel_end_cursor.par;
1987 sel_end_cursor.par->previous
1988 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
1990 // care about footnotes
1991 if (simple_cut_buffer->footnoteflag) {
1992 LyXParagraph * tmppar = simple_cut_buffer;
1994 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1995 tmppar = tmppar->next;
1999 // the cut selection should begin with standard layout
2000 simple_cut_buffer->Clear();
2002 // paste the paragraphs again, if possible
2004 sel_start_cursor.par->Next()->ClearParagraph();
2005 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2007 !sel_start_cursor.par->Next()->Last())
2008 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2010 #ifndef FIX_DOUBLE_SPACE
2011 // maybe a forgotten blank
2012 if (sel_start_cursor.pos
2013 && sel_start_cursor.par
2014 ->IsLineSeparator(sel_start_cursor.pos)
2015 && sel_start_cursor.par
2016 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2017 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2022 // sometimes necessary
2024 sel_start_cursor.par->ClearParagraph();
2026 RedoParagraphs(sel_start_cursor, endpar);
2029 cursor = sel_start_cursor;
2030 SetCursor(cursor.par, cursor.pos);
2031 sel_cursor = cursor;
2032 UpdateCounters(cursor.row);
2036 void LyXText::CopySelection()
2038 // this doesnt make sense, if there is no selection
2042 // ok we have a selection. This is always between sel_start_cursor
2043 // and sel_end cursor
2044 LyXParagraph * tmppar;
2046 /* check wether there are half footnotes in the selection */
2047 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2048 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2049 tmppar = sel_start_cursor.par;
2050 while (tmppar != sel_end_cursor.par) {
2051 if (tmppar->footnoteflag !=
2052 sel_end_cursor.par->footnoteflag) {
2053 WriteAlert(_("Impossible operation"),
2054 _("Don't know what to do"
2055 " with half floats."),
2059 tmppar = tmppar->Next();
2063 /* table stuff -- begin */
2064 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2065 if ( sel_start_cursor.par != sel_end_cursor.par){
2066 WriteAlert(_("Impossible operation"),
2067 _("Don't know what to do with half tables."),
2072 /* table stuff -- end */
2074 // delete the simple_cut_buffer
2075 DeleteSimpleCutBuffer();
2077 // set the textclass
2078 simple_cut_buffer_textclass = parameters->textclass;
2080 #ifdef FIX_DOUBLE_SPACE
2081 // copy behind a space if there is one
2082 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2083 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2084 && (sel_start_cursor.par != sel_end_cursor.par
2085 || sel_start_cursor.pos < sel_end_cursor.pos))
2086 sel_start_cursor.pos++;
2088 // there are two cases: copy only within one paragraph
2089 // or more than one paragraph
2090 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2091 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2092 // only within one paragraph
2093 simple_cut_buffer = new LyXParagraph;
2094 LyXParagraph::size_type i = 0;
2095 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2096 sel_start_cursor.par->CopyIntoMinibuffer(i);
2097 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2100 // copy more than one paragraph
2101 // clone the paragraphs within the selection
2103 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2104 simple_cut_buffer = tmppar->Clone();
2105 LyXParagraph *tmppar2 = simple_cut_buffer;
2107 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2109 tmppar = tmppar->next;
2110 tmppar2->next = tmppar->Clone();
2111 tmppar2->next->previous = tmppar2;
2112 tmppar2 = tmppar2->next;
2116 // care about footnotes
2117 if (simple_cut_buffer->footnoteflag) {
2118 tmppar = simple_cut_buffer;
2120 tmppar->footnoteflag =
2121 LyXParagraph::NO_FOOTNOTE;
2122 tmppar = tmppar->next;
2126 // the simple_cut_buffer paragraph is too big
2127 LyXParagraph::size_type tmpi2 =
2128 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2129 for (; tmpi2; --tmpi2)
2130 simple_cut_buffer->Erase(0);
2132 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2134 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2135 while (tmppar2->size() > tmpi2) {
2136 tmppar2->Erase(tmppar2->text.size() - 1);
2142 void LyXText::PasteSelection()
2144 // this does not make sense, if there is nothing to paste
2145 if (!simple_cut_buffer)
2148 LyXParagraph * tmppar;
2149 LyXParagraph * endpar;
2151 LyXCursor tmpcursor;
2153 // be carefull with footnotes in footnotes
2154 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2156 // check whether the cut_buffer includes a footnote
2157 tmppar = simple_cut_buffer;
2159 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2160 tmppar = tmppar->next;
2163 WriteAlert(_("Impossible operation"),
2164 _("Can't paste float into float!"),
2170 /* table stuff -- begin */
2171 if (cursor.par->table) {
2172 if (simple_cut_buffer->next) {
2173 WriteAlert(_("Impossible operation"),
2174 _("Table cell cannot include more than one paragraph!"),
2179 /* table stuff -- end */
2181 SetUndo(Undo::INSERT,
2182 cursor.par->ParFromPos(cursor.pos)->previous,
2183 cursor.par->ParFromPos(cursor.pos)->next);
2187 // There are two cases: cutbuffer only one paragraph or many
2188 if (!simple_cut_buffer->next) {
2189 // only within a paragraph
2191 #ifndef FIX_DOUBLE_SPACE
2192 // please break behind a space, if there is one
2193 while (tmpcursor.par->Last() > tmpcursor.pos
2194 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2197 tmppar = simple_cut_buffer->Clone();
2198 /* table stuff -- begin */
2199 bool table_too_small = false;
2200 if (tmpcursor.par->table) {
2201 while (simple_cut_buffer->text.size()
2202 && !table_too_small) {
2203 if (simple_cut_buffer->IsNewline(0)){
2204 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2206 simple_cut_buffer->Erase(0);
2207 if (tmpcursor.pos < tmpcursor.par->Last())
2210 table_too_small = true;
2212 #ifdef FIX_DOUBLE_SPACE
2213 // This is an attempt to fix the
2214 // "never insert a space at the
2215 // beginning of a paragraph" problem.
2216 if (tmpcursor.pos == 0
2217 && simple_cut_buffer->IsLineSeparator(0)) {
2218 simple_cut_buffer->Erase(0);
2220 simple_cut_buffer->CutIntoMinibuffer(0);
2221 simple_cut_buffer->Erase(0);
2222 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2226 simple_cut_buffer->CutIntoMinibuffer(0);
2227 simple_cut_buffer->Erase(0);
2228 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2234 /* table stuff -- end */
2235 // Some provisions should be done here for checking
2236 // if we are inserting at the beginning of a
2237 // paragraph. If there are a space at the beginning
2238 // of the text to insert and we are inserting at
2239 // the beginning of the paragraph the space should
2241 while (simple_cut_buffer->text.size()) {
2242 #ifdef FIX_DOUBLE_SPACE
2243 // This is an attempt to fix the
2244 // "never insert a space at the
2245 // beginning of a paragraph" problem.
2246 if (tmpcursor.pos == 0
2247 && simple_cut_buffer->IsLineSeparator(0)) {
2248 simple_cut_buffer->Erase(0);
2250 simple_cut_buffer->CutIntoMinibuffer(0);
2251 simple_cut_buffer->Erase(0);
2252 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2256 simple_cut_buffer->CutIntoMinibuffer(0);
2257 simple_cut_buffer->Erase(0);
2258 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2263 delete simple_cut_buffer;
2264 simple_cut_buffer = tmppar;
2265 endpar = tmpcursor.par->Next();
2269 // make a copy of the simple cut_buffer
2270 tmppar = simple_cut_buffer;
2271 LyXParagraph * simple_cut_clone = tmppar->Clone();
2272 LyXParagraph * tmppar2 = simple_cut_clone;
2273 if (cursor.par->footnoteflag){
2274 tmppar->footnoteflag = cursor.par->footnoteflag;
2275 tmppar->footnotekind = cursor.par->footnotekind;
2277 while (tmppar->next) {
2278 tmppar = tmppar->next;
2279 tmppar2->next = tmppar->Clone();
2280 tmppar2->next->previous = tmppar2;
2281 tmppar2 = tmppar2->next;
2282 if (cursor.par->footnoteflag){
2283 tmppar->footnoteflag = cursor.par->footnoteflag;
2284 tmppar->footnotekind = cursor.par->footnotekind;
2288 // make sure there is no class difference
2289 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2290 parameters->textclass,
2293 // make the simple_cut_buffer exactly the same layout than
2294 // the cursor paragraph
2295 simple_cut_buffer->MakeSameLayout(cursor.par);
2297 // find the end of the buffer
2298 LyXParagraph * lastbuffer = simple_cut_buffer;
2299 while (lastbuffer->Next())
2300 lastbuffer = lastbuffer->Next();
2302 // find the physical end of the buffer
2303 #ifdef WITH_WARNINGS
2304 #warning Explain this please.
2307 // Can someone explain to be why this is done a second time?
2309 lastbuffer = simple_cut_buffer;
2310 while (lastbuffer->Next())
2311 lastbuffer = lastbuffer->Next();
2313 #ifndef FIX_DOUBLE_SPACE
2314 // Please break behind a space, if there is one. The space
2315 // should be copied too.
2316 if (cursor.par->Last() > cursor.pos
2317 && cursor.par->IsLineSeparator(cursor.pos))
2320 bool paste_the_end = false;
2322 // open the paragraph for inserting the simple_cut_buffer
2324 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2325 cursor.par->BreakParagraphConservative(cursor.pos);
2326 paste_the_end = true;
2329 #ifndef FIX_DOUBLE_SPACE
2330 // be careful with double spaces
2331 if ((!cursor.par->Last()
2332 || cursor.par->IsLineSeparator(cursor.pos - 1)
2333 || cursor.par->IsNewline(cursor.pos - 1))
2334 && simple_cut_buffer->text.size()
2335 && simple_cut_buffer->IsLineSeparator(0))
2336 simple_cut_buffer->Erase(0);
2338 // set the end for redoing later
2339 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2342 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2343 cursor.par->ParFromPos(cursor.pos)->next;
2344 cursor.par->ParFromPos(cursor.pos)->next->previous =
2345 lastbuffer->ParFromPos(lastbuffer->Last());
2347 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2348 simple_cut_buffer->previous =
2349 cursor.par->ParFromPos(cursor.pos);
2351 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2352 lastbuffer = cursor.par;
2354 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2356 // store the new cursor position
2357 tmpcursor.par = lastbuffer;
2358 tmpcursor.pos = lastbuffer->Last();
2360 // maybe some pasting
2361 if (lastbuffer->Next() && paste_the_end) {
2362 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2363 #ifndef FIX_DOUBLE_SPACE
2364 // be careful with double spaces
2365 if ((!lastbuffer->Last()
2366 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2367 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2368 && lastbuffer->Next()->Last()
2369 && lastbuffer->Next()->IsLineSeparator(0))
2370 lastbuffer->Next()->Erase(0);
2372 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2374 } else if (!lastbuffer->Next()->Last()) {
2375 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2376 #ifndef FIX_DOUBLE_SPACE
2377 // be careful witth double spaces
2378 if ((!lastbuffer->Last()
2379 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2380 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2381 && lastbuffer->Next()->Last()
2382 && lastbuffer->Next()->IsLineSeparator(0))
2383 lastbuffer->Next()->Erase(0);
2385 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2387 } else if (!lastbuffer->Last()) {
2388 lastbuffer->MakeSameLayout(lastbuffer->next);
2389 #ifndef FIX_DOUBLE_SPACE
2390 // be careful witth double spaces
2391 if ((!lastbuffer->Last()
2392 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2393 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2394 && lastbuffer->Next()->Last()
2395 && lastbuffer->Next()->IsLineSeparator(0))
2396 lastbuffer->Next()->Erase(0);
2398 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2401 lastbuffer->Next()->ClearParagraph();
2404 // restore the simple cut buffer
2405 simple_cut_buffer = simple_cut_clone;
2408 RedoParagraphs(cursor, endpar);
2410 SetCursor(cursor.par, cursor.pos);
2413 sel_cursor = cursor;
2414 SetCursor(tmpcursor.par, tmpcursor.pos);
2416 UpdateCounters(cursor.row);
2420 // returns a pointer to the very first LyXParagraph
2421 LyXParagraph * LyXText::FirstParagraph() const
2423 return params->paragraph;
2427 // returns true if the specified string is at the specified position
2428 bool LyXText::IsStringInText(LyXParagraph * par,
2429 LyXParagraph::size_type pos,
2430 char const * str) const
2434 while (pos + i < par->Last() && str[i] &&
2435 str[i] == par->GetChar(pos + i)) {
2445 // sets the selection over the number of characters of string, no check!!
2446 void LyXText::SetSelectionOverString(char const * string)
2448 sel_cursor = cursor;
2449 for (int i = 0; string[i]; ++i)
2455 // simple replacing. The font of the first selected character is used
2456 void LyXText::ReplaceSelectionWithString(char const * str)
2461 if (!selection) { // create a dummy selection
2462 sel_end_cursor = cursor;
2463 sel_start_cursor = cursor;
2466 // Get font setting before we cut
2467 LyXParagraph::size_type pos = sel_end_cursor.pos;
2468 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2470 // Insert the new string
2471 for (int i = 0; str[i]; ++i) {
2472 sel_end_cursor.par->InsertChar(pos, str[i]);
2473 sel_end_cursor.par->SetFont(pos, font);
2477 // Cut the selection
2484 // if the string can be found: return true and set the cursor to
2486 bool LyXText::SearchForward(char const * str) const
2488 LyXParagraph * par = cursor.par;
2489 LyXParagraph::size_type pos = cursor.pos;
2490 while (par && !IsStringInText(par, pos, str)) {
2491 if (pos < par->Last() - 1)
2499 SetCursor(par, pos);
2507 bool LyXText::SearchBackward(char const * string) const
2509 LyXParagraph * par = cursor.par;
2510 int pos = cursor.pos;
2516 // We skip empty paragraphs (Asger)
2518 par = par->Previous();
2520 pos = par->Last() - 1;
2521 } while (par && pos < 0);
2523 } while (par && !IsStringInText(par, pos, string));
2526 SetCursor(par, pos);
2533 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2535 char * str = new char[text.size() + 1];
2536 copy(text.begin(), text.end(), str);
2537 str[text.size()] = '\0';
2543 // needed to insert the selection
2544 void LyXText::InsertStringA(char const * s)
2547 LyXParagraph * par = cursor.par;
2548 LyXParagraph::size_type pos = cursor.pos;
2549 LyXParagraph::size_type a = 0;
2551 LyXParagraph * endpar = cursor.par->Next();
2556 textclasslist.Style(parameters->textclass,
2557 cursor.par->GetLayout()).isEnvironment();
2558 // only to be sure, should not be neccessary
2561 // insert the string, don't insert doublespace
2562 string::size_type i = 0;
2563 while (i < str.length()) {
2564 if (str[i] != '\n') {
2566 && i + 1 < str.length() && str[i + 1] != ' '
2567 && pos && par->GetChar(pos - 1)!= ' ') {
2568 par->InsertChar(pos,' ');
2570 } else if (par->table) {
2571 if (str[i] == '\t') {
2572 while((pos < par->size()) &&
2573 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2575 if (pos < par->size())
2577 else // no more fields to fill skip the rest
2579 } else if ((str[i] != 13) &&
2580 ((str[i] & 127) >= ' ')) {
2581 par->InsertChar(pos, str[i]);
2584 } else if (str[i] == ' ') {
2585 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2587 } else if (str[i] == '\t') {
2588 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2589 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2592 } else if (str[i]!= 13 &&
2593 // Ignore unprintables
2594 (str[i] & 127) >= ' ') {
2595 par->InsertChar(pos, str[i]);
2600 if (i + 1 >= str.length()) {
2604 while((pos < par->size()) &&
2605 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2608 cell = NumberOfCell(par, pos);
2609 while((pos < par->size()) &&
2610 !(par->table->IsFirstCell(cell))) {
2611 while((pos < par->size()) &&
2612 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2615 cell = NumberOfCell(par, pos);
2617 if (pos >= par->size())
2618 // no more fields to fill skip the rest
2621 if (!par->text.size()) {
2622 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2625 par->BreakParagraph(pos, flag);
2633 RedoParagraphs(cursor, endpar);
2634 SetCursor(cursor.par, cursor.pos);
2635 sel_cursor = cursor;
2636 SetCursor(par, pos);
2641 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2643 char * str = new char[text.size() + 1];
2644 copy(text.begin(), text.end(), str);
2645 str[text.size()] = '\0';
2651 /* turns double-CR to single CR, others where converted into one blank and 13s
2652 * that are ignored .Double spaces are also converted into one. Spaces at
2653 * the beginning of a paragraph are forbidden. tabs are converted into one
2654 * space. then InsertStringA is called */
2655 void LyXText::InsertStringB(char const * s)
2658 LyXParagraph * par = cursor.par;
2659 string::size_type i = 1;
2660 while (i < str.length()) {
2661 if (str[i] == '\t' && !par->table)
2663 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2665 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2666 if (str[i + 1] != '\n') {
2667 if (str[i - 1] != ' ')
2672 while (i + 1 < str.length()
2673 && (str[i + 1] == ' '
2674 || str[i + 1] == '\t'
2675 || str[i + 1] == '\n'
2676 || str[i + 1] == 13)) {
2683 InsertStringA(str.c_str());
2687 bool LyXText::GotoNextError() const
2689 LyXCursor res = cursor;
2691 if (res.pos < res.par->Last() - 1) {
2695 res.par = res.par->Next();
2700 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2701 && res.par->GetInset(res.pos)->AutoDelete()));
2704 SetCursor(res.par, res.pos);
2711 bool LyXText::GotoNextNote() const
2713 LyXCursor res = cursor;
2715 if (res.pos < res.par->Last() - 1) {
2718 res.par = res.par->Next();
2723 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2724 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2727 SetCursor(res.par, res.pos);
2734 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2738 if (!par || class1 == class2)
2740 par = par->FirstPhysicalPar();
2742 string name = textclasslist.NameOfLayout(class1, par->layout);
2744 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2745 textclasslist.NumberOfLayout(class2, name);
2748 } else { // layout not found
2749 // use default layout "Standard" (0)
2754 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2756 string s = "Layout had to be changed from\n"
2757 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2758 + "\nbecause of class conversion from\n"
2759 + textclasslist.NameOfClass(class1) + " to "
2760 + textclasslist.NameOfClass(class2);
2761 InsetError * new_inset = new InsetError(s);
2762 par->InsertChar(0, LyXParagraph::META_INSET);
2763 par->InsertInset(0, new_inset);
2772 void LyXText::CheckParagraph(LyXParagraph * par,
2773 LyXParagraph::size_type pos)
2776 LyXCursor tmpcursor;
2778 /* table stuff -- begin*/
2781 CheckParagraphInTable(par, pos);
2784 /* table stuff -- end*/
2787 LyXParagraph::size_type z;
2788 Row * row = GetRow(par, pos, y);
2790 // is there a break one row above
2791 if (row->previous && row->previous->par == row->par) {
2792 z = NextBreakPoint(row->previous, paperwidth);
2793 if ( z >= row->pos) {
2794 // set the dimensions of the row above
2795 y -= row->previous->height;
2797 refresh_row = row->previous;
2798 status = LyXText::NEED_MORE_REFRESH;
2800 BreakAgain(row->previous);
2802 // set the cursor again. Otherwise
2803 // dangling pointers are possible
2804 SetCursor(cursor.par, cursor.pos);
2805 sel_cursor = cursor;
2810 int tmpheight = row->height;
2811 LyXParagraph::size_type tmplast = RowLast(row);
2816 if (row->height == tmpheight && RowLast(row) == tmplast)
2817 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2819 status = LyXText::NEED_MORE_REFRESH;
2821 // check the special right address boxes
2822 if (textclasslist.Style(parameters->textclass,
2823 par->GetLayout()).margintype
2824 == MARGIN_RIGHT_ADDRESS_BOX) {
2825 tmpcursor.par = par;
2826 tmpcursor.row = row;
2829 tmpcursor.x_fix = 0;
2830 tmpcursor.pos = pos;
2831 RedoDrawingOfParagraph(tmpcursor);
2836 // set the cursor again. Otherwise dangling pointers are possible
2837 // also set the selection
2841 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2842 sel_cursor = cursor;
2843 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2844 sel_start_cursor = cursor;
2845 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2846 sel_end_cursor = cursor;
2847 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2848 last_sel_cursor = cursor;
2851 SetCursorIntern(cursor.par, cursor.pos);
2855 // returns 0 if inset wasn't found
2856 int LyXText::UpdateInset(Inset * inset)
2858 // first check the current paragraph
2859 int pos = cursor.par->GetPositionOfInset(inset);
2861 CheckParagraph(cursor.par, pos);
2865 // check every paragraph
2867 LyXParagraph * par = FirstParagraph();
2869 // make sure the paragraph is open
2870 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2871 pos = par->GetPositionOfInset(inset);
2873 CheckParagraph(par, pos);
2884 void LyXText::SetCursor(LyXParagraph * par,
2885 LyXParagraph::size_type pos) const
2887 LyXCursor old_cursor = cursor;
2888 SetCursorIntern(par, pos);
2889 DeleteEmptyParagraphMechanism(old_cursor);
2893 void LyXText::SetCursorIntern(LyXParagraph * par,
2894 LyXParagraph::size_type pos) const
2899 LyXParagraph * tmppar;
2901 // correct the cursor position if impossible
2902 if (pos > par->Last()){
2903 tmppar = par->ParFromPos(pos);
2904 pos = par->PositionInParFromPos(pos);
2907 if (par->IsDummy() && par->previous &&
2908 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2909 while (par->previous &&
2910 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2911 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2912 par = par->previous ;
2913 if (par->IsDummy() &&
2914 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2915 pos += par->text.size() + 1;
2917 if (par->previous) {
2918 par = par->previous;
2920 pos += par->text.size() + 1;
2926 /* get the cursor y position in text */
2927 row = GetRow(par, pos, y);
2928 /* y is now the beginning of the cursor row */
2930 /* y is now the cursor baseline */
2933 /* now get the cursors x position */
2936 float fill_separator, fill_hfill, fill_label_hfill;
2937 left_margin = LabelEnd(row);
2938 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2939 LyXParagraph::size_type main_body =
2940 BeginningOfMainBody(row->par);
2941 /* table stuff -- begin*/
2942 if (row->par->table) {
2943 int cell = NumberOfCell(row->par, row->pos);
2945 x += row->par->table->GetBeginningOfTextInCell(cell);
2946 for (pos = row->pos; pos < cursor.pos; ++pos) {
2947 if (row->par->IsNewline(pos)) {
2948 x = x_old + row->par->table->WidthOfColumn(cell);
2951 x += row->par->table->GetBeginningOfTextInCell(cell);
2953 x += SingleWidth(row->par, pos);
2957 /* table stuff -- end*/
2959 for (pos = row->pos; pos < cursor.pos; ++pos) {
2960 if (pos && pos == main_body
2961 && !row->par->IsLineSeparator(pos - 1)) {
2962 x += GetFont(row->par, -2).stringWidth(
2963 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
2964 if (x < left_margin)
2968 x += SingleWidth(row->par, pos);
2969 if (HfillExpansion(row, pos)) {
2970 if (pos >= main_body)
2973 x += fill_label_hfill;
2975 else if (pos >= main_body && row->par->IsSeparator(pos)) {
2979 if (pos + 1 == main_body
2980 && row->par->IsLineSeparator(pos)) {
2981 x += GetFont(row->par, -2).stringWidth(
2982 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
2983 if (row->par->IsLineSeparator(pos))
2984 x -= SingleWidth(row->par, pos);
2985 if (x < left_margin)
2992 cursor.x_fix = cursor.x;
2996 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2997 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2998 && !cursor.par->IsSeparator(cursor.pos))
3000 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3001 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3003 current_font = cursor.par->GetFontSettings(cursor.pos);
3004 real_current_font = GetFont(cursor.par, cursor.pos);
3009 void LyXText::SetCursorFromCoordinates(int x, long y) const
3011 LyXCursor old_cursor = cursor;
3013 /* get the row first */
3015 Row * row = GetRowNearY(y);
3017 cursor.par = row->par;
3019 int column = GetColumnNearX(row, x);
3020 cursor.pos = row->pos + column;
3022 cursor.y = y + row->baseline;
3027 (cursor.pos == cursor.par->Last()
3028 || cursor.par->IsSeparator(cursor.pos)
3029 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3030 && !cursor.par->IsSeparator(cursor.pos))
3032 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3033 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3035 current_font = cursor.par->GetFontSettings(cursor.pos);
3036 real_current_font = GetFont(cursor.par, cursor.pos);
3038 DeleteEmptyParagraphMechanism(old_cursor);
3042 void LyXText::CursorLeft() const
3045 if (cursor.par->table) {
3046 int cell = NumberOfCell(cursor.par, cursor.pos);
3047 if (cursor.par->table->IsContRow(cell) &&
3048 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3055 void LyXText::CursorLeftIntern() const
3057 if (cursor.pos > 0) {
3058 SetCursor(cursor.par, cursor.pos - 1);
3060 else if (cursor.par->Previous()) {
3061 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3066 void LyXText::CursorRight() const
3068 CursorRightIntern();
3069 if (cursor.par->table) {
3070 int cell = NumberOfCell(cursor.par, cursor.pos);
3071 if (cursor.par->table->IsContRow(cell) &&
3072 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3079 void LyXText::CursorRightIntern() const
3081 if (cursor.pos < cursor.par->Last()) {
3082 SetCursor(cursor.par, cursor.pos + 1);
3084 else if (cursor.par->Next()) {
3085 SetCursor(cursor.par->Next(), 0);
3090 void LyXText::CursorUp() const
3092 SetCursorFromCoordinates(cursor.x_fix,
3093 cursor.y - cursor.row->baseline - 1);
3094 if (cursor.par->table) {
3095 int cell = NumberOfCell(cursor.par, cursor.pos);
3096 if (cursor.par->table->IsContRow(cell) &&
3097 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3104 void LyXText::CursorDown() const
3106 if (cursor.par->table &&
3107 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3110 SetCursorFromCoordinates(cursor.x_fix,
3111 cursor.y - cursor.row->baseline
3112 + cursor.row->height + 1);
3113 if (cursor.par->table) {
3114 int cell = NumberOfCell(cursor.par, cursor.pos);
3115 int cell_above = cursor.par->table->GetCellAbove(cell);
3116 while(cursor.par->table &&
3117 cursor.par->table->IsContRow(cell) &&
3118 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3119 SetCursorFromCoordinates(cursor.x_fix,
3120 cursor.y - cursor.row->baseline
3121 + cursor.row->height + 1);
3122 if (cursor.par->table) {
3123 cell = NumberOfCell(cursor.par, cursor.pos);
3124 cell_above = cursor.par->table->GetCellAbove(cell);
3131 void LyXText::CursorUpParagraph() const
3133 if (cursor.pos > 0) {
3134 SetCursor(cursor.par, 0);
3136 else if (cursor.par->Previous()) {
3137 SetCursor(cursor.par->Previous(), 0);
3142 void LyXText::CursorDownParagraph() const
3144 if (cursor.par->Next()) {
3145 SetCursor(cursor.par->Next(), 0);
3147 SetCursor(cursor.par, cursor.par->Last());
3153 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3155 bool deleted = false;
3157 // this is the delete-empty-paragraph-mechanism.
3158 if (selection) return;
3160 #ifdef FIX_DOUBLE_SPACE
3161 /* Ok I'll put some comments here about what is missing.
3162 I have fixed BackSpace (and thus Delete) to not delete
3163 double-spaces automagically. I have also changed Cut,
3164 Copy and Paste to hopefully do some sensible things.
3165 There are still some small problems that can lead to
3166 double spaces stored in the document file or space at
3167 the beginning of paragraphs. This happens if you have
3168 the cursor betwenn to spaces and then save. Or if you
3169 cut and paste and the selection have a space at the
3170 beginning and then save right after the paste. I am
3171 sure none of these are very hard to fix, but I will
3172 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3173 that I can get some feedback. (Lgb)
3176 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3177 // delete the LineSeparator.
3180 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3181 // delete the LineSeparator.
3184 // If the pos around the old_cursor were spaces, delete one of them.
3185 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3186 && old_cursor.pos > 0
3187 && old_cursor.pos < old_cursor.par->Last()
3188 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3189 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3190 old_cursor.par->Erase(old_cursor.pos - 1);
3191 RedoParagraphs(old_cursor, old_cursor.par->Next());
3192 // or RedoDrawingOfParagraph(old_cursor);
3194 if (old_cursor.par == cursor.par &&
3195 cursor.pos > old_cursor.pos)
3196 SetCursor(cursor.par, cursor.pos - 1);
3198 SetCursor(cursor.par, cursor.pos);
3203 // Paragraph should not be deleted if empty
3204 if ((textclasslist.Style(parameters->textclass,
3205 old_cursor.par->GetLayout())).keepempty)
3208 LyXCursor tmpcursor;
3210 if (old_cursor.par != cursor.par) {
3211 if ( (old_cursor.par->Last() == 0
3212 || (old_cursor.par->Last() == 1
3213 && (old_cursor.par->IsLineSeparator(0))))
3214 && old_cursor.par->FirstPhysicalPar()
3215 == old_cursor.par->LastPhysicalPar()) {
3217 // ok, we will delete anything
3219 // make sure that you do not delete any environments
3220 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3221 !(old_cursor.row->previous
3222 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3223 && !(old_cursor.row->next
3224 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3226 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3227 ((old_cursor.row->previous
3228 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3230 (old_cursor.row->next
3231 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3233 status = LyXText::NEED_MORE_REFRESH;
3236 if (old_cursor.row->previous) {
3237 refresh_row = old_cursor.row->previous;
3238 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3240 cursor = old_cursor; // that undo can restore the right cursor position
3241 LyXParagraph * endpar = old_cursor.par->next;
3242 if (endpar && endpar->GetDepth()) {
3243 while (endpar && endpar->GetDepth()) {
3244 endpar = endpar->LastPhysicalPar()->Next();
3247 SetUndo(Undo::DELETE,
3248 old_cursor.par->previous,
3253 RemoveRow(old_cursor.row);
3254 if (params->paragraph == old_cursor.par) {
3255 params->paragraph = params->paragraph->next;
3258 delete old_cursor.par;
3260 /* Breakagain the next par. Needed
3261 * because of the parindent that
3262 * can occur or dissappear. The
3263 * next row can change its height,
3264 * if there is another layout before */
3265 if (refresh_row->next) {
3266 BreakAgain(refresh_row->next);
3267 UpdateCounters(refresh_row);
3269 SetHeightOfRow(refresh_row);
3271 refresh_row = old_cursor.row->next;
3272 refresh_y = old_cursor.y - old_cursor.row->baseline;
3275 cursor = old_cursor; // that undo can restore the right cursor position
3276 LyXParagraph *endpar = old_cursor.par->next;
3277 if (endpar && endpar->GetDepth()) {
3278 while (endpar && endpar->GetDepth()) {
3279 endpar = endpar->LastPhysicalPar()->Next();
3282 SetUndo(Undo::DELETE,
3283 old_cursor.par->previous,
3288 RemoveRow(old_cursor.row);
3290 if (params->paragraph == old_cursor.par) {
3291 params->paragraph = params->paragraph->next;
3293 delete old_cursor.par;
3295 /* Breakagain the next par. Needed
3296 because of the parindent that can
3297 occur or dissappear.
3298 The next row can change its height,
3299 if there is another layout before
3302 BreakAgain(refresh_row);
3303 UpdateCounters(refresh_row->previous);
3308 SetCursor(cursor.par, cursor.pos);
3310 /* if (cursor.y > old_cursor.y)
3311 cursor.y -= old_cursor.row->height; */
3313 if (sel_cursor.par == old_cursor.par
3314 && sel_cursor.pos == sel_cursor.pos) {
3315 // correct selection
3316 sel_cursor = cursor;
3321 if (old_cursor.par->ClearParagraph()){
3322 RedoParagraphs(old_cursor, old_cursor.par->Next());
3324 SetCursor(cursor.par, cursor.pos);
3325 sel_cursor = cursor;
3330 else if (cursor.par->table && (cursor.row != old_cursor.row)) {
3331 int cell = NumberOfCell(old_cursor.par, old_cursor.pos);
3332 if (old_cursor.par->table->IsContRow(cell) &&
3333 IsEmptyTableRow(old_cursor)) {
3334 RemoveTableRow(const_cast<LyXCursor*>(&old_cursor));
3342 LyXParagraph * LyXText::GetParFromID(int id)
3344 LyXParagraph * result = FirstParagraph();
3345 while (result && result->id() != id)
3346 result = result->next;
3352 bool LyXText::TextUndo()
3354 // returns false if no undo possible
3355 Undo * undo = params->undostack.pop();
3360 .push(CreateUndo(undo->kind,
3361 GetParFromID(undo->number_of_before_par),
3362 GetParFromID(undo->number_of_behind_par)));
3364 return TextHandleUndo(undo);
3368 bool LyXText::TextRedo()
3370 // returns false if no redo possible
3371 Undo * undo = params->redostack.pop();
3376 .push(CreateUndo(undo->kind,
3377 GetParFromID(undo->number_of_before_par),
3378 GetParFromID(undo->number_of_behind_par)));
3380 return TextHandleUndo(undo);
3384 bool LyXText::TextHandleUndo(Undo * undo)
3386 // returns false if no undo possible
3387 bool result = false;
3389 LyXParagraph * before =
3390 GetParFromID(undo->number_of_before_par);
3391 LyXParagraph * behind =
3392 GetParFromID(undo->number_of_behind_par);
3393 LyXParagraph * tmppar;
3394 LyXParagraph * tmppar2;
3395 LyXParagraph * tmppar3;
3396 LyXParagraph * tmppar4;
3397 LyXParagraph * endpar;
3398 LyXParagraph * tmppar5;
3400 // if there's no before take the beginning
3401 // of the document for redoing
3403 SetCursorIntern(FirstParagraph(), 0);
3405 // replace the paragraphs with the undo informations
3407 tmppar3 = undo->par;
3408 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3411 while (tmppar4->next)
3412 tmppar4 = tmppar4->next;
3413 } // get last undo par
3415 // now remove the old text if there is any
3416 if (before != behind || (!behind && !before)){
3418 tmppar5 = before->next;
3420 tmppar5 = params->paragraph;
3422 while (tmppar5 && tmppar5 != behind){
3424 tmppar5 = tmppar5->next;
3425 // a memory optimization for edit: Only layout information
3426 // is stored in the undo. So restore the text informations.
3427 if (undo->kind == Undo::EDIT){
3428 tmppar2->text = tmppar->text;
3429 tmppar->text.clear();
3430 tmppar2 = tmppar2->next;
3432 if ( currentrow && currentrow->par == tmppar )
3433 currentrow = currentrow -> previous;
3434 // Commenting out this might remove the error
3435 // reported by Purify, but it might also
3436 // introduce a memory leak. We need to
3442 // put the new stuff in the list if there is one
3445 before->next = tmppar3;
3447 params->paragraph = tmppar3;
3448 tmppar3->previous = before;
3452 params->paragraph = behind;
3455 tmppar4->next = behind;
3457 behind->previous = tmppar4;
3461 // Set the cursor for redoing
3463 SetCursorIntern(before->FirstSelfrowPar(), 0);
3464 // check wether before points to a closed float and open it if necessary
3465 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3466 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3468 while (tmppar4->previous &&
3469 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3470 tmppar4 = tmppar4->previous;
3471 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3472 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3473 tmppar4 = tmppar4->next;
3478 // open a cosed footnote at the end if necessary
3479 if (behind && behind->previous &&
3480 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3481 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3482 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3483 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3484 behind = behind->next;
3488 // calculate the endpar for redoing the paragraphs.
3490 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3491 endpar = behind->LastPhysicalPar()->Next();
3493 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3498 tmppar = GetParFromID(undo->number_of_cursor_par);
3499 RedoParagraphs(cursor, endpar);
3501 SetCursorIntern(tmppar, undo->cursor_pos);
3502 UpdateCounters(cursor.row);
3512 void LyXText::FinishUndo()
3514 // makes sure the next operation will be stored
3515 undo_finished = True;
3519 void LyXText::FreezeUndo()
3521 // this is dangerous and for internal use only
3526 void LyXText::UnFreezeUndo()
3528 // this is dangerous and for internal use only
3529 undo_frozen = false;
3533 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3534 LyXParagraph const * behind) const
3537 params->undostack.push(CreateUndo(kind, before, behind));
3538 params->redostack.clear();
3542 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3543 LyXParagraph const * behind)
3545 params->redostack.push(CreateUndo(kind, before, behind));
3549 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3550 LyXParagraph const * behind) const
3552 int before_number = -1;
3553 int behind_number = -1;
3555 before_number = before->id();
3557 behind_number = behind->id();
3558 // Undo::EDIT and Undo::FINISH are
3559 // always finished. (no overlapping there)
3560 // overlapping only with insert and delete inside one paragraph:
3561 // Nobody wants all removed character
3562 // appear one by one when undoing.
3563 // EDIT is special since only layout information, not the
3564 // contents of a paragaph are stored.
3565 if (!undo_finished && kind != Undo::EDIT &&
3566 kind != Undo::FINISH){
3567 // check wether storing is needed
3568 if (!params->undostack.empty() &&
3569 params->undostack.top()->kind == kind &&
3570 params->undostack.top()->number_of_before_par == before_number &&
3571 params->undostack.top()->number_of_behind_par == behind_number ){
3576 // create a new Undo
3577 LyXParagraph * undopar;
3578 LyXParagraph * tmppar;
3579 LyXParagraph * tmppar2;
3581 LyXParagraph * start = 0;
3582 LyXParagraph * end = 0;
3585 start = before->next;
3587 start = FirstParagraph();
3589 end = behind->previous;
3591 end = FirstParagraph();
3597 && start != end->next
3598 && (before != behind || (!before && !behind))) {
3600 tmppar2 = tmppar->Clone();
3601 tmppar2->id(tmppar->id());
3603 // a memory optimization: Just store the layout information
3605 if (kind == Undo::EDIT){
3606 tmppar2->text.clear();
3611 while (tmppar != end && tmppar->next) {
3612 tmppar = tmppar->next;
3613 tmppar2->next = tmppar->Clone();
3614 tmppar2->next->id(tmppar->id());
3615 // a memory optimization: Just store the layout
3616 // information when only edit
3617 if (kind == Undo::EDIT){
3618 tmppar2->next->text.clear();
3620 tmppar2->next->previous = tmppar2;
3621 tmppar2 = tmppar2->next;
3625 undopar = 0; // nothing to replace (undo of delete maybe)
3627 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3628 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3630 Undo * undo = new Undo(kind,
3631 before_number, behind_number,
3632 cursor_par, cursor_pos,
3635 undo_finished = false;
3640 void LyXText::SetCursorParUndo()
3642 SetUndo(Undo::FINISH,
3643 cursor.par->ParFromPos(cursor.pos)->previous,
3644 cursor.par->ParFromPos(cursor.pos)->next);
3648 void LyXText::RemoveTableRow(LyXCursor * cur) const
3654 // move to the previous row
3655 int cell_act = NumberOfCell(cur->par, cur->pos);
3658 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3661 !cur->par->table->IsFirstCell(cell_act)) {
3663 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3668 // now we have to pay attention if the actual table is the
3669 // main row of TableContRows and if yes to delete all of them
3674 // delete up to the next row
3675 while (cur->pos < cur->par->Last() &&
3677 || !cur->par->table->IsFirstCell(cell_act))) {
3678 while (cur->pos < cur->par->Last() &&
3679 !cur->par->IsNewline(cur->pos))
3680 cur->par->Erase(cur->pos);
3683 if (cur->pos < cur->par->Last())
3684 cur->par->Erase(cur->pos);
3686 if (cur->pos && cur->pos == cur->par->Last()) {
3688 cur->par->Erase(cur->pos); // no newline at very end!
3690 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3691 !cur->par->table->IsContRow(cell_org) &&
3692 cur->par->table->IsContRow(cell));
3693 cur->par->table->DeleteRow(cell_org);
3699 bool LyXText::IsEmptyTableRow(LyXCursor const & old_cursor) const
3701 if (!old_cursor.par->table)
3703 #ifdef I_DONT_KNOW_IF_I_SHOULD_DO_THIS
3704 int pos = old_cursor.pos;
3705 int cell = NumberOfCell(old_cursor.par, pos);
3707 // search first charater of this table row
3708 while (pos && !old_cursor.par->table->IsFirstCell(cell)) {
3710 while (pos && !old_cursor.par->IsNewline(pos-1))
3714 if (!old_cursor.par->IsNewline(pos))
3718 while ((pos < old_cursor.par->Last()) &&
3719 !old_cursor.par->table->IsFirstCell(cell)) {
3720 if (!old_cursor.par->IsNewline(pos))
3732 bool LyXText::IsEmptyTableCell() const
3734 LyXParagraph::size_type pos = cursor.pos - 1;
3735 while (pos >= 0 && pos < cursor.par->Last()
3736 && !cursor.par->IsNewline(pos))
3738 return cursor.par->IsNewline(pos + 1);
3742 void LyXText::toggleAppendix(){
3743 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3744 bool start = !par->start_of_appendix;
3746 // ensure that we have only one start_of_appendix in this document
3747 LyXParagraph * tmp = FirstParagraph();
3748 for (; tmp; tmp = tmp->next)
3749 tmp->start_of_appendix = 0;
3750 par->start_of_appendix = start;
3752 // we can set the refreshing parameters now
3753 status = LyXText::NEED_MORE_REFRESH;
3755 refresh_row = 0; // not needed for full update
3757 SetCursor(cursor.par, cursor.pos);