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.par == cursor.par)
941 if (sel_cursor.pos < cursor.pos) {
942 sel_end_cursor = cursor;
943 sel_start_cursor = sel_cursor;
945 sel_end_cursor = sel_cursor;
946 sel_start_cursor = cursor;
948 else if (sel_cursor.y < cursor.y ||
949 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
950 sel_end_cursor = cursor;
951 sel_start_cursor = sel_cursor;
954 sel_end_cursor = sel_cursor;
955 sel_start_cursor = cursor;
958 // a selection with no contents is not a selection
959 if (sel_start_cursor.x == sel_end_cursor.x &&
960 sel_start_cursor.y == sel_end_cursor.y)
965 void LyXText::ClearSelection() const
972 void LyXText::CursorHome() const
974 SetCursor(cursor.par, cursor.row->pos);
978 void LyXText::CursorEnd() const
980 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
981 SetCursor(cursor.par, RowLast(cursor.row) + 1);
983 if (cursor.par->Last() &&
984 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
985 || cursor.par->IsNewline(RowLast(cursor.row))))
986 SetCursor(cursor.par, RowLast(cursor.row));
988 SetCursor(cursor.par, RowLast(cursor.row) + 1);
990 if (cursor.par->table) {
991 int cell = NumberOfCell(cursor.par, cursor.pos);
992 if (cursor.par->table->RowHasContRow(cell) &&
993 cursor.par->table->CellHasContRow(cell)<0) {
994 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
995 SetCursor(cursor.par, RowLast(cursor.row) + 1);
997 if (cursor.par->Last() &&
998 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
999 || cursor.par->IsNewline(RowLast(cursor.row))))
1000 SetCursor(cursor.par, RowLast(cursor.row));
1002 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1009 void LyXText::CursorTop() const
1011 while (cursor.par->Previous())
1012 cursor.par = cursor.par->Previous();
1013 SetCursor(cursor.par, 0);
1017 void LyXText::CursorBottom() const
1019 while (cursor.par->Next())
1020 cursor.par = cursor.par->Next();
1021 SetCursor(cursor.par, cursor.par->Last());
1025 /* returns a pointer to the row near the specified y-coordinate
1026 * (relative to the whole text). y is set to the real beginning
1028 Row * LyXText::GetRowNearY(long & y) const
1034 tmprow = currentrow;
1035 tmpy = currentrow_y;
1042 while (tmprow->next && tmpy + tmprow->height <= y) {
1043 tmpy += tmprow->height;
1044 tmprow = tmprow->next;
1047 while (tmprow->previous && tmpy > y) {
1048 tmprow = tmprow->previous;
1049 tmpy -= tmprow->height;
1052 currentrow = tmprow;
1053 currentrow_y = tmpy;
1055 y = tmpy; // return the real y
1060 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1062 // If the mask is completely neutral, tell user
1063 if (font == LyXFont(LyXFont::ALL_IGNORE)){
1064 // Could only happen with user style
1065 current_view->owner()->getMiniBuffer()
1066 ->Set(_("No font change defined. Use Character under"
1067 " the Layout menu to define font change."));
1071 // Try implicit word selection
1072 LyXCursor resetCursor = cursor;
1073 int implicitSelection = SelectWordWhenUnderCursor();
1076 SetFont(font, toggleall);
1078 /* Implicit selections are cleared afterwards and cursor is set to the
1079 original position. */
1080 if (implicitSelection) {
1082 cursor = resetCursor;
1083 SetCursor( cursor.par, cursor.pos );
1084 sel_cursor = cursor;
1089 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1091 if (textclasslist.Style(parameters->textclass,
1092 par->GetLayout()).labeltype != LABEL_MANUAL)
1095 return par->BeginningOfMainBody();
1099 /* if there is a selection, reset every environment you can find
1100 * in the selection, otherwise just the environment you are in */
1101 void LyXText::MeltFootnoteEnvironment()
1103 LyXParagraph * tmppar, * firsttmppar;
1107 /* is is only allowed, if the cursor is IN an open footnote.
1108 * Otherwise it is too dangerous */
1109 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1112 SetUndo(Undo::FINISH,
1113 cursor.par->PreviousBeforeFootnote()->previous,
1114 cursor.par->NextAfterFootnote()->next);
1116 /* ok, move to the beginning of the footnote. */
1117 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1118 cursor.par = cursor.par->Previous();
1120 SetCursor(cursor.par, cursor.par->Last());
1121 /* this is just faster than using CursorLeft(); */
1123 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1124 tmppar = firsttmppar;
1125 /* tmppar is now the paragraph right before the footnote */
1127 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1130 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1131 tmppar = tmppar->next; /* I use next instead of Next(),
1132 * because there cannot be any
1133 * footnotes in a footnote
1135 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1137 /* remember the captions and empty paragraphs */
1138 if ((textclasslist.Style(parameters->textclass,
1139 tmppar->GetLayout())
1140 .labeltype == LABEL_SENSITIVE)
1142 tmppar->SetLayout(0);
1145 // now we will paste the ex-footnote, if the layouts allow it
1146 // first restore the layout of the paragraph right behind
1149 tmppar->next->MakeSameLayout(cursor.par);
1152 if ((!tmppar->GetLayout() && !tmppar->table)
1154 && (!tmppar->Next()->Last()
1155 || tmppar->Next()->HasSameLayout(tmppar)))) {
1156 if (tmppar->Next()->Last()
1157 && tmppar->Next()->IsLineSeparator(0))
1158 tmppar->Next()->Erase(0);
1159 tmppar->PasteParagraph();
1162 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1163 * by the pasting of the beginning */
1165 /* then the beginning */
1166 /* if there is no space between the text and the footnote, so we insert
1168 * (only if the previous par and the footnotepar are not empty!) */
1169 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1170 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1171 if (firsttmppar->text.size()
1172 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1173 && first_footnote_par_is_not_empty) {
1174 firsttmppar->next->InsertChar(0, ' ');
1176 firsttmppar->PasteParagraph();
1179 /* now redo the paragaphs */
1180 RedoParagraphs(cursor, tmppar);
1182 SetCursor(cursor.par, cursor.pos);
1184 /* sometimes it can happen, that there is a counter change */
1185 Row * row = cursor.row;
1186 while (row->next && row->par != tmppar && row->next->par != tmppar)
1188 UpdateCounters(row);
1195 /* the DTP switches for paragraphs. LyX will store them in the
1196 * first physicla paragraph. When a paragraph is broken, the top settings
1197 * rest, the bottom settings are given to the new one. So I can make shure,
1198 * they do not duplicate themself and you cannnot make dirty things with
1201 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1202 bool pagebreak_top, bool pagebreak_bottom,
1203 VSpace const & space_top,
1204 VSpace const & space_bottom,
1206 string labelwidthstring,
1209 LyXCursor tmpcursor = cursor;
1211 sel_start_cursor = cursor;
1212 sel_end_cursor = cursor;
1215 // make sure that the depth behind the selection are restored, too
1216 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1217 LyXParagraph * undoendpar = endpar;
1219 if (endpar && endpar->GetDepth()) {
1220 while (endpar && endpar->GetDepth()) {
1221 endpar = endpar->LastPhysicalPar()->Next();
1222 undoendpar = endpar;
1226 endpar = endpar->Next(); // because of parindents etc.
1231 .par->ParFromPos(sel_start_cursor.pos)->previous,
1235 LyXParagraph * tmppar = sel_end_cursor.par;
1236 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1237 SetCursor(tmppar->FirstPhysicalPar(), 0);
1238 status = LyXText::NEED_MORE_REFRESH;
1239 refresh_row = cursor.row;
1240 refresh_y = cursor.y - cursor.row->baseline;
1241 if (cursor.par->footnoteflag ==
1242 sel_start_cursor.par->footnoteflag) {
1243 cursor.par->line_top = line_top;
1244 cursor.par->line_bottom = line_bottom;
1245 cursor.par->pagebreak_top = pagebreak_top;
1246 cursor.par->pagebreak_bottom = pagebreak_bottom;
1247 cursor.par->added_space_top = space_top;
1248 cursor.par->added_space_bottom = space_bottom;
1249 // does the layout allow the new alignment?
1250 if (align == LYX_ALIGN_LAYOUT)
1251 align = textclasslist
1252 .Style(parameters->textclass,
1253 cursor.par->GetLayout()).align;
1254 if (align & textclasslist
1255 .Style(parameters->textclass,
1256 cursor.par->GetLayout()).alignpossible) {
1257 if (align == textclasslist
1258 .Style(parameters->textclass,
1259 cursor.par->GetLayout()).align)
1260 cursor.par->align = LYX_ALIGN_LAYOUT;
1262 cursor.par->align = align;
1264 cursor.par->SetLabelWidthString(labelwidthstring);
1265 cursor.par->noindent = noindent;
1268 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1271 RedoParagraphs(sel_start_cursor, endpar);
1274 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1275 sel_cursor = cursor;
1276 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1278 SetCursor(tmpcursor.par, tmpcursor.pos);
1282 void LyXText::SetParagraphExtraOpt(int type,
1284 char const * widthp,
1285 int alignment, bool hfill,
1286 bool start_minipage)
1288 LyXCursor tmpcursor = cursor;
1289 LyXParagraph * tmppar;
1291 sel_start_cursor = cursor;
1292 sel_end_cursor = cursor;
1295 // make sure that the depth behind the selection are restored, too
1296 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1297 LyXParagraph * undoendpar = endpar;
1299 if (endpar && endpar->GetDepth()) {
1300 while (endpar && endpar->GetDepth()) {
1301 endpar = endpar->LastPhysicalPar()->Next();
1302 undoendpar = endpar;
1306 endpar = endpar->Next(); // because of parindents etc.
1311 .par->ParFromPos(sel_start_cursor.pos)->previous,
1314 tmppar = sel_end_cursor.par;
1315 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1316 SetCursor(tmppar->FirstPhysicalPar(), 0);
1317 status = LyXText::NEED_MORE_REFRESH;
1318 refresh_row = cursor.row;
1319 refresh_y = cursor.y - cursor.row->baseline;
1320 if (cursor.par->footnoteflag ==
1321 sel_start_cursor.par->footnoteflag) {
1322 if (type == LyXParagraph::PEXTRA_NONE) {
1323 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1324 cursor.par->UnsetPExtraType();
1325 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1328 cursor.par->SetPExtraType(type, width, widthp);
1329 cursor.par->pextra_hfill = hfill;
1330 cursor.par->pextra_start_minipage = start_minipage;
1331 cursor.par->pextra_alignment = alignment;
1334 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1336 RedoParagraphs(sel_start_cursor, endpar);
1338 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1339 sel_cursor = cursor;
1340 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1342 SetCursor(tmpcursor.par, tmpcursor.pos);
1346 static char const * alphaCounter(int n) {
1347 static char result[2];
1352 result[0] = 'A' + n;
1360 // set the counter of a paragraph. This includes the labels
1361 void LyXText::SetCounter(LyXParagraph * par) const
1363 // this is only relevant for the beginning of paragraph
1364 par = par->FirstPhysicalPar();
1366 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1369 LyXTextClass const & textclass =
1370 textclasslist.TextClass(parameters->textclass);
1372 /* copy the prev-counters to this one, unless this is the start of a
1373 footnote or of a bibliography or the very first paragraph */
1375 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1376 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1377 && par->footnotekind == LyXParagraph::FOOTNOTE)
1378 && !(textclasslist.Style(parameters->textclass,
1379 par->Previous()->GetLayout()
1380 ).labeltype != LABEL_BIBLIO
1381 && layout.labeltype == LABEL_BIBLIO)) {
1382 for (int i = 0; i < 10; ++i) {
1383 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1385 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1386 if (!par->appendix && par->start_of_appendix){
1387 par->appendix = true;
1388 for (int i = 0; i < 10; ++i) {
1389 par->setCounter(i, 0);
1392 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1393 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1396 for (int i = 0; i < 10; ++i) {
1397 par->setCounter(i, 0);
1399 par->appendix = par->start_of_appendix;
1404 // if this is an open marginnote and this is the first
1405 // entry in the marginnote and the enclosing
1406 // environment is an enum/item then correct for the
1407 // LaTeX behaviour (ARRae)
1408 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1409 && par->footnotekind == LyXParagraph::MARGIN
1411 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1412 && (par->PreviousBeforeFootnote()
1413 && textclasslist.Style(parameters->textclass,
1414 par->PreviousBeforeFootnote()->GetLayout()
1415 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1416 // Any itemize or enumerate environment in a marginnote
1417 // that is embedded in an itemize or enumerate
1418 // paragraph is seen by LaTeX as being at a deeper
1419 // level within that enclosing itemization/enumeration
1420 // even if there is a "standard" layout at the start of
1426 /* Maybe we have to increment the enumeration depth.
1427 * BUT, enumeration in a footnote is considered in isolation from its
1428 * surrounding paragraph so don't increment if this is the
1429 * first line of the footnote
1430 * AND, bibliographies can't have their depth changed ie. they
1431 * are always of depth 0
1434 && par->Previous()->GetDepth() < par->GetDepth()
1435 && textclasslist.Style(parameters->textclass,
1436 par->Previous()->GetLayout()
1437 ).labeltype == LABEL_COUNTER_ENUMI
1438 && par->enumdepth < 3
1439 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1440 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1441 && par->footnotekind == LyXParagraph::FOOTNOTE)
1442 && layout.labeltype != LABEL_BIBLIO) {
1446 /* Maybe we have to decrement the enumeration depth, see note above */
1448 && par->Previous()->GetDepth() > par->GetDepth()
1449 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1450 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1451 && par->footnotekind == LyXParagraph::FOOTNOTE)
1452 && layout.labeltype != LABEL_BIBLIO) {
1453 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1454 par->setCounter(6 + par->enumdepth,
1455 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1456 /* reset the counters.
1457 * A depth change is like a breaking layout
1459 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1460 par->setCounter(i, 0);
1463 if (!par->labelstring.empty()) {
1464 par->labelstring.clear();
1467 if (layout.margintype == MARGIN_MANUAL) {
1468 if (par->labelwidthstring.empty()) {
1469 par->SetLabelWidthString(layout.labelstring());
1473 par->SetLabelWidthString(string());
1476 /* is it a layout that has an automatic label ? */
1477 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1479 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1480 if (i >= 0 && i<= parameters->secnumdepth) {
1481 par->incCounter(i); // increment the counter
1483 char * s = new char[50];
1485 // Is there a label? Useful for Chapter layout
1486 if (!par->appendix){
1487 if (!layout.labelstring().empty())
1488 par->labelstring = layout.labelstring();
1490 par->labelstring.clear();
1492 if (!layout.labelstring_appendix().empty())
1493 par->labelstring = layout.labelstring_appendix();
1495 par->labelstring.clear();
1498 if (!par->appendix){
1499 switch (2 * LABEL_FIRST_COUNTER -
1500 textclass.maxcounter() + i) {
1501 case LABEL_COUNTER_CHAPTER:
1503 par->getCounter(i));
1505 case LABEL_COUNTER_SECTION:
1507 par->getCounter(i - 1),
1508 par->getCounter(i));
1510 case LABEL_COUNTER_SUBSECTION:
1511 sprintf(s, "%d.%d.%d",
1512 par->getCounter(i-2),
1513 par->getCounter(i-1),
1514 par->getCounter(i));
1516 case LABEL_COUNTER_SUBSUBSECTION:
1517 sprintf(s, "%d.%d.%d.%d",
1518 par->getCounter(i-3),
1519 par->getCounter(i-2),
1520 par->getCounter(i-1),
1521 par->getCounter(i));
1523 case LABEL_COUNTER_PARAGRAPH:
1524 sprintf(s, "%d.%d.%d.%d.%d",
1525 par->getCounter(i-4),
1526 par->getCounter(i-3),
1527 par->getCounter(i-2),
1528 par->getCounter(i-1),
1529 par->getCounter(i));
1531 case LABEL_COUNTER_SUBPARAGRAPH:
1532 sprintf(s, "%d.%d.%d.%d.%d.%d",
1533 par->getCounter(i-5),
1534 par->getCounter(i-4),
1535 par->getCounter(i-3),
1536 par->getCounter(i-2),
1537 par->getCounter(i-1),
1538 par->getCounter(i));
1541 sprintf(s, "%d.", par->getCounter(i));
1545 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1546 case LABEL_COUNTER_CHAPTER:
1548 alphaCounter(par->getCounter(i)));
1550 case LABEL_COUNTER_SECTION:
1552 alphaCounter(par->getCounter(i - 1)),
1553 par->getCounter(i));
1555 case LABEL_COUNTER_SUBSECTION:
1556 sprintf(s, "%s.%d.%d",
1557 alphaCounter(par->getCounter(i-2)),
1558 par->getCounter(i-1),
1559 par->getCounter(i));
1561 case LABEL_COUNTER_SUBSUBSECTION:
1562 sprintf(s, "%s.%d.%d.%d",
1563 alphaCounter(par->getCounter(i-3)),
1564 par->getCounter(i-2),
1565 par->getCounter(i-1),
1566 par->getCounter(i));
1568 case LABEL_COUNTER_PARAGRAPH:
1569 sprintf(s, "%s.%d.%d.%d.%d",
1570 alphaCounter(par->getCounter(i-4)),
1571 par->getCounter(i-3),
1572 par->getCounter(i-2),
1573 par->getCounter(i-1),
1574 par->getCounter(i));
1576 case LABEL_COUNTER_SUBPARAGRAPH:
1577 sprintf(s, "%s.%d.%d.%d.%d.%d",
1578 alphaCounter(par->getCounter(i-5)),
1579 par->getCounter(i-4),
1580 par->getCounter(i-3),
1581 par->getCounter(i-2),
1582 par->getCounter(i-1),
1583 par->getCounter(i));
1586 sprintf(s, "%c.", par->getCounter(i));
1591 par->labelstring += s;
1594 for (i++; i < 10; ++i) {
1595 // reset the following counters
1596 par->setCounter(i, 0);
1598 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1599 for (i++; i < 10; ++i) {
1600 // reset the following counters
1601 par->setCounter(i, 0);
1603 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1604 par->incCounter(i + par->enumdepth);
1605 char * s = new char[25];
1606 int number = par->getCounter(i + par->enumdepth);
1608 static const char *roman[20] = {
1609 "i", "ii", "iii", "iv", "v",
1610 "vi", "vii", "viii", "ix", "x",
1611 "xi", "xii", "xiii", "xiv", "xv",
1612 "xvi", "xvii", "xviii", "xix", "xx"
1614 static const char hebrew[22] = {
1615 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1616 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1617 '÷', 'ø', 'ù', 'ú'
1620 switch (par->enumdepth) {
1622 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1623 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1625 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1628 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1629 sprintf(s, "%s.", roman[(number-1) % 20]);
1631 sprintf(s, ".%s", roman[(number-1) % 20]);
1634 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1635 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1637 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1640 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1641 sprintf(s, "%d.", number);
1643 sprintf(s, ".%d", number);
1646 par->labelstring = s;
1649 for (i += par->enumdepth + 1; i < 10; ++i)
1650 par->setCounter(i, 0); /* reset the following counters */
1653 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1654 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1656 int number = par->getCounter(i);
1658 par->bibkey = new InsetBibKey();
1659 par->bibkey->setCounter(number);
1660 par->labelstring = layout.labelstring();
1662 // In biblio should't be following counters but...
1664 string s = layout.labelstring();
1666 // the caption hack:
1668 if (layout.labeltype == LABEL_SENSITIVE) {
1669 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1670 && (par->footnotekind == LyXParagraph::FIG
1671 || par->footnotekind == LyXParagraph::WIDE_FIG))
1672 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1676 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1677 && (par->footnotekind == LyXParagraph::TAB
1678 || par->footnotekind == LyXParagraph::WIDE_TAB))
1679 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1683 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1684 && par->footnotekind == LyXParagraph::ALGORITHM)
1685 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1688 s = ":Ãúéøåâìà ";
1690 /* par->SetLayout(0);
1691 s = layout->labelstring; */
1692 if (GetParDirection(par) == LYX_DIR_LEFT_TO_RIGHT)
1695 s = " :úåòîùî øñç";
1699 par->labelstring = s;
1701 /* reset the enumeration counter. They are always resetted
1702 * when there is any other layout between */
1703 for (int i = 6 + par->enumdepth; i < 10; ++i)
1704 par->setCounter(i, 0);
1709 /* Updates all counters BEHIND the row. Changed paragraphs
1710 * with a dynamic left margin will be rebroken. */
1711 void LyXText::UpdateCounters(Row * row) const
1720 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1721 par = row->par->LastPhysicalPar()->Next();
1723 par = row->par->next;
1728 while (row->par != par)
1733 /* now check for the headline layouts. remember that they
1734 * have a dynamic left margin */
1736 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1737 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1740 /* Rebreak the paragraph */
1741 RemoveParagraph(row);
1742 AppendParagraph(row);
1744 /* think about the damned open footnotes! */
1745 while (par->Next() &&
1746 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1747 || par->Next()->IsDummy())){
1749 if (par->IsDummy()) {
1750 while (row->par != par)
1752 RemoveParagraph(row);
1753 AppendParagraph(row);
1758 par = par->LastPhysicalPar()->Next();
1764 /* insets an inset. */
1765 void LyXText::InsertInset(Inset *inset)
1767 SetUndo(Undo::INSERT,
1768 cursor.par->ParFromPos(cursor.pos)->previous,
1769 cursor.par->ParFromPos(cursor.pos)->next);
1770 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1771 cursor.par->InsertInset(cursor.pos, inset);
1772 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1773 * The character will not be inserted a
1778 // this is for the simple cut and paste mechanism
1779 static LyXParagraph * simple_cut_buffer = 0;
1780 static char simple_cut_buffer_textclass = 0;
1782 void DeleteSimpleCutBuffer()
1784 if (!simple_cut_buffer)
1786 LyXParagraph * tmppar;
1788 while (simple_cut_buffer) {
1789 tmppar = simple_cut_buffer;
1790 simple_cut_buffer = simple_cut_buffer->next;
1793 simple_cut_buffer = 0;
1797 void LyXText::copyEnvironmentType()
1799 copylayouttype = cursor.par->GetLayout();
1803 void LyXText::pasteEnvironmentType()
1805 SetLayout(copylayouttype);
1809 void LyXText::CutSelection(bool doclear)
1811 // This doesn't make sense, if there is no selection
1815 // OK, we have a selection. This is always between sel_start_cursor
1816 // and sel_end cursor
1817 LyXParagraph * tmppar;
1819 // Check whether there are half footnotes in the selection
1820 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1821 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1822 tmppar = sel_start_cursor.par;
1823 while (tmppar != sel_end_cursor.par){
1824 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1825 WriteAlert(_("Impossible operation"),
1826 _("Don't know what to do with half floats."),
1830 tmppar = tmppar->Next();
1834 /* table stuff -- begin */
1835 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1836 if ( sel_start_cursor.par != sel_end_cursor.par) {
1837 WriteAlert(_("Impossible operation"),
1838 _("Don't know what to do with half tables."),
1842 sel_start_cursor.par->table->Reinit();
1844 /* table stuff -- end */
1846 // make sure that the depth behind the selection are restored, too
1847 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1848 LyXParagraph * undoendpar = endpar;
1850 if (endpar && endpar->GetDepth()) {
1851 while (endpar && endpar->GetDepth()) {
1852 endpar = endpar->LastPhysicalPar()->Next();
1853 undoendpar = endpar;
1855 } else if (endpar) {
1856 endpar = endpar->Next(); // because of parindents etc.
1859 SetUndo(Undo::DELETE,
1861 .par->ParFromPos(sel_start_cursor.pos)->previous,
1864 // clear the simple_cut_buffer
1865 DeleteSimpleCutBuffer();
1867 // set the textclass
1868 simple_cut_buffer_textclass = parameters->textclass;
1870 #ifdef WITH_WARNINGS
1871 #warning Asger: Make cut more intelligent here.
1874 White paper for "intelligent" cutting:
1876 Example: "This is our text."
1877 Using " our " as selection, cutting will give "This istext.".
1878 Using "our" as selection, cutting will give "This is text.".
1879 Using " our" as selection, cutting will give "This is text.".
1880 Using "our " as selection, cutting will give "This is text.".
1882 All those four selections will (however) paste identically:
1883 Pasting with the cursor right after the "is" will give the
1884 original text with all four selections.
1886 The rationale is to be intelligent such that words are copied,
1887 cut and pasted in a functional manner.
1889 This is not implemented yet. (Asger)
1891 The changes below sees to do a lot of what you want. However
1892 I have not verified that all cases work as they should:
1894 - cut in multiple row
1896 - cut across footnotes and paragraph
1897 My simplistic tests show that the idea are basically sound but
1898 there are some items to fix up...we only need to find them
1901 As do redo Asger's example above (with | beeing the cursor in the
1902 result after cutting.):
1904 Example: "This is our text."
1905 Using " our " as selection, cutting will give "This is|text.".
1906 Using "our" as selection, cutting will give "This is | text.".
1907 Using " our" as selection, cutting will give "This is| text.".
1908 Using "our " as selection, cutting will give "This is |text.".
1913 #ifndef FIX_DOUBLE_SPACE
1914 bool space_wrapped =
1915 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1916 if (sel_end_cursor.pos > 0
1917 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1918 // please break before a space at the end
1919 sel_end_cursor.pos--;
1920 space_wrapped = true;
1922 // cut behind a space if there is one
1923 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1924 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1925 && (sel_start_cursor.par != sel_end_cursor.par
1926 || sel_start_cursor.pos < sel_end_cursor.pos))
1927 sel_start_cursor.pos++;
1929 // there are two cases: cut only within one paragraph or
1930 // more than one paragraph
1932 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1933 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1934 // only within one paragraph
1935 simple_cut_buffer = new LyXParagraph;
1936 LyXParagraph::size_type i =
1937 sel_start_cursor.pos;
1938 for (; i < sel_end_cursor.pos; ++i) {
1939 /* table stuff -- begin */
1940 if (sel_start_cursor.par->table
1941 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1942 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1943 sel_start_cursor.pos++;
1945 /* table stuff -- end */
1946 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1947 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1949 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1951 #ifndef FIX_DOUBLE_SPACE
1952 // check for double spaces
1953 if (sel_start_cursor.pos &&
1954 sel_start_cursor.par->Last() > sel_start_cursor.pos
1955 && sel_start_cursor.par
1956 ->IsLineSeparator(sel_start_cursor.pos - 1)
1957 && sel_start_cursor.par
1958 ->IsLineSeparator(sel_start_cursor.pos)) {
1959 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1962 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1965 endpar = sel_end_cursor.par->Next();
1967 // cut more than one paragraph
1970 ->BreakParagraphConservative(sel_end_cursor.pos);
1971 #ifndef FIX_DOUBLE_SPACE
1972 // insert a space at the end if there was one
1975 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1977 sel_end_cursor.par = sel_end_cursor.par->Next();
1978 sel_end_cursor.pos = 0;
1980 cursor = sel_end_cursor;
1982 #ifndef FIX_DOUBLE_SPACE
1983 // please break behind a space, if there is one.
1984 // The space should be copied too
1985 if (sel_start_cursor.par
1986 ->IsLineSeparator(sel_start_cursor.pos))
1987 sel_start_cursor.pos++;
1989 sel_start_cursor.par
1990 ->BreakParagraphConservative(sel_start_cursor.pos);
1991 #ifndef FIX_DOUBLE_SPACE
1992 if (!sel_start_cursor.pos
1993 || sel_start_cursor.par
1994 ->IsLineSeparator(sel_start_cursor.pos - 1)
1995 || sel_start_cursor.par
1996 ->IsNewline(sel_start_cursor.pos - 1)) {
1997 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2000 // store the endparagraph for redoing later
2001 endpar = sel_end_cursor.par->Next(); /* needed because
2006 // store the selection
2007 simple_cut_buffer = sel_start_cursor.par
2008 ->ParFromPos(sel_start_cursor.pos)->next;
2009 simple_cut_buffer->previous = 0;
2010 sel_end_cursor.par->previous->next = 0;
2012 // cut the selection
2013 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2014 = sel_end_cursor.par;
2016 sel_end_cursor.par->previous
2017 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2019 // care about footnotes
2020 if (simple_cut_buffer->footnoteflag) {
2021 LyXParagraph * tmppar = simple_cut_buffer;
2023 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2024 tmppar = tmppar->next;
2028 // the cut selection should begin with standard layout
2029 simple_cut_buffer->Clear();
2031 // paste the paragraphs again, if possible
2033 sel_start_cursor.par->Next()->ClearParagraph();
2034 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2036 !sel_start_cursor.par->Next()->Last())
2037 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2039 #ifndef FIX_DOUBLE_SPACE
2040 // maybe a forgotten blank
2041 if (sel_start_cursor.pos
2042 && sel_start_cursor.par
2043 ->IsLineSeparator(sel_start_cursor.pos)
2044 && sel_start_cursor.par
2045 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2046 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2051 // sometimes necessary
2053 sel_start_cursor.par->ClearParagraph();
2055 RedoParagraphs(sel_start_cursor, endpar);
2058 cursor = sel_start_cursor;
2059 SetCursor(cursor.par, cursor.pos);
2060 sel_cursor = cursor;
2061 UpdateCounters(cursor.row);
2065 void LyXText::CopySelection()
2067 // this doesnt make sense, if there is no selection
2071 // ok we have a selection. This is always between sel_start_cursor
2072 // and sel_end cursor
2073 LyXParagraph * tmppar;
2075 /* check wether there are half footnotes in the selection */
2076 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2077 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2078 tmppar = sel_start_cursor.par;
2079 while (tmppar != sel_end_cursor.par) {
2080 if (tmppar->footnoteflag !=
2081 sel_end_cursor.par->footnoteflag) {
2082 WriteAlert(_("Impossible operation"),
2083 _("Don't know what to do"
2084 " with half floats."),
2088 tmppar = tmppar->Next();
2092 /* table stuff -- begin */
2093 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2094 if ( sel_start_cursor.par != sel_end_cursor.par){
2095 WriteAlert(_("Impossible operation"),
2096 _("Don't know what to do with half tables."),
2101 /* table stuff -- end */
2103 // delete the simple_cut_buffer
2104 DeleteSimpleCutBuffer();
2106 // set the textclass
2107 simple_cut_buffer_textclass = parameters->textclass;
2109 #ifdef FIX_DOUBLE_SPACE
2110 // copy behind a space if there is one
2111 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2112 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2113 && (sel_start_cursor.par != sel_end_cursor.par
2114 || sel_start_cursor.pos < sel_end_cursor.pos))
2115 sel_start_cursor.pos++;
2117 // there are two cases: copy only within one paragraph
2118 // or more than one paragraph
2119 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2120 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2121 // only within one paragraph
2122 simple_cut_buffer = new LyXParagraph;
2123 LyXParagraph::size_type i = 0;
2124 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2125 sel_start_cursor.par->CopyIntoMinibuffer(i);
2126 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2129 // copy more than one paragraph
2130 // clone the paragraphs within the selection
2132 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2133 simple_cut_buffer = tmppar->Clone();
2134 LyXParagraph *tmppar2 = simple_cut_buffer;
2136 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2138 tmppar = tmppar->next;
2139 tmppar2->next = tmppar->Clone();
2140 tmppar2->next->previous = tmppar2;
2141 tmppar2 = tmppar2->next;
2145 // care about footnotes
2146 if (simple_cut_buffer->footnoteflag) {
2147 tmppar = simple_cut_buffer;
2149 tmppar->footnoteflag =
2150 LyXParagraph::NO_FOOTNOTE;
2151 tmppar = tmppar->next;
2155 // the simple_cut_buffer paragraph is too big
2156 LyXParagraph::size_type tmpi2 =
2157 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2158 for (; tmpi2; --tmpi2)
2159 simple_cut_buffer->Erase(0);
2161 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2163 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2164 while (tmppar2->size() > tmpi2) {
2165 tmppar2->Erase(tmppar2->text.size() - 1);
2171 void LyXText::PasteSelection()
2173 // this does not make sense, if there is nothing to paste
2174 if (!simple_cut_buffer)
2177 LyXParagraph * tmppar;
2178 LyXParagraph * endpar;
2180 LyXCursor tmpcursor;
2182 // be carefull with footnotes in footnotes
2183 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2185 // check whether the cut_buffer includes a footnote
2186 tmppar = simple_cut_buffer;
2188 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2189 tmppar = tmppar->next;
2192 WriteAlert(_("Impossible operation"),
2193 _("Can't paste float into float!"),
2199 /* table stuff -- begin */
2200 if (cursor.par->table) {
2201 if (simple_cut_buffer->next) {
2202 WriteAlert(_("Impossible operation"),
2203 _("Table cell cannot include more than one paragraph!"),
2208 /* table stuff -- end */
2210 SetUndo(Undo::INSERT,
2211 cursor.par->ParFromPos(cursor.pos)->previous,
2212 cursor.par->ParFromPos(cursor.pos)->next);
2216 // There are two cases: cutbuffer only one paragraph or many
2217 if (!simple_cut_buffer->next) {
2218 // only within a paragraph
2220 #ifndef FIX_DOUBLE_SPACE
2221 // please break behind a space, if there is one
2222 while (tmpcursor.par->Last() > tmpcursor.pos
2223 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2226 tmppar = simple_cut_buffer->Clone();
2227 /* table stuff -- begin */
2228 bool table_too_small = false;
2229 if (tmpcursor.par->table) {
2230 while (simple_cut_buffer->text.size()
2231 && !table_too_small) {
2232 if (simple_cut_buffer->IsNewline(0)){
2233 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2235 simple_cut_buffer->Erase(0);
2236 if (tmpcursor.pos < tmpcursor.par->Last())
2239 table_too_small = true;
2241 #ifdef FIX_DOUBLE_SPACE
2242 // This is an attempt to fix the
2243 // "never insert a space at the
2244 // beginning of a paragraph" problem.
2245 if (tmpcursor.pos == 0
2246 && simple_cut_buffer->IsLineSeparator(0)) {
2247 simple_cut_buffer->Erase(0);
2249 simple_cut_buffer->CutIntoMinibuffer(0);
2250 simple_cut_buffer->Erase(0);
2251 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2255 simple_cut_buffer->CutIntoMinibuffer(0);
2256 simple_cut_buffer->Erase(0);
2257 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2263 /* table stuff -- end */
2264 // Some provisions should be done here for checking
2265 // if we are inserting at the beginning of a
2266 // paragraph. If there are a space at the beginning
2267 // of the text to insert and we are inserting at
2268 // the beginning of the paragraph the space should
2270 while (simple_cut_buffer->text.size()) {
2271 #ifdef FIX_DOUBLE_SPACE
2272 // This is an attempt to fix the
2273 // "never insert a space at the
2274 // beginning of a paragraph" problem.
2275 if (tmpcursor.pos == 0
2276 && simple_cut_buffer->IsLineSeparator(0)) {
2277 simple_cut_buffer->Erase(0);
2279 simple_cut_buffer->CutIntoMinibuffer(0);
2280 simple_cut_buffer->Erase(0);
2281 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2285 simple_cut_buffer->CutIntoMinibuffer(0);
2286 simple_cut_buffer->Erase(0);
2287 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2292 delete simple_cut_buffer;
2293 simple_cut_buffer = tmppar;
2294 endpar = tmpcursor.par->Next();
2298 // make a copy of the simple cut_buffer
2299 tmppar = simple_cut_buffer;
2300 LyXParagraph * simple_cut_clone = tmppar->Clone();
2301 LyXParagraph * tmppar2 = simple_cut_clone;
2302 if (cursor.par->footnoteflag){
2303 tmppar->footnoteflag = cursor.par->footnoteflag;
2304 tmppar->footnotekind = cursor.par->footnotekind;
2306 while (tmppar->next) {
2307 tmppar = tmppar->next;
2308 tmppar2->next = tmppar->Clone();
2309 tmppar2->next->previous = tmppar2;
2310 tmppar2 = tmppar2->next;
2311 if (cursor.par->footnoteflag){
2312 tmppar->footnoteflag = cursor.par->footnoteflag;
2313 tmppar->footnotekind = cursor.par->footnotekind;
2317 // make sure there is no class difference
2318 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2319 parameters->textclass,
2322 // make the simple_cut_buffer exactly the same layout than
2323 // the cursor paragraph
2324 simple_cut_buffer->MakeSameLayout(cursor.par);
2326 // find the end of the buffer
2327 LyXParagraph * lastbuffer = simple_cut_buffer;
2328 while (lastbuffer->Next())
2329 lastbuffer = lastbuffer->Next();
2331 // find the physical end of the buffer
2332 #ifdef WITH_WARNINGS
2333 #warning Explain this please.
2336 // Can someone explain to be why this is done a second time?
2338 lastbuffer = simple_cut_buffer;
2339 while (lastbuffer->Next())
2340 lastbuffer = lastbuffer->Next();
2342 #ifndef FIX_DOUBLE_SPACE
2343 // Please break behind a space, if there is one. The space
2344 // should be copied too.
2345 if (cursor.par->Last() > cursor.pos
2346 && cursor.par->IsLineSeparator(cursor.pos))
2349 bool paste_the_end = false;
2351 // open the paragraph for inserting the simple_cut_buffer
2353 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2354 cursor.par->BreakParagraphConservative(cursor.pos);
2355 paste_the_end = true;
2358 #ifndef FIX_DOUBLE_SPACE
2359 // be careful with double spaces
2360 if ((!cursor.par->Last()
2361 || cursor.par->IsLineSeparator(cursor.pos - 1)
2362 || cursor.par->IsNewline(cursor.pos - 1))
2363 && simple_cut_buffer->text.size()
2364 && simple_cut_buffer->IsLineSeparator(0))
2365 simple_cut_buffer->Erase(0);
2367 // set the end for redoing later
2368 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2371 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2372 cursor.par->ParFromPos(cursor.pos)->next;
2373 cursor.par->ParFromPos(cursor.pos)->next->previous =
2374 lastbuffer->ParFromPos(lastbuffer->Last());
2376 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2377 simple_cut_buffer->previous =
2378 cursor.par->ParFromPos(cursor.pos);
2380 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2381 lastbuffer = cursor.par;
2383 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2385 // store the new cursor position
2386 tmpcursor.par = lastbuffer;
2387 tmpcursor.pos = lastbuffer->Last();
2389 // maybe some pasting
2390 if (lastbuffer->Next() && paste_the_end) {
2391 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2392 #ifndef FIX_DOUBLE_SPACE
2393 // be careful with double spaces
2394 if ((!lastbuffer->Last()
2395 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2396 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2397 && lastbuffer->Next()->Last()
2398 && lastbuffer->Next()->IsLineSeparator(0))
2399 lastbuffer->Next()->Erase(0);
2401 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2403 } else if (!lastbuffer->Next()->Last()) {
2404 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2405 #ifndef FIX_DOUBLE_SPACE
2406 // be careful witth double spaces
2407 if ((!lastbuffer->Last()
2408 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2409 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2410 && lastbuffer->Next()->Last()
2411 && lastbuffer->Next()->IsLineSeparator(0))
2412 lastbuffer->Next()->Erase(0);
2414 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2416 } else if (!lastbuffer->Last()) {
2417 lastbuffer->MakeSameLayout(lastbuffer->next);
2418 #ifndef FIX_DOUBLE_SPACE
2419 // be careful witth double spaces
2420 if ((!lastbuffer->Last()
2421 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2422 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2423 && lastbuffer->Next()->Last()
2424 && lastbuffer->Next()->IsLineSeparator(0))
2425 lastbuffer->Next()->Erase(0);
2427 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2430 lastbuffer->Next()->ClearParagraph();
2433 // restore the simple cut buffer
2434 simple_cut_buffer = simple_cut_clone;
2437 RedoParagraphs(cursor, endpar);
2439 SetCursor(cursor.par, cursor.pos);
2442 sel_cursor = cursor;
2443 SetCursor(tmpcursor.par, tmpcursor.pos);
2445 UpdateCounters(cursor.row);
2449 // returns a pointer to the very first LyXParagraph
2450 LyXParagraph * LyXText::FirstParagraph() const
2452 return params->paragraph;
2456 // returns true if the specified string is at the specified position
2457 bool LyXText::IsStringInText(LyXParagraph * par,
2458 LyXParagraph::size_type pos,
2459 char const * str) const
2463 while (pos + i < par->Last() && str[i] &&
2464 str[i] == par->GetChar(pos + i)) {
2474 // sets the selection over the number of characters of string, no check!!
2475 void LyXText::SetSelectionOverString(char const * string)
2477 sel_cursor = cursor;
2478 for (int i = 0; string[i]; ++i)
2484 // simple replacing. The font of the first selected character is used
2485 void LyXText::ReplaceSelectionWithString(char const * str)
2490 if (!selection) { // create a dummy selection
2491 sel_end_cursor = cursor;
2492 sel_start_cursor = cursor;
2495 // Get font setting before we cut
2496 LyXParagraph::size_type pos = sel_end_cursor.pos;
2497 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2499 // Insert the new string
2500 for (int i = 0; str[i]; ++i) {
2501 sel_end_cursor.par->InsertChar(pos, str[i]);
2502 sel_end_cursor.par->SetFont(pos, font);
2506 // Cut the selection
2513 // if the string can be found: return true and set the cursor to
2515 bool LyXText::SearchForward(char const * str) const
2517 LyXParagraph * par = cursor.par;
2518 LyXParagraph::size_type pos = cursor.pos;
2519 while (par && !IsStringInText(par, pos, str)) {
2520 if (pos < par->Last() - 1)
2528 SetCursor(par, pos);
2536 bool LyXText::SearchBackward(char const * string) const
2538 LyXParagraph * par = cursor.par;
2539 int pos = cursor.pos;
2545 // We skip empty paragraphs (Asger)
2547 par = par->Previous();
2549 pos = par->Last() - 1;
2550 } while (par && pos < 0);
2552 } while (par && !IsStringInText(par, pos, string));
2555 SetCursor(par, pos);
2562 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2564 char * str = new char[text.size() + 1];
2565 copy(text.begin(), text.end(), str);
2566 str[text.size()] = '\0';
2572 // needed to insert the selection
2573 void LyXText::InsertStringA(char const * s)
2576 LyXParagraph * par = cursor.par;
2577 LyXParagraph::size_type pos = cursor.pos;
2578 LyXParagraph::size_type a = 0;
2580 LyXParagraph * endpar = cursor.par->Next();
2585 textclasslist.Style(parameters->textclass,
2586 cursor.par->GetLayout()).isEnvironment();
2587 // only to be sure, should not be neccessary
2590 // insert the string, don't insert doublespace
2591 string::size_type i = 0;
2592 while (i < str.length()) {
2593 if (str[i] != '\n') {
2595 && i + 1 < str.length() && str[i + 1] != ' '
2596 && pos && par->GetChar(pos - 1)!= ' ') {
2597 par->InsertChar(pos,' ');
2599 } else if (par->table) {
2600 if (str[i] == '\t') {
2601 while((pos < par->size()) &&
2602 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2604 if (pos < par->size())
2606 else // no more fields to fill skip the rest
2608 } else if ((str[i] != 13) &&
2609 ((str[i] & 127) >= ' ')) {
2610 par->InsertChar(pos, str[i]);
2613 } else if (str[i] == ' ') {
2614 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2616 } else if (str[i] == '\t') {
2617 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2618 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2621 } else if (str[i]!= 13 &&
2622 // Ignore unprintables
2623 (str[i] & 127) >= ' ') {
2624 par->InsertChar(pos, str[i]);
2629 if (i + 1 >= str.length()) {
2633 while((pos < par->size()) &&
2634 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2637 cell = NumberOfCell(par, pos);
2638 while((pos < par->size()) &&
2639 !(par->table->IsFirstCell(cell))) {
2640 while((pos < par->size()) &&
2641 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2644 cell = NumberOfCell(par, pos);
2646 if (pos >= par->size())
2647 // no more fields to fill skip the rest
2650 if (!par->text.size()) {
2651 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2654 par->BreakParagraph(pos, flag);
2662 RedoParagraphs(cursor, endpar);
2663 SetCursor(cursor.par, cursor.pos);
2664 sel_cursor = cursor;
2665 SetCursor(par, pos);
2670 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2672 char * str = new char[text.size() + 1];
2673 copy(text.begin(), text.end(), str);
2674 str[text.size()] = '\0';
2680 /* turns double-CR to single CR, others where converted into one blank and 13s
2681 * that are ignored .Double spaces are also converted into one. Spaces at
2682 * the beginning of a paragraph are forbidden. tabs are converted into one
2683 * space. then InsertStringA is called */
2684 void LyXText::InsertStringB(char const * s)
2687 LyXParagraph * par = cursor.par;
2688 string::size_type i = 1;
2689 while (i < str.length()) {
2690 if (str[i] == '\t' && !par->table)
2692 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2694 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2695 if (str[i + 1] != '\n') {
2696 if (str[i - 1] != ' ')
2701 while (i + 1 < str.length()
2702 && (str[i + 1] == ' '
2703 || str[i + 1] == '\t'
2704 || str[i + 1] == '\n'
2705 || str[i + 1] == 13)) {
2712 InsertStringA(str.c_str());
2716 bool LyXText::GotoNextError() const
2718 LyXCursor res = cursor;
2720 if (res.pos < res.par->Last() - 1) {
2724 res.par = res.par->Next();
2729 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2730 && res.par->GetInset(res.pos)->AutoDelete()));
2733 SetCursor(res.par, res.pos);
2740 bool LyXText::GotoNextNote() const
2742 LyXCursor res = cursor;
2744 if (res.pos < res.par->Last() - 1) {
2747 res.par = res.par->Next();
2752 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2753 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2756 SetCursor(res.par, res.pos);
2763 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2767 if (!par || class1 == class2)
2769 par = par->FirstPhysicalPar();
2771 string name = textclasslist.NameOfLayout(class1, par->layout);
2773 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2774 textclasslist.NumberOfLayout(class2, name);
2777 } else { // layout not found
2778 // use default layout "Standard" (0)
2783 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2785 string s = "Layout had to be changed from\n"
2786 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2787 + "\nbecause of class conversion from\n"
2788 + textclasslist.NameOfClass(class1) + " to "
2789 + textclasslist.NameOfClass(class2);
2790 InsetError * new_inset = new InsetError(s);
2791 par->InsertChar(0, LyXParagraph::META_INSET);
2792 par->InsertInset(0, new_inset);
2801 void LyXText::CheckParagraph(LyXParagraph * par,
2802 LyXParagraph::size_type pos)
2805 LyXCursor tmpcursor;
2807 /* table stuff -- begin*/
2810 CheckParagraphInTable(par, pos);
2813 /* table stuff -- end*/
2816 LyXParagraph::size_type z;
2817 Row * row = GetRow(par, pos, y);
2819 // is there a break one row above
2820 if (row->previous && row->previous->par == row->par) {
2821 z = NextBreakPoint(row->previous, paperwidth);
2822 if ( z >= row->pos) {
2823 // set the dimensions of the row above
2824 y -= row->previous->height;
2826 refresh_row = row->previous;
2827 status = LyXText::NEED_MORE_REFRESH;
2829 BreakAgain(row->previous);
2831 // set the cursor again. Otherwise
2832 // dangling pointers are possible
2833 SetCursor(cursor.par, cursor.pos);
2834 sel_cursor = cursor;
2839 int tmpheight = row->height;
2840 LyXParagraph::size_type tmplast = RowLast(row);
2845 if (row->height == tmpheight && RowLast(row) == tmplast)
2846 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2848 status = LyXText::NEED_MORE_REFRESH;
2850 // check the special right address boxes
2851 if (textclasslist.Style(parameters->textclass,
2852 par->GetLayout()).margintype
2853 == MARGIN_RIGHT_ADDRESS_BOX) {
2854 tmpcursor.par = par;
2855 tmpcursor.row = row;
2858 tmpcursor.x_fix = 0;
2859 tmpcursor.pos = pos;
2860 RedoDrawingOfParagraph(tmpcursor);
2865 // set the cursor again. Otherwise dangling pointers are possible
2866 // also set the selection
2870 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2871 sel_cursor = cursor;
2872 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2873 sel_start_cursor = cursor;
2874 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2875 sel_end_cursor = cursor;
2876 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2877 last_sel_cursor = cursor;
2880 SetCursorIntern(cursor.par, cursor.pos);
2884 // returns 0 if inset wasn't found
2885 int LyXText::UpdateInset(Inset * inset)
2887 // first check the current paragraph
2888 int pos = cursor.par->GetPositionOfInset(inset);
2890 CheckParagraph(cursor.par, pos);
2894 // check every paragraph
2896 LyXParagraph * par = FirstParagraph();
2898 // make sure the paragraph is open
2899 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2900 pos = par->GetPositionOfInset(inset);
2902 CheckParagraph(par, pos);
2913 void LyXText::SetCursor(LyXParagraph * par,
2914 LyXParagraph::size_type pos, bool setfont) const
2916 LyXCursor old_cursor = cursor;
2917 SetCursorIntern(par, pos, setfont);
2918 DeleteEmptyParagraphMechanism(old_cursor);
2922 void LyXText::SetCursorIntern(LyXParagraph * par,
2923 LyXParagraph::size_type pos, bool setfont) const
2927 LyXParagraph * tmppar;
2928 LyXParagraph::size_type vpos,cursor_vpos;
2930 // correct the cursor position if impossible
2931 if (pos > par->Last()){
2932 tmppar = par->ParFromPos(pos);
2933 pos = par->PositionInParFromPos(pos);
2936 if (par->IsDummy() && par->previous &&
2937 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2938 while (par->previous &&
2939 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2940 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2941 par = par->previous ;
2942 if (par->IsDummy() &&
2943 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2944 pos += par->text.size() + 1;
2946 if (par->previous) {
2947 par = par->previous;
2949 pos += par->text.size() + 1;
2957 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2958 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2959 && !cursor.par->IsSeparator(cursor.pos))
2960 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2962 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2963 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2965 current_font = cursor.par->GetFontSettings(cursor.pos);
2966 real_current_font = GetFont(cursor.par, cursor.pos);
2967 if (pos == 0 && par->size() == 0
2968 && GetDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2969 current_font.setDirection(LyXFont::RTL_DIR);
2970 real_current_font.setDirection(LyXFont::RTL_DIR);
2974 /* get the cursor y position in text */
2975 row = GetRow(par, pos, y);
2976 /* y is now the beginning of the cursor row */
2978 /* y is now the cursor baseline */
2981 /* now get the cursors x position */
2983 float fill_separator, fill_hfill, fill_label_hfill;
2984 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2986 LyXParagraph::size_type last = RowLast(row);
2987 if (row->pos > last)
2989 else if (pos <= last ) {
2990 LyXDirection letter_direction = GetLetterDirection(row->par, pos);
2991 LyXDirection font_direction = GetFontDirection(real_current_font);
2992 if (letter_direction == font_direction || pos == 0)
2993 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
2994 ? log2vis(pos) : log2vis(pos)+1;
2996 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
2997 ? log2vis(pos-1)+1 : log2vis(pos-1);
2999 cursor_vpos = (GetLetterDirection(row->par, last) == LYX_DIR_LEFT_TO_RIGHT)
3000 ? log2vis(last)+1 : log2vis(last);
3002 /* table stuff -- begin*/
3003 if (row->par->table) {
3004 int cell = NumberOfCell(row->par, row->pos);
3006 x += row->par->table->GetBeginningOfTextInCell(cell);
3007 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3008 pos = vis2log(vpos);
3009 if (row->par->IsNewline(pos)) {
3010 x = x_old + row->par->table->WidthOfColumn(cell);
3013 x += row->par->table->GetBeginningOfTextInCell(cell);
3015 x += SingleWidth(row->par, pos);
3019 /* table stuff -- end*/
3020 LyXParagraph::size_type main_body =
3021 BeginningOfMainBody(row->par);
3022 if (main_body > 0 &&
3023 (main_body-1 > last ||
3024 !row->par->IsLineSeparator(main_body-1)))
3027 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3028 pos = vis2log(vpos);
3029 if (main_body > 0 && pos == main_body-1) {
3030 x += fill_label_hfill +
3031 GetFont(row->par, -2).stringWidth(
3032 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3033 if (row->par->IsLineSeparator(main_body-1))
3034 x -= SingleWidth(row->par, main_body-1);
3037 x += SingleWidth(row->par, pos);
3038 if (HfillExpansion(row, pos)) {
3039 if (pos >= main_body)
3042 x += fill_label_hfill;
3044 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3052 cursor.x_fix = cursor.x;
3057 void LyXText::SetCursorFromCoordinates(int x, long y) const
3059 LyXCursor old_cursor = cursor;
3061 /* get the row first */
3063 Row * row = GetRowNearY(y);
3065 cursor.par = row->par;
3067 int column = GetColumnNearX(row, x);
3068 cursor.pos = row->pos + column;
3070 cursor.y = y + row->baseline;
3075 (cursor.pos == cursor.par->Last()
3076 || cursor.par->IsSeparator(cursor.pos)
3077 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3078 && !cursor.par->IsSeparator(cursor.pos))
3079 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3081 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3082 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3084 current_font = cursor.par->GetFontSettings(cursor.pos);
3085 real_current_font = GetFont(cursor.par, cursor.pos);
3087 DeleteEmptyParagraphMechanism(old_cursor);
3091 void LyXText::CursorLeft() const
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::CursorLeftIntern() const
3106 if (cursor.pos > 0) {
3107 SetCursor(cursor.par, cursor.pos - 1);
3109 else if (cursor.par->Previous()) {
3110 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3115 void LyXText::CursorRight() const
3117 CursorRightIntern();
3118 if (cursor.par->table) {
3119 int cell = NumberOfCell(cursor.par, cursor.pos);
3120 if (cursor.par->table->IsContRow(cell) &&
3121 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3128 void LyXText::CursorRightIntern() const
3130 if (cursor.pos < cursor.par->Last()) {
3131 SetCursor(cursor.par, cursor.pos + 1);
3133 else if (cursor.par->Next()) {
3134 SetCursor(cursor.par->Next(), 0);
3139 void LyXText::CursorUp() const
3141 SetCursorFromCoordinates(cursor.x_fix,
3142 cursor.y - cursor.row->baseline - 1);
3143 if (cursor.par->table) {
3144 int cell = NumberOfCell(cursor.par, cursor.pos);
3145 if (cursor.par->table->IsContRow(cell) &&
3146 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3153 void LyXText::CursorDown() const
3155 if (cursor.par->table &&
3156 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3159 SetCursorFromCoordinates(cursor.x_fix,
3160 cursor.y - cursor.row->baseline
3161 + cursor.row->height + 1);
3162 if (cursor.par->table) {
3163 int cell = NumberOfCell(cursor.par, cursor.pos);
3164 int cell_above = cursor.par->table->GetCellAbove(cell);
3165 while(cursor.par->table &&
3166 cursor.par->table->IsContRow(cell) &&
3167 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3168 SetCursorFromCoordinates(cursor.x_fix,
3169 cursor.y - cursor.row->baseline
3170 + cursor.row->height + 1);
3171 if (cursor.par->table) {
3172 cell = NumberOfCell(cursor.par, cursor.pos);
3173 cell_above = cursor.par->table->GetCellAbove(cell);
3180 void LyXText::CursorUpParagraph() const
3182 if (cursor.pos > 0) {
3183 SetCursor(cursor.par, 0);
3185 else if (cursor.par->Previous()) {
3186 SetCursor(cursor.par->Previous(), 0);
3191 void LyXText::CursorDownParagraph() const
3193 if (cursor.par->Next()) {
3194 SetCursor(cursor.par->Next(), 0);
3196 SetCursor(cursor.par, cursor.par->Last());
3202 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3204 bool deleted = false;
3206 // this is the delete-empty-paragraph-mechanism.
3207 if (selection) return;
3209 #ifdef FIX_DOUBLE_SPACE
3210 /* Ok I'll put some comments here about what is missing.
3211 I have fixed BackSpace (and thus Delete) to not delete
3212 double-spaces automagically. I have also changed Cut,
3213 Copy and Paste to hopefully do some sensible things.
3214 There are still some small problems that can lead to
3215 double spaces stored in the document file or space at
3216 the beginning of paragraphs. This happens if you have
3217 the cursor betwenn to spaces and then save. Or if you
3218 cut and paste and the selection have a space at the
3219 beginning and then save right after the paste. I am
3220 sure none of these are very hard to fix, but I will
3221 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3222 that I can get some feedback. (Lgb)
3225 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3226 // delete the LineSeparator.
3229 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3230 // delete the LineSeparator.
3233 // If the pos around the old_cursor were spaces, delete one of them.
3234 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3235 && old_cursor.pos > 0
3236 && old_cursor.pos < old_cursor.par->Last()
3237 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3238 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3239 old_cursor.par->Erase(old_cursor.pos - 1);
3240 RedoParagraphs(old_cursor, old_cursor.par->Next());
3241 // or RedoDrawingOfParagraph(old_cursor);
3243 if (old_cursor.par == cursor.par &&
3244 cursor.pos > old_cursor.pos)
3245 SetCursor(cursor.par, cursor.pos - 1);
3247 SetCursor(cursor.par, cursor.pos);
3252 // Paragraph should not be deleted if empty
3253 if ((textclasslist.Style(parameters->textclass,
3254 old_cursor.par->GetLayout())).keepempty)
3257 LyXCursor tmpcursor;
3259 if (old_cursor.par != cursor.par) {
3260 if ( (old_cursor.par->Last() == 0
3261 || (old_cursor.par->Last() == 1
3262 && (old_cursor.par->IsLineSeparator(0))))
3263 && old_cursor.par->FirstPhysicalPar()
3264 == old_cursor.par->LastPhysicalPar()) {
3266 // ok, we will delete anything
3268 // make sure that you do not delete any environments
3269 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3270 !(old_cursor.row->previous
3271 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3272 && !(old_cursor.row->next
3273 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3275 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3276 ((old_cursor.row->previous
3277 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3279 (old_cursor.row->next
3280 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3282 status = LyXText::NEED_MORE_REFRESH;
3285 if (old_cursor.row->previous) {
3286 refresh_row = old_cursor.row->previous;
3287 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3289 cursor = old_cursor; // that undo can restore the right cursor position
3290 LyXParagraph * endpar = old_cursor.par->next;
3291 if (endpar && endpar->GetDepth()) {
3292 while (endpar && endpar->GetDepth()) {
3293 endpar = endpar->LastPhysicalPar()->Next();
3296 SetUndo(Undo::DELETE,
3297 old_cursor.par->previous,
3302 RemoveRow(old_cursor.row);
3303 if (params->paragraph == old_cursor.par) {
3304 params->paragraph = params->paragraph->next;
3307 delete old_cursor.par;
3309 /* Breakagain the next par. Needed
3310 * because of the parindent that
3311 * can occur or dissappear. The
3312 * next row can change its height,
3313 * if there is another layout before */
3314 if (refresh_row->next) {
3315 BreakAgain(refresh_row->next);
3316 UpdateCounters(refresh_row);
3318 SetHeightOfRow(refresh_row);
3320 refresh_row = old_cursor.row->next;
3321 refresh_y = old_cursor.y - old_cursor.row->baseline;
3324 cursor = old_cursor; // that undo can restore the right cursor position
3325 LyXParagraph *endpar = old_cursor.par->next;
3326 if (endpar && endpar->GetDepth()) {
3327 while (endpar && endpar->GetDepth()) {
3328 endpar = endpar->LastPhysicalPar()->Next();
3331 SetUndo(Undo::DELETE,
3332 old_cursor.par->previous,
3337 RemoveRow(old_cursor.row);
3339 if (params->paragraph == old_cursor.par) {
3340 params->paragraph = params->paragraph->next;
3342 delete old_cursor.par;
3344 /* Breakagain the next par. Needed
3345 because of the parindent that can
3346 occur or dissappear.
3347 The next row can change its height,
3348 if there is another layout before
3351 BreakAgain(refresh_row);
3352 UpdateCounters(refresh_row->previous);
3357 SetCursor(cursor.par, cursor.pos);
3359 /* if (cursor.y > old_cursor.y)
3360 cursor.y -= old_cursor.row->height; */
3362 if (sel_cursor.par == old_cursor.par
3363 && sel_cursor.pos == sel_cursor.pos) {
3364 // correct selection
3365 sel_cursor = cursor;
3370 if (old_cursor.par->ClearParagraph()){
3371 RedoParagraphs(old_cursor, old_cursor.par->Next());
3373 SetCursor(cursor.par, cursor.pos);
3374 sel_cursor = cursor;
3379 else if (cursor.par->table && (cursor.row != old_cursor.row)) {
3380 int cell = NumberOfCell(old_cursor.par, old_cursor.pos);
3381 if (old_cursor.par->table->IsContRow(cell) &&
3382 IsEmptyTableRow(old_cursor)) {
3383 RemoveTableRow(const_cast<LyXCursor*>(&old_cursor));
3391 LyXParagraph * LyXText::GetParFromID(int id)
3393 LyXParagraph * result = FirstParagraph();
3394 while (result && result->id() != id)
3395 result = result->next;
3401 bool LyXText::TextUndo()
3403 // returns false if no undo possible
3404 Undo * undo = params->undostack.pop();
3409 .push(CreateUndo(undo->kind,
3410 GetParFromID(undo->number_of_before_par),
3411 GetParFromID(undo->number_of_behind_par)));
3413 return TextHandleUndo(undo);
3417 bool LyXText::TextRedo()
3419 // returns false if no redo possible
3420 Undo * undo = params->redostack.pop();
3425 .push(CreateUndo(undo->kind,
3426 GetParFromID(undo->number_of_before_par),
3427 GetParFromID(undo->number_of_behind_par)));
3429 return TextHandleUndo(undo);
3433 bool LyXText::TextHandleUndo(Undo * undo)
3435 // returns false if no undo possible
3436 bool result = false;
3438 LyXParagraph * before =
3439 GetParFromID(undo->number_of_before_par);
3440 LyXParagraph * behind =
3441 GetParFromID(undo->number_of_behind_par);
3442 LyXParagraph * tmppar;
3443 LyXParagraph * tmppar2;
3444 LyXParagraph * tmppar3;
3445 LyXParagraph * tmppar4;
3446 LyXParagraph * endpar;
3447 LyXParagraph * tmppar5;
3449 // if there's no before take the beginning
3450 // of the document for redoing
3452 SetCursorIntern(FirstParagraph(), 0);
3454 // replace the paragraphs with the undo informations
3456 tmppar3 = undo->par;
3457 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3460 while (tmppar4->next)
3461 tmppar4 = tmppar4->next;
3462 } // get last undo par
3464 // now remove the old text if there is any
3465 if (before != behind || (!behind && !before)){
3467 tmppar5 = before->next;
3469 tmppar5 = params->paragraph;
3471 while (tmppar5 && tmppar5 != behind){
3473 tmppar5 = tmppar5->next;
3474 // a memory optimization for edit: Only layout information
3475 // is stored in the undo. So restore the text informations.
3476 if (undo->kind == Undo::EDIT){
3477 tmppar2->text = tmppar->text;
3478 tmppar->text.clear();
3479 tmppar2 = tmppar2->next;
3481 if ( currentrow && currentrow->par == tmppar )
3482 currentrow = currentrow -> previous;
3483 // Commenting out this might remove the error
3484 // reported by Purify, but it might also
3485 // introduce a memory leak. We need to
3491 // put the new stuff in the list if there is one
3494 before->next = tmppar3;
3496 params->paragraph = tmppar3;
3497 tmppar3->previous = before;
3501 params->paragraph = behind;
3504 tmppar4->next = behind;
3506 behind->previous = tmppar4;
3510 // Set the cursor for redoing
3512 SetCursorIntern(before->FirstSelfrowPar(), 0);
3513 // check wether before points to a closed float and open it if necessary
3514 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3515 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3517 while (tmppar4->previous &&
3518 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3519 tmppar4 = tmppar4->previous;
3520 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3521 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3522 tmppar4 = tmppar4->next;
3527 // open a cosed footnote at the end if necessary
3528 if (behind && behind->previous &&
3529 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3530 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3531 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3532 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3533 behind = behind->next;
3537 // calculate the endpar for redoing the paragraphs.
3539 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3540 endpar = behind->LastPhysicalPar()->Next();
3542 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3547 tmppar = GetParFromID(undo->number_of_cursor_par);
3548 RedoParagraphs(cursor, endpar);
3550 SetCursorIntern(tmppar, undo->cursor_pos);
3551 UpdateCounters(cursor.row);
3561 void LyXText::FinishUndo()
3563 // makes sure the next operation will be stored
3564 undo_finished = True;
3568 void LyXText::FreezeUndo()
3570 // this is dangerous and for internal use only
3575 void LyXText::UnFreezeUndo()
3577 // this is dangerous and for internal use only
3578 undo_frozen = false;
3582 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3583 LyXParagraph const * behind) const
3586 params->undostack.push(CreateUndo(kind, before, behind));
3587 params->redostack.clear();
3591 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3592 LyXParagraph const * behind)
3594 params->redostack.push(CreateUndo(kind, before, behind));
3598 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3599 LyXParagraph const * behind) const
3601 int before_number = -1;
3602 int behind_number = -1;
3604 before_number = before->id();
3606 behind_number = behind->id();
3607 // Undo::EDIT and Undo::FINISH are
3608 // always finished. (no overlapping there)
3609 // overlapping only with insert and delete inside one paragraph:
3610 // Nobody wants all removed character
3611 // appear one by one when undoing.
3612 // EDIT is special since only layout information, not the
3613 // contents of a paragaph are stored.
3614 if (!undo_finished && kind != Undo::EDIT &&
3615 kind != Undo::FINISH){
3616 // check wether storing is needed
3617 if (!params->undostack.empty() &&
3618 params->undostack.top()->kind == kind &&
3619 params->undostack.top()->number_of_before_par == before_number &&
3620 params->undostack.top()->number_of_behind_par == behind_number ){
3625 // create a new Undo
3626 LyXParagraph * undopar;
3627 LyXParagraph * tmppar;
3628 LyXParagraph * tmppar2;
3630 LyXParagraph * start = 0;
3631 LyXParagraph * end = 0;
3634 start = before->next;
3636 start = FirstParagraph();
3638 end = behind->previous;
3640 end = FirstParagraph();
3646 && start != end->next
3647 && (before != behind || (!before && !behind))) {
3649 tmppar2 = tmppar->Clone();
3650 tmppar2->id(tmppar->id());
3652 // a memory optimization: Just store the layout information
3654 if (kind == Undo::EDIT){
3655 tmppar2->text.clear();
3660 while (tmppar != end && tmppar->next) {
3661 tmppar = tmppar->next;
3662 tmppar2->next = tmppar->Clone();
3663 tmppar2->next->id(tmppar->id());
3664 // a memory optimization: Just store the layout
3665 // information when only edit
3666 if (kind == Undo::EDIT){
3667 tmppar2->next->text.clear();
3669 tmppar2->next->previous = tmppar2;
3670 tmppar2 = tmppar2->next;
3674 undopar = 0; // nothing to replace (undo of delete maybe)
3676 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3677 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3679 Undo * undo = new Undo(kind,
3680 before_number, behind_number,
3681 cursor_par, cursor_pos,
3684 undo_finished = false;
3689 void LyXText::SetCursorParUndo()
3691 SetUndo(Undo::FINISH,
3692 cursor.par->ParFromPos(cursor.pos)->previous,
3693 cursor.par->ParFromPos(cursor.pos)->next);
3697 void LyXText::RemoveTableRow(LyXCursor * cur) const
3703 // move to the previous row
3704 int cell_act = NumberOfCell(cur->par, cur->pos);
3707 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3710 !cur->par->table->IsFirstCell(cell_act)) {
3712 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3717 // now we have to pay attention if the actual table is the
3718 // main row of TableContRows and if yes to delete all of them
3723 // delete up to the next row
3724 while (cur->pos < cur->par->Last() &&
3726 || !cur->par->table->IsFirstCell(cell_act))) {
3727 while (cur->pos < cur->par->Last() &&
3728 !cur->par->IsNewline(cur->pos))
3729 cur->par->Erase(cur->pos);
3732 if (cur->pos < cur->par->Last())
3733 cur->par->Erase(cur->pos);
3735 if (cur->pos && cur->pos == cur->par->Last()) {
3737 cur->par->Erase(cur->pos); // no newline at very end!
3739 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3740 !cur->par->table->IsContRow(cell_org) &&
3741 cur->par->table->IsContRow(cell));
3742 cur->par->table->DeleteRow(cell_org);
3748 bool LyXText::IsEmptyTableRow(LyXCursor const & old_cursor) const
3750 if (!old_cursor.par->table)
3752 #ifdef I_DONT_KNOW_IF_I_SHOULD_DO_THIS
3753 int pos = old_cursor.pos;
3754 int cell = NumberOfCell(old_cursor.par, pos);
3756 // search first charater of this table row
3757 while (pos && !old_cursor.par->table->IsFirstCell(cell)) {
3759 while (pos && !old_cursor.par->IsNewline(pos-1))
3763 if (!old_cursor.par->IsNewline(pos))
3767 while ((pos < old_cursor.par->Last()) &&
3768 !old_cursor.par->table->IsFirstCell(cell)) {
3769 if (!old_cursor.par->IsNewline(pos))
3781 bool LyXText::IsEmptyTableCell() const
3783 LyXParagraph::size_type pos = cursor.pos - 1;
3784 while (pos >= 0 && pos < cursor.par->Last()
3785 && !cursor.par->IsNewline(pos))
3787 return cursor.par->IsNewline(pos + 1);
3791 void LyXText::toggleAppendix(){
3792 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3793 bool start = !par->start_of_appendix;
3795 // ensure that we have only one start_of_appendix in this document
3796 LyXParagraph * tmp = FirstParagraph();
3797 for (; tmp; tmp = tmp->next)
3798 tmp->start_of_appendix = 0;
3799 par->start_of_appendix = start;
3801 // we can set the refreshing parameters now
3802 status = LyXText::NEED_MORE_REFRESH;
3804 refresh_row = 0; // not needed for full update
3806 SetCursor(cursor.par, cursor.pos);