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"
24 #include "insets/insetbib.h"
27 #include "support/textutils.h"
30 #include "minibuffer.h"
32 #include "bufferparams.h"
33 #include "lyx_gui_misc.h"
36 #include "BufferView.h"
40 #define FIX_DOUBLE_SPACE 1
42 extern BufferView * current_view;
46 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
54 parameters = &p->params;
58 status = LyXText::UNCHANGED;
59 LyXParagraph * par = p->paragraph;
60 current_font = GetFont(par, 0);
65 InsertParagraph(par, lastrow);
68 // set cursor at the very top position
69 selection = true; /* these setting is necessary
70 because of the delete-empty-
71 paragraph mechanism in
73 SetCursor(firstrow->par, 0);
78 // no rebreak necessary
84 // Default layouttype for copy environment type
91 // Delete all rows, this does not touch the paragraphs!
92 Row * tmprow = firstrow;
94 tmprow = firstrow->next;
101 void LyXText::owner(BufferView * bv)
103 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
107 // Gets the fully instantiated font at a given position in a paragraph
108 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
109 // The difference is that this one is used for displaying, and thus we
110 // are allowed to make cosmetic improvements. For instance make footnotes
112 // If position is -1, we get the layout font of the paragraph.
113 // If position is -2, we get the font of the manual label of the paragraph.
114 LyXFont LyXText::GetFont(LyXParagraph * par,
115 LyXParagraph::size_type pos) const
117 LyXLayout const & layout =
118 textclasslist.Style(parameters->textclass, par->GetLayout());
120 char par_depth = par->GetDepth();
121 // We specialize the 95% common case:
122 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
125 if (layout.labeltype == LABEL_MANUAL
126 && pos < BeginningOfMainBody(par)) {
128 return par->GetFontSettings(pos).
129 realize(layout.reslabelfont);
131 return par->GetFontSettings(pos).
132 realize(layout.resfont);
135 // process layoutfont for pos == -1 and labelfont for pos < -1
137 return layout.resfont;
139 return layout.reslabelfont;
143 // The uncommon case need not be optimized as much
145 LyXFont layoutfont, tmpfont;
149 if (pos < BeginningOfMainBody(par)) {
151 layoutfont = layout.labelfont;
154 layoutfont = layout.font;
156 tmpfont = par->GetFontSettings(pos);
157 tmpfont.realize(layoutfont);
160 // process layoutfont for pos == -1 and labelfont for pos < -1
162 tmpfont = layout.font;
164 tmpfont = layout.labelfont;
167 // Resolve against environment font information
168 while (par && par_depth && !tmpfont.resolved()) {
169 par = par->DepthHook(par_depth - 1);
171 tmpfont.realize(textclasslist.
172 Style(parameters->textclass,
173 par->GetLayout()).font);
174 par_depth = par->GetDepth();
178 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
180 // Cosmetic improvement: If this is an open footnote, make the font
182 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
183 && par->footnotekind == LyXParagraph::FOOTNOTE) {
191 void LyXText::SetCharFont(LyXParagraph * par,
192 LyXParagraph::size_type pos,
196 // Let the insets convert their font
197 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
198 if (par->GetInset(pos))
199 font = par->GetInset(pos)->ConvertFont(font);
202 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
205 // Get concrete layout font to reduce against
208 if (pos < BeginningOfMainBody(par))
209 layoutfont = layout.labelfont;
211 layoutfont = layout.font;
213 // Realize against environment font information
214 if (par->GetDepth()){
215 LyXParagraph * tp = par;
216 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
217 tp = tp->DepthHook(tp->GetDepth()-1);
219 layoutfont.realize(textclasslist.
220 Style(parameters->textclass,
221 tp->GetLayout()).font);
225 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
227 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
228 && par->footnotekind == LyXParagraph::FOOTNOTE) {
229 layoutfont.decSize();
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 par->SetFont(pos, font);
239 /* inserts a new row behind the specified row, increments
240 * the touched counters */
241 void LyXText::InsertRow(Row * row, LyXParagraph * par,
242 LyXParagraph::size_type pos) const
244 Row * tmprow = new Row;
246 tmprow->previous = 0;
247 tmprow->next = firstrow;
251 tmprow->previous = row;
252 tmprow->next = row->next;
257 tmprow->next->previous = tmprow;
259 if (tmprow->previous)
260 tmprow->previous->next = tmprow;
268 ++number_of_rows; // one more row
272 // removes the row and reset the touched counters
273 void LyXText::RemoveRow(Row * row) const
275 /* this must not happen before the currentrow for clear reasons.
276 so the trick is just to set the current row onto the previous
279 GetRow(row->par, row->pos, unused_y);
280 currentrow = currentrow->previous;
282 currentrow_y -= currentrow->height;
287 row->next->previous = row->previous;
288 if (!row->previous) {
289 firstrow = row->next;
292 row->previous->next = row->next;
295 lastrow = row->previous;
297 height -= row->height; // the text becomes smaller
300 --number_of_rows; // one row less
304 // remove all following rows of the paragraph of the specified row.
305 void LyXText::RemoveParagraph(Row * row) const
307 LyXParagraph * tmppar = row->par;
311 while (row && row->par == tmppar) {
319 // insert the specified paragraph behind the specified row
320 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
322 InsertRow(row, par, 0); /* insert a new row, starting
325 SetCounter(par); // set the counters
327 // and now append the whole paragraph behind the new row
329 firstrow->height = 0;
330 AppendParagraph(firstrow);
333 row->next->height = 0;
334 AppendParagraph(row->next);
339 void LyXText::ToggleFootnote()
341 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
343 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
345 current_view->owner()->getMiniBuffer()->Set(_("Opened float"));
347 current_view->owner()->getMiniBuffer()->Set(_("Closed float"));
353 void LyXText::OpenStuff()
355 if (cursor.pos == 0 && cursor.par->bibkey){
356 cursor.par->bibkey->Edit(0, 0);
358 else if (cursor.pos < cursor.par->Last()
359 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
360 && cursor.par->GetInset(cursor.pos)->Editable()) {
361 current_view->owner()->getMiniBuffer()
362 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
363 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
365 cursor.par->GetInset(cursor.pos)->Edit(0, 0);
372 void LyXText::CloseFootnote()
374 LyXParagraph * tmppar;
375 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
377 // if the cursor is not in an open footnote, or
378 // there is no open footnote in this paragraph, just return.
379 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
382 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
383 current_view->owner()->getMiniBuffer()
384 ->Set(_("Nothing to do"));
388 // ok, move the cursor right before the footnote
389 // just a little faster than using CursorRight()
391 cursor.par->ParFromPos(cursor.pos) != par;
395 // now the cursor is at the beginning of the physical par
396 SetCursor(cursor.par,
398 cursor.par->ParFromPos(cursor.pos)->text.size());
400 /* we are in a footnote, so let us move at the beginning */
401 /* this is just faster than using just CursorLeft() */
404 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
405 // just a little bit faster than movin the cursor
406 tmppar = tmppar->Previous();
408 SetCursor(tmppar, tmppar->Last());
411 // the cursor must be exactly before the footnote
412 par = cursor.par->ParFromPos(cursor.pos);
414 status = LyXText::NEED_MORE_REFRESH;
415 refresh_row = cursor.row;
416 refresh_y = cursor.y - cursor.row->baseline;
419 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
420 Row * row = cursor.row;
422 tmppar->CloseFootnote(cursor.pos);
424 while (tmppar != endpar) {
425 RemoveRow(row->next);
427 tmppar = row->next->par;
432 AppendParagraph(cursor.row);
434 SetCursor(cursor.par, cursor.pos);
438 if (cursor.row->next)
439 SetHeightOfRow(cursor.row->next);
443 /* used in setlayout */
444 // Asger is not sure we want to do this...
445 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
447 LyXFont layoutfont, tmpfont;
449 LyXLayout const & layout =
450 textclasslist.Style(parameters->textclass, par->GetLayout());
452 for (LyXParagraph::size_type pos = 0;
453 pos < par->Last(); ++pos) {
454 if (pos < BeginningOfMainBody(par))
455 layoutfont = layout.labelfont;
457 layoutfont = layout.font;
459 tmpfont = par->GetFontSettings(pos);
460 tmpfont.reduce(layoutfont);
461 par->SetFont(pos, tmpfont);
466 // set layout over selection and make a total rebreak of those paragraphs
467 void LyXText::SetLayout(char layout)
471 // if there is no selection just set the layout
472 // of the current paragraph */
474 sel_start_cursor = cursor; // dummy selection
475 sel_end_cursor = cursor;
478 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
479 LyXParagraph * undoendpar = endpar;
481 if (endpar && endpar->GetDepth()) {
482 while (endpar && endpar->GetDepth()) {
483 endpar = endpar->LastPhysicalPar()->Next();
488 endpar = endpar->Next(); // because of parindents etc.
492 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
495 tmpcursor = cursor; /* store the current cursor */
497 /* ok we have a selection. This is always between sel_start_cursor
498 * and sel_end cursor */
499 cursor = sel_start_cursor;
501 LyXLayout const & lyxlayout =
502 textclasslist.Style(parameters->textclass, layout);
504 while (cursor.par != sel_end_cursor.par) {
505 if (cursor.par->footnoteflag ==
506 sel_start_cursor.par->footnoteflag) {
507 cursor.par->SetLayout(layout);
508 MakeFontEntriesLayoutSpecific(cursor.par);
509 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
510 fppar->added_space_top = lyxlayout.fill_top ?
511 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
512 fppar->added_space_bottom = lyxlayout.fill_bottom ?
513 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
514 if (lyxlayout.margintype == MARGIN_MANUAL)
515 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
516 if (lyxlayout.labeltype != LABEL_BIBLIO
518 delete fppar->bibkey;
522 cursor.par = cursor.par->Next();
524 if (cursor.par->footnoteflag ==
525 sel_start_cursor.par->footnoteflag) {
526 cursor.par->SetLayout(layout);
527 MakeFontEntriesLayoutSpecific(cursor.par);
528 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
529 fppar->added_space_top = lyxlayout.fill_top ?
530 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
531 fppar->added_space_bottom = lyxlayout.fill_bottom ?
532 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
533 if (lyxlayout.margintype == MARGIN_MANUAL)
534 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
535 if (lyxlayout.labeltype != LABEL_BIBLIO
537 delete fppar->bibkey;
542 RedoParagraphs(sel_start_cursor, endpar);
544 // we have to reset the selection, because the
545 // geometry could have changed */
546 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
548 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
549 UpdateCounters(cursor.row);
552 SetCursor(tmpcursor.par, tmpcursor.pos);
556 // increment depth over selection and
557 // make a total rebreak of those paragraphs
558 void LyXText::IncDepth()
560 // If there is no selection, just use the current paragraph
562 sel_start_cursor = cursor; // dummy selection
563 sel_end_cursor = cursor;
566 // We end at the next paragraph with depth 0
567 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
568 LyXParagraph * undoendpar = endpar;
570 if (endpar && endpar->GetDepth()) {
571 while (endpar && endpar->GetDepth()) {
572 endpar = endpar->LastPhysicalPar()->Next();
577 endpar = endpar->Next(); // because of parindents etc.
582 .par->ParFromPos(sel_start_cursor.pos)->previous,
585 LyXCursor tmpcursor = cursor; // store the current cursor
587 // ok we have a selection. This is always between sel_start_cursor
588 // and sel_end cursor
589 cursor = sel_start_cursor;
591 bool anything_changed = false;
594 // NOTE: you can't change the depth of a bibliography entry
595 if (cursor.par->footnoteflag ==
596 sel_start_cursor.par->footnoteflag
597 && textclasslist.Style(parameters->textclass,
598 cursor.par->GetLayout()
599 ).labeltype != LABEL_BIBLIO) {
600 LyXParagraph * prev =
601 cursor.par->FirstPhysicalPar()->Previous();
603 && (prev->GetDepth() - cursor.par->GetDepth() > 0
604 || (prev->GetDepth() == cursor.par->GetDepth()
605 && textclasslist.Style(parameters->textclass,
606 prev->GetLayout()).isEnvironment()))) {
607 cursor.par->FirstPhysicalPar()->depth++;
608 anything_changed = true;
611 if (cursor.par == sel_end_cursor.par)
613 cursor.par = cursor.par->Next();
616 // if nothing changed set all depth to 0
617 if (!anything_changed) {
618 cursor = sel_start_cursor;
619 while (cursor.par != sel_end_cursor.par) {
620 cursor.par->FirstPhysicalPar()->depth = 0;
621 cursor.par = cursor.par->Next();
623 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
624 cursor.par->FirstPhysicalPar()->depth = 0;
627 RedoParagraphs(sel_start_cursor, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
633 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
634 UpdateCounters(cursor.row);
637 SetCursor(tmpcursor.par, tmpcursor.pos);
641 // decrement depth over selection and
642 // make a total rebreak of those paragraphs
643 void LyXText::DecDepth()
645 // if there is no selection just set the layout
646 // of the current paragraph
648 sel_start_cursor = cursor; // dummy selection
649 sel_end_cursor = cursor;
652 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
653 LyXParagraph * undoendpar = endpar;
655 if (endpar && endpar->GetDepth()) {
656 while (endpar && endpar->GetDepth()) {
657 endpar = endpar->LastPhysicalPar()->Next();
662 endpar = endpar->Next(); // because of parindents etc.
667 .par->ParFromPos(sel_start_cursor.pos)->previous,
670 LyXCursor tmpcursor = cursor; // store the current cursor
672 // ok we have a selection. This is always between sel_start_cursor
673 // and sel_end cursor
674 cursor = sel_start_cursor;
677 if (cursor.par->footnoteflag ==
678 sel_start_cursor.par->footnoteflag) {
679 if (cursor.par->FirstPhysicalPar()->depth)
680 cursor.par->FirstPhysicalPar()->depth--;
682 if (cursor.par == sel_end_cursor.par)
684 cursor.par = cursor.par->Next();
687 RedoParagraphs(sel_start_cursor, endpar);
689 // we have to reset the selection, because the
690 // geometry could have changed
691 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
693 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
694 UpdateCounters(cursor.row);
697 SetCursor(tmpcursor.par, tmpcursor.pos);
701 // set font over selection and make a total rebreak of those paragraphs
702 void LyXText::SetFont(LyXFont const & font, bool toggleall)
704 // if there is no selection just set the current_font
706 // Determine basis font
708 if (cursor.pos < BeginningOfMainBody(cursor.par))
709 layoutfont = GetFont(cursor.par, -2);
711 layoutfont = GetFont(cursor.par, -1);
712 // Update current font
713 real_current_font.update(font, toggleall);
715 // Reduce to implicit settings
716 current_font = real_current_font;
717 current_font.reduce(layoutfont);
718 // And resolve it completely
719 real_current_font.realize(layoutfont);
723 LyXCursor tmpcursor = cursor; // store the current cursor
725 // ok we have a selection. This is always between sel_start_cursor
726 // and sel_end cursor
729 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
730 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
731 cursor = sel_start_cursor;
732 while (cursor.par != sel_end_cursor.par ||
733 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
734 && cursor.pos < sel_end_cursor.pos))
736 if (cursor.pos < cursor.par->Last()
737 && cursor.par->footnoteflag
738 == sel_start_cursor.par->footnoteflag) {
739 // an open footnote should behave
741 LyXFont newfont = GetFont(cursor.par, cursor.pos);
742 newfont.update(font, toggleall);
743 SetCharFont(cursor.par, cursor.pos, newfont);
747 cursor.par = cursor.par->Next();
751 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
753 // we have to reset the selection, because the
754 // geometry could have changed
755 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
757 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
760 SetCursor(tmpcursor.par, tmpcursor.pos);
764 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
766 Row * tmprow = cur.row;
767 long y = cur.y - tmprow->baseline;
769 SetHeightOfRow(tmprow);
770 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
771 // find the first row of the paragraph
772 if (first_phys_par != tmprow->par)
773 while (tmprow->previous
774 && tmprow->previous->par != first_phys_par) {
775 tmprow = tmprow->previous;
777 SetHeightOfRow(tmprow);
779 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
780 tmprow = tmprow->previous;
782 SetHeightOfRow(tmprow);
785 // we can set the refreshing parameters now
786 status = LyXText::NEED_MORE_REFRESH;
788 refresh_row = tmprow;
789 SetCursor(cur.par, cur.pos);
793 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
795 Row * tmprow = cur.row;
797 long y = cur.y - tmprow->baseline;
798 SetHeightOfRow(tmprow);
799 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
800 // find the first row of the paragraph
801 if (first_phys_par != tmprow->par)
802 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
803 tmprow = tmprow->previous;
806 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
807 tmprow = tmprow->previous;
811 // we can set the refreshing parameters now
812 if (status == LyXText::UNCHANGED || y < refresh_y) {
814 refresh_row = tmprow;
816 status = LyXText::NEED_MORE_REFRESH;
817 SetCursor(cur.par, cur.pos);
821 /* deletes and inserts again all paragaphs between the cursor
822 * and the specified par
823 * This function is needed after SetLayout and SetFont etc. */
824 void LyXText::RedoParagraphs(LyXCursor const & cur,
825 LyXParagraph const * endpar) const
828 LyXParagraph * tmppar, * first_phys_par;
830 Row * tmprow = cur.row;
832 long y = cur.y - tmprow->baseline;
834 if (!tmprow->previous){
835 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
837 first_phys_par = tmprow->par->FirstPhysicalPar();
838 // find the first row of the paragraph
839 if (first_phys_par != tmprow->par)
840 while (tmprow->previous
841 && tmprow->previous->par != first_phys_par) {
842 tmprow = tmprow->previous;
845 while (tmprow->previous
846 && tmprow->previous->par == first_phys_par) {
847 tmprow = tmprow->previous;
852 // we can set the refreshing parameters now
853 status = LyXText::NEED_MORE_REFRESH;
855 refresh_row = tmprow->previous; /* the real refresh row will
856 be deleted, so I store
860 tmppar = tmprow->next->par;
863 while (tmppar != endpar) {
864 RemoveRow(tmprow->next);
866 tmppar = tmprow->next->par;
871 // remove the first one
872 tmprow2 = tmprow; /* this is because tmprow->previous
874 tmprow = tmprow->previous;
877 tmppar = first_phys_par;
881 InsertParagraph(tmppar, tmprow);
884 while (tmprow->next && tmprow->next->par == tmppar)
885 tmprow = tmprow->next;
886 tmppar = tmppar->Next();
888 } while (tmppar != endpar);
890 // this is because of layout changes
892 refresh_y -= refresh_row->height;
893 SetHeightOfRow(refresh_row);
895 refresh_row = firstrow;
897 SetHeightOfRow(refresh_row);
900 if (tmprow && tmprow->next)
901 SetHeightOfRow(tmprow->next);
905 int LyXText::FullRebreak()
907 if (need_break_row) {
908 BreakAgain(need_break_row);
916 /* important for the screen */
919 /* the cursor set functions have a special mechanism. When they
920 * realize, that you left an empty paragraph, they will delete it.
921 * They also delet the corresponding row */
923 // need the selection cursor:
924 void LyXText::SetSelection()
927 last_sel_cursor = sel_cursor;
928 sel_start_cursor = sel_cursor;
929 sel_end_cursor = sel_cursor;
934 // first the toggling area
935 if (cursor.y < last_sel_cursor.y ||
936 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
937 toggle_end_cursor = last_sel_cursor;
938 toggle_cursor = cursor;
941 toggle_end_cursor = cursor;
942 toggle_cursor = last_sel_cursor;
945 last_sel_cursor = cursor;
947 // and now the whole selection
949 if (sel_cursor.par == cursor.par)
950 if (sel_cursor.pos < cursor.pos) {
951 sel_end_cursor = cursor;
952 sel_start_cursor = sel_cursor;
954 sel_end_cursor = sel_cursor;
955 sel_start_cursor = cursor;
957 else if (sel_cursor.y < cursor.y ||
958 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
959 sel_end_cursor = cursor;
960 sel_start_cursor = sel_cursor;
963 sel_end_cursor = sel_cursor;
964 sel_start_cursor = cursor;
967 // a selection with no contents is not a selection
968 if (sel_start_cursor.x == sel_end_cursor.x &&
969 sel_start_cursor.y == sel_end_cursor.y)
974 void LyXText::ClearSelection() const
981 void LyXText::CursorHome() const
983 SetCursor(cursor.par, cursor.row->pos);
987 void LyXText::CursorEnd() const
989 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
990 SetCursor(cursor.par, RowLast(cursor.row) + 1);
992 if (cursor.par->Last() &&
993 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
994 || cursor.par->IsNewline(RowLast(cursor.row))))
995 SetCursor(cursor.par, RowLast(cursor.row));
997 SetCursor(cursor.par, RowLast(cursor.row) + 1);
999 if (cursor.par->table) {
1000 int cell = NumberOfCell(cursor.par, cursor.pos);
1001 if (cursor.par->table->RowHasContRow(cell) &&
1002 cursor.par->table->CellHasContRow(cell)<0) {
1003 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1004 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1006 if (cursor.par->Last() &&
1007 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1008 || cursor.par->IsNewline(RowLast(cursor.row))))
1009 SetCursor(cursor.par, RowLast(cursor.row));
1011 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1018 void LyXText::CursorTop() const
1020 while (cursor.par->Previous())
1021 cursor.par = cursor.par->Previous();
1022 SetCursor(cursor.par, 0);
1026 void LyXText::CursorBottom() const
1028 while (cursor.par->Next())
1029 cursor.par = cursor.par->Next();
1030 SetCursor(cursor.par, cursor.par->Last());
1034 /* returns a pointer to the row near the specified y-coordinate
1035 * (relative to the whole text). y is set to the real beginning
1037 Row * LyXText::GetRowNearY(long & y) const
1043 tmprow = currentrow;
1044 tmpy = currentrow_y;
1051 while (tmprow->next && tmpy + tmprow->height <= y) {
1052 tmpy += tmprow->height;
1053 tmprow = tmprow->next;
1056 while (tmprow->previous && tmpy > y) {
1057 tmprow = tmprow->previous;
1058 tmpy -= tmprow->height;
1061 currentrow = tmprow;
1062 currentrow_y = tmpy;
1064 y = tmpy; // return the real y
1069 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1071 // If the mask is completely neutral, tell user
1072 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1073 // Could only happen with user style
1074 current_view->owner()->getMiniBuffer()
1075 ->Set(_("No font change defined. Use Character under"
1076 " the Layout menu to define font change."));
1080 // Try implicit word selection
1081 LyXCursor resetCursor = cursor;
1082 int implicitSelection = SelectWordWhenUnderCursor();
1085 SetFont(font, toggleall);
1087 /* Implicit selections are cleared afterwards and cursor is set to the
1088 original position. */
1089 if (implicitSelection) {
1091 cursor = resetCursor;
1092 SetCursor( cursor.par, cursor.pos );
1093 sel_cursor = cursor;
1098 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1100 if (textclasslist.Style(parameters->textclass,
1101 par->GetLayout()).labeltype != LABEL_MANUAL)
1104 return par->BeginningOfMainBody();
1108 /* if there is a selection, reset every environment you can find
1109 * in the selection, otherwise just the environment you are in */
1110 void LyXText::MeltFootnoteEnvironment()
1112 LyXParagraph * tmppar, * firsttmppar;
1116 /* is is only allowed, if the cursor is IN an open footnote.
1117 * Otherwise it is too dangerous */
1118 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1121 SetUndo(Undo::FINISH,
1122 cursor.par->PreviousBeforeFootnote()->previous,
1123 cursor.par->NextAfterFootnote()->next);
1125 /* ok, move to the beginning of the footnote. */
1126 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1127 cursor.par = cursor.par->Previous();
1129 SetCursor(cursor.par, cursor.par->Last());
1130 /* this is just faster than using CursorLeft(); */
1132 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1133 tmppar = firsttmppar;
1134 /* tmppar is now the paragraph right before the footnote */
1136 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1139 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1140 tmppar = tmppar->next; /* I use next instead of Next(),
1141 * because there cannot be any
1142 * footnotes in a footnote
1144 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1146 /* remember the captions and empty paragraphs */
1147 if ((textclasslist.Style(parameters->textclass,
1148 tmppar->GetLayout())
1149 .labeltype == LABEL_SENSITIVE)
1151 tmppar->SetLayout(0);
1154 // now we will paste the ex-footnote, if the layouts allow it
1155 // first restore the layout of the paragraph right behind
1158 tmppar->next->MakeSameLayout(cursor.par);
1161 if ((!tmppar->GetLayout() && !tmppar->table)
1163 && (!tmppar->Next()->Last()
1164 || tmppar->Next()->HasSameLayout(tmppar)))) {
1165 if (tmppar->Next()->Last()
1166 && tmppar->Next()->IsLineSeparator(0))
1167 tmppar->Next()->Erase(0);
1168 tmppar->PasteParagraph();
1171 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1172 * by the pasting of the beginning */
1174 /* then the beginning */
1175 /* if there is no space between the text and the footnote, so we insert
1177 * (only if the previous par and the footnotepar are not empty!) */
1178 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1179 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1180 if (firsttmppar->text.size()
1181 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1182 && first_footnote_par_is_not_empty) {
1183 firsttmppar->next->InsertChar(0, ' ');
1185 firsttmppar->PasteParagraph();
1188 /* now redo the paragaphs */
1189 RedoParagraphs(cursor, tmppar);
1191 SetCursor(cursor.par, cursor.pos);
1193 /* sometimes it can happen, that there is a counter change */
1194 Row * row = cursor.row;
1195 while (row->next && row->par != tmppar && row->next->par != tmppar)
1197 UpdateCounters(row);
1204 /* the DTP switches for paragraphs. LyX will store them in the
1205 * first physicla paragraph. When a paragraph is broken, the top settings
1206 * rest, the bottom settings are given to the new one. So I can make shure,
1207 * they do not duplicate themself and you cannnot make dirty things with
1210 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1211 bool pagebreak_top, bool pagebreak_bottom,
1212 VSpace const & space_top,
1213 VSpace const & space_bottom,
1215 string labelwidthstring,
1218 LyXCursor tmpcursor = cursor;
1220 sel_start_cursor = cursor;
1221 sel_end_cursor = cursor;
1224 // make sure that the depth behind the selection are restored, too
1225 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1226 LyXParagraph * undoendpar = endpar;
1228 if (endpar && endpar->GetDepth()) {
1229 while (endpar && endpar->GetDepth()) {
1230 endpar = endpar->LastPhysicalPar()->Next();
1231 undoendpar = endpar;
1235 endpar = endpar->Next(); // because of parindents etc.
1240 .par->ParFromPos(sel_start_cursor.pos)->previous,
1244 LyXParagraph * tmppar = sel_end_cursor.par;
1245 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1246 SetCursor(tmppar->FirstPhysicalPar(), 0);
1247 status = LyXText::NEED_MORE_REFRESH;
1248 refresh_row = cursor.row;
1249 refresh_y = cursor.y - cursor.row->baseline;
1250 if (cursor.par->footnoteflag ==
1251 sel_start_cursor.par->footnoteflag) {
1252 cursor.par->line_top = line_top;
1253 cursor.par->line_bottom = line_bottom;
1254 cursor.par->pagebreak_top = pagebreak_top;
1255 cursor.par->pagebreak_bottom = pagebreak_bottom;
1256 cursor.par->added_space_top = space_top;
1257 cursor.par->added_space_bottom = space_bottom;
1258 // does the layout allow the new alignment?
1259 if (align == LYX_ALIGN_LAYOUT)
1260 align = textclasslist
1261 .Style(parameters->textclass,
1262 cursor.par->GetLayout()).align;
1263 if (align & textclasslist
1264 .Style(parameters->textclass,
1265 cursor.par->GetLayout()).alignpossible) {
1266 if (align == textclasslist
1267 .Style(parameters->textclass,
1268 cursor.par->GetLayout()).align)
1269 cursor.par->align = LYX_ALIGN_LAYOUT;
1271 cursor.par->align = align;
1273 cursor.par->SetLabelWidthString(labelwidthstring);
1274 cursor.par->noindent = noindent;
1277 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1280 RedoParagraphs(sel_start_cursor, endpar);
1283 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1284 sel_cursor = cursor;
1285 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1287 SetCursor(tmpcursor.par, tmpcursor.pos);
1291 void LyXText::SetParagraphExtraOpt(int type,
1293 char const * widthp,
1294 int alignment, bool hfill,
1295 bool start_minipage)
1297 LyXCursor tmpcursor = cursor;
1298 LyXParagraph * tmppar;
1300 sel_start_cursor = cursor;
1301 sel_end_cursor = cursor;
1304 // make sure that the depth behind the selection are restored, too
1305 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1306 LyXParagraph * undoendpar = endpar;
1308 if (endpar && endpar->GetDepth()) {
1309 while (endpar && endpar->GetDepth()) {
1310 endpar = endpar->LastPhysicalPar()->Next();
1311 undoendpar = endpar;
1315 endpar = endpar->Next(); // because of parindents etc.
1320 .par->ParFromPos(sel_start_cursor.pos)->previous,
1323 tmppar = sel_end_cursor.par;
1324 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1325 SetCursor(tmppar->FirstPhysicalPar(), 0);
1326 status = LyXText::NEED_MORE_REFRESH;
1327 refresh_row = cursor.row;
1328 refresh_y = cursor.y - cursor.row->baseline;
1329 if (cursor.par->footnoteflag ==
1330 sel_start_cursor.par->footnoteflag) {
1331 if (type == LyXParagraph::PEXTRA_NONE) {
1332 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1333 cursor.par->UnsetPExtraType();
1334 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1337 cursor.par->SetPExtraType(type, width, widthp);
1338 cursor.par->pextra_hfill = hfill;
1339 cursor.par->pextra_start_minipage = start_minipage;
1340 cursor.par->pextra_alignment = alignment;
1343 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1345 RedoParagraphs(sel_start_cursor, endpar);
1347 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1348 sel_cursor = cursor;
1349 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1351 SetCursor(tmpcursor.par, tmpcursor.pos);
1355 static char const * alphaCounter(int n) {
1356 static char result[2];
1361 result[0] = 'A' + n;
1369 // set the counter of a paragraph. This includes the labels
1370 void LyXText::SetCounter(LyXParagraph * par) const
1372 // this is only relevant for the beginning of paragraph
1373 par = par->FirstPhysicalPar();
1375 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1378 LyXTextClass const & textclass =
1379 textclasslist.TextClass(parameters->textclass);
1381 /* copy the prev-counters to this one, unless this is the start of a
1382 footnote or of a bibliography or the very first paragraph */
1384 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1385 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1386 && par->footnotekind == LyXParagraph::FOOTNOTE)
1387 && !(textclasslist.Style(parameters->textclass,
1388 par->Previous()->GetLayout()
1389 ).labeltype != LABEL_BIBLIO
1390 && layout.labeltype == LABEL_BIBLIO)) {
1391 for (int i = 0; i < 10; ++i) {
1392 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1394 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1395 if (!par->appendix && par->start_of_appendix){
1396 par->appendix = true;
1397 for (int i = 0; i < 10; ++i) {
1398 par->setCounter(i, 0);
1401 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1402 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1405 for (int i = 0; i < 10; ++i) {
1406 par->setCounter(i, 0);
1408 par->appendix = par->start_of_appendix;
1413 // if this is an open marginnote and this is the first
1414 // entry in the marginnote and the enclosing
1415 // environment is an enum/item then correct for the
1416 // LaTeX behaviour (ARRae)
1417 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1418 && par->footnotekind == LyXParagraph::MARGIN
1420 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1421 && (par->PreviousBeforeFootnote()
1422 && textclasslist.Style(parameters->textclass,
1423 par->PreviousBeforeFootnote()->GetLayout()
1424 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1425 // Any itemize or enumerate environment in a marginnote
1426 // that is embedded in an itemize or enumerate
1427 // paragraph is seen by LaTeX as being at a deeper
1428 // level within that enclosing itemization/enumeration
1429 // even if there is a "standard" layout at the start of
1435 /* Maybe we have to increment the enumeration depth.
1436 * BUT, enumeration in a footnote is considered in isolation from its
1437 * surrounding paragraph so don't increment if this is the
1438 * first line of the footnote
1439 * AND, bibliographies can't have their depth changed ie. they
1440 * are always of depth 0
1443 && par->Previous()->GetDepth() < par->GetDepth()
1444 && textclasslist.Style(parameters->textclass,
1445 par->Previous()->GetLayout()
1446 ).labeltype == LABEL_COUNTER_ENUMI
1447 && par->enumdepth < 3
1448 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1449 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1450 && par->footnotekind == LyXParagraph::FOOTNOTE)
1451 && layout.labeltype != LABEL_BIBLIO) {
1455 /* Maybe we have to decrement the enumeration depth, see note above */
1457 && par->Previous()->GetDepth() > par->GetDepth()
1458 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1459 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1460 && par->footnotekind == LyXParagraph::FOOTNOTE)
1461 && layout.labeltype != LABEL_BIBLIO) {
1462 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1463 par->setCounter(6 + par->enumdepth,
1464 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1465 /* reset the counters.
1466 * A depth change is like a breaking layout
1468 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1469 par->setCounter(i, 0);
1472 if (!par->labelstring.empty()) {
1473 par->labelstring.clear();
1476 if (layout.margintype == MARGIN_MANUAL) {
1477 if (par->labelwidthstring.empty()) {
1478 par->SetLabelWidthString(layout.labelstring());
1482 par->SetLabelWidthString(string());
1485 /* is it a layout that has an automatic label ? */
1486 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1488 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1489 if (i >= 0 && i<= parameters->secnumdepth) {
1490 par->incCounter(i); // increment the counter
1492 char * s = new char[50];
1494 // Is there a label? Useful for Chapter layout
1495 if (!par->appendix){
1496 if (!layout.labelstring().empty())
1497 par->labelstring = layout.labelstring();
1499 par->labelstring.clear();
1501 if (!layout.labelstring_appendix().empty())
1502 par->labelstring = layout.labelstring_appendix();
1504 par->labelstring.clear();
1507 if (!par->appendix){
1508 switch (2 * LABEL_FIRST_COUNTER -
1509 textclass.maxcounter() + i) {
1510 case LABEL_COUNTER_CHAPTER:
1512 par->getCounter(i));
1514 case LABEL_COUNTER_SECTION:
1516 par->getCounter(i - 1),
1517 par->getCounter(i));
1519 case LABEL_COUNTER_SUBSECTION:
1520 sprintf(s, "%d.%d.%d",
1521 par->getCounter(i-2),
1522 par->getCounter(i-1),
1523 par->getCounter(i));
1525 case LABEL_COUNTER_SUBSUBSECTION:
1526 sprintf(s, "%d.%d.%d.%d",
1527 par->getCounter(i-3),
1528 par->getCounter(i-2),
1529 par->getCounter(i-1),
1530 par->getCounter(i));
1532 case LABEL_COUNTER_PARAGRAPH:
1533 sprintf(s, "%d.%d.%d.%d.%d",
1534 par->getCounter(i-4),
1535 par->getCounter(i-3),
1536 par->getCounter(i-2),
1537 par->getCounter(i-1),
1538 par->getCounter(i));
1540 case LABEL_COUNTER_SUBPARAGRAPH:
1541 sprintf(s, "%d.%d.%d.%d.%d.%d",
1542 par->getCounter(i-5),
1543 par->getCounter(i-4),
1544 par->getCounter(i-3),
1545 par->getCounter(i-2),
1546 par->getCounter(i-1),
1547 par->getCounter(i));
1550 sprintf(s, "%d.", par->getCounter(i));
1554 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1555 case LABEL_COUNTER_CHAPTER:
1557 alphaCounter(par->getCounter(i)));
1559 case LABEL_COUNTER_SECTION:
1561 alphaCounter(par->getCounter(i - 1)),
1562 par->getCounter(i));
1564 case LABEL_COUNTER_SUBSECTION:
1565 sprintf(s, "%s.%d.%d",
1566 alphaCounter(par->getCounter(i-2)),
1567 par->getCounter(i-1),
1568 par->getCounter(i));
1570 case LABEL_COUNTER_SUBSUBSECTION:
1571 sprintf(s, "%s.%d.%d.%d",
1572 alphaCounter(par->getCounter(i-3)),
1573 par->getCounter(i-2),
1574 par->getCounter(i-1),
1575 par->getCounter(i));
1577 case LABEL_COUNTER_PARAGRAPH:
1578 sprintf(s, "%s.%d.%d.%d.%d",
1579 alphaCounter(par->getCounter(i-4)),
1580 par->getCounter(i-3),
1581 par->getCounter(i-2),
1582 par->getCounter(i-1),
1583 par->getCounter(i));
1585 case LABEL_COUNTER_SUBPARAGRAPH:
1586 sprintf(s, "%s.%d.%d.%d.%d.%d",
1587 alphaCounter(par->getCounter(i-5)),
1588 par->getCounter(i-4),
1589 par->getCounter(i-3),
1590 par->getCounter(i-2),
1591 par->getCounter(i-1),
1592 par->getCounter(i));
1595 sprintf(s, "%c.", par->getCounter(i));
1600 par->labelstring += s;
1603 for (i++; i < 10; ++i) {
1604 // reset the following counters
1605 par->setCounter(i, 0);
1607 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1608 for (i++; i < 10; ++i) {
1609 // reset the following counters
1610 par->setCounter(i, 0);
1612 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1613 par->incCounter(i + par->enumdepth);
1614 char * s = new char[25];
1615 int number = par->getCounter(i + par->enumdepth);
1617 static const char *roman[20] = {
1618 "i", "ii", "iii", "iv", "v",
1619 "vi", "vii", "viii", "ix", "x",
1620 "xi", "xii", "xiii", "xiv", "xv",
1621 "xvi", "xvii", "xviii", "xix", "xx"
1623 static const char hebrew[22] = {
1624 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1625 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1626 '÷', 'ø', 'ù', 'ú'
1629 switch (par->enumdepth) {
1631 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1632 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1634 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1637 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1638 sprintf(s, "%s.", roman[(number-1) % 20]);
1640 sprintf(s, ".%s", roman[(number-1) % 20]);
1643 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1644 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1646 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1649 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1650 sprintf(s, "%d.", number);
1652 sprintf(s, ".%d", number);
1655 par->labelstring = s;
1658 for (i += par->enumdepth + 1; i < 10; ++i)
1659 par->setCounter(i, 0); /* reset the following counters */
1662 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1663 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1665 int number = par->getCounter(i);
1667 par->bibkey = new InsetBibKey();
1668 par->bibkey->setCounter(number);
1669 par->labelstring = layout.labelstring();
1671 // In biblio should't be following counters but...
1673 string s = layout.labelstring();
1675 // the caption hack:
1677 if (layout.labeltype == LABEL_SENSITIVE) {
1678 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1679 && (par->footnotekind == LyXParagraph::FIG
1680 || par->footnotekind == LyXParagraph::WIDE_FIG))
1681 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1685 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1686 && (par->footnotekind == LyXParagraph::TAB
1687 || par->footnotekind == LyXParagraph::WIDE_TAB))
1688 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1692 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1693 && par->footnotekind == LyXParagraph::ALGORITHM)
1694 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1697 s = ":Ãúéøåâìà ";
1699 /* par->SetLayout(0);
1700 s = layout->labelstring; */
1701 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1704 s = " :úåòîùî øñç";
1708 par->labelstring = s;
1710 /* reset the enumeration counter. They are always resetted
1711 * when there is any other layout between */
1712 for (int i = 6 + par->enumdepth; i < 10; ++i)
1713 par->setCounter(i, 0);
1718 /* Updates all counters BEHIND the row. Changed paragraphs
1719 * with a dynamic left margin will be rebroken. */
1720 void LyXText::UpdateCounters(Row * row) const
1729 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1730 par = row->par->LastPhysicalPar()->Next();
1732 par = row->par->next;
1737 while (row->par != par)
1742 /* now check for the headline layouts. remember that they
1743 * have a dynamic left margin */
1745 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1746 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1749 /* Rebreak the paragraph */
1750 RemoveParagraph(row);
1751 AppendParagraph(row);
1753 /* think about the damned open footnotes! */
1754 while (par->Next() &&
1755 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1756 || par->Next()->IsDummy())){
1758 if (par->IsDummy()) {
1759 while (row->par != par)
1761 RemoveParagraph(row);
1762 AppendParagraph(row);
1767 par = par->LastPhysicalPar()->Next();
1773 /* insets an inset. */
1774 void LyXText::InsertInset(Inset *inset)
1776 SetUndo(Undo::INSERT,
1777 cursor.par->ParFromPos(cursor.pos)->previous,
1778 cursor.par->ParFromPos(cursor.pos)->next);
1779 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1780 cursor.par->InsertInset(cursor.pos, inset);
1781 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1782 * The character will not be inserted a
1787 // this is for the simple cut and paste mechanism
1788 static LyXParagraph * simple_cut_buffer = 0;
1789 static char simple_cut_buffer_textclass = 0;
1791 void DeleteSimpleCutBuffer()
1793 if (!simple_cut_buffer)
1795 LyXParagraph * tmppar;
1797 while (simple_cut_buffer) {
1798 tmppar = simple_cut_buffer;
1799 simple_cut_buffer = simple_cut_buffer->next;
1802 simple_cut_buffer = 0;
1806 void LyXText::copyEnvironmentType()
1808 copylayouttype = cursor.par->GetLayout();
1812 void LyXText::pasteEnvironmentType()
1814 SetLayout(copylayouttype);
1818 void LyXText::CutSelection(bool doclear)
1820 // This doesn't make sense, if there is no selection
1824 // OK, we have a selection. This is always between sel_start_cursor
1825 // and sel_end cursor
1826 LyXParagraph * tmppar;
1828 // Check whether there are half footnotes in the selection
1829 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1830 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1831 tmppar = sel_start_cursor.par;
1832 while (tmppar != sel_end_cursor.par){
1833 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1834 WriteAlert(_("Impossible operation"),
1835 _("Don't know what to do with half floats."),
1839 tmppar = tmppar->Next();
1843 /* table stuff -- begin */
1844 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1845 if ( sel_start_cursor.par != sel_end_cursor.par) {
1846 WriteAlert(_("Impossible operation"),
1847 _("Don't know what to do with half tables."),
1851 sel_start_cursor.par->table->Reinit();
1853 /* table stuff -- end */
1855 // make sure that the depth behind the selection are restored, too
1856 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1857 LyXParagraph * undoendpar = endpar;
1859 if (endpar && endpar->GetDepth()) {
1860 while (endpar && endpar->GetDepth()) {
1861 endpar = endpar->LastPhysicalPar()->Next();
1862 undoendpar = endpar;
1864 } else if (endpar) {
1865 endpar = endpar->Next(); // because of parindents etc.
1868 SetUndo(Undo::DELETE,
1870 .par->ParFromPos(sel_start_cursor.pos)->previous,
1873 // clear the simple_cut_buffer
1874 DeleteSimpleCutBuffer();
1876 // set the textclass
1877 simple_cut_buffer_textclass = parameters->textclass;
1879 #ifdef WITH_WARNINGS
1880 #warning Asger: Make cut more intelligent here.
1883 White paper for "intelligent" cutting:
1885 Example: "This is our text."
1886 Using " our " as selection, cutting will give "This istext.".
1887 Using "our" as selection, cutting will give "This is text.".
1888 Using " our" as selection, cutting will give "This is text.".
1889 Using "our " as selection, cutting will give "This is text.".
1891 All those four selections will (however) paste identically:
1892 Pasting with the cursor right after the "is" will give the
1893 original text with all four selections.
1895 The rationale is to be intelligent such that words are copied,
1896 cut and pasted in a functional manner.
1898 This is not implemented yet. (Asger)
1900 The changes below sees to do a lot of what you want. However
1901 I have not verified that all cases work as they should:
1903 - cut in multiple row
1905 - cut across footnotes and paragraph
1906 My simplistic tests show that the idea are basically sound but
1907 there are some items to fix up...we only need to find them
1910 As do redo Asger's example above (with | beeing the cursor in the
1911 result after cutting.):
1913 Example: "This is our text."
1914 Using " our " as selection, cutting will give "This is|text.".
1915 Using "our" as selection, cutting will give "This is | text.".
1916 Using " our" as selection, cutting will give "This is| text.".
1917 Using "our " as selection, cutting will give "This is |text.".
1922 #ifndef FIX_DOUBLE_SPACE
1923 bool space_wrapped =
1924 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1925 if (sel_end_cursor.pos > 0
1926 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1927 // please break before a space at the end
1928 sel_end_cursor.pos--;
1929 space_wrapped = true;
1931 // cut behind a space if there is one
1932 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1933 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1934 && (sel_start_cursor.par != sel_end_cursor.par
1935 || sel_start_cursor.pos < sel_end_cursor.pos))
1936 sel_start_cursor.pos++;
1938 // there are two cases: cut only within one paragraph or
1939 // more than one paragraph
1941 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1942 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1943 // only within one paragraph
1944 simple_cut_buffer = new LyXParagraph;
1945 LyXParagraph::size_type i =
1946 sel_start_cursor.pos;
1947 for (; i < sel_end_cursor.pos; ++i) {
1948 /* table stuff -- begin */
1949 if (sel_start_cursor.par->table
1950 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1951 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1952 sel_start_cursor.pos++;
1954 /* table stuff -- end */
1955 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1956 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1958 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1960 #ifndef FIX_DOUBLE_SPACE
1961 // check for double spaces
1962 if (sel_start_cursor.pos &&
1963 sel_start_cursor.par->Last() > sel_start_cursor.pos
1964 && sel_start_cursor.par
1965 ->IsLineSeparator(sel_start_cursor.pos - 1)
1966 && sel_start_cursor.par
1967 ->IsLineSeparator(sel_start_cursor.pos)) {
1968 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1971 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1974 endpar = sel_end_cursor.par->Next();
1976 // cut more than one paragraph
1979 ->BreakParagraphConservative(sel_end_cursor.pos);
1980 #ifndef FIX_DOUBLE_SPACE
1981 // insert a space at the end if there was one
1984 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1986 sel_end_cursor.par = sel_end_cursor.par->Next();
1987 sel_end_cursor.pos = 0;
1989 cursor = sel_end_cursor;
1991 #ifndef FIX_DOUBLE_SPACE
1992 // please break behind a space, if there is one.
1993 // The space should be copied too
1994 if (sel_start_cursor.par
1995 ->IsLineSeparator(sel_start_cursor.pos))
1996 sel_start_cursor.pos++;
1998 sel_start_cursor.par
1999 ->BreakParagraphConservative(sel_start_cursor.pos);
2000 #ifndef FIX_DOUBLE_SPACE
2001 if (!sel_start_cursor.pos
2002 || sel_start_cursor.par
2003 ->IsLineSeparator(sel_start_cursor.pos - 1)
2004 || sel_start_cursor.par
2005 ->IsNewline(sel_start_cursor.pos - 1)) {
2006 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2009 // store the endparagraph for redoing later
2010 endpar = sel_end_cursor.par->Next(); /* needed because
2015 // store the selection
2016 simple_cut_buffer = sel_start_cursor.par
2017 ->ParFromPos(sel_start_cursor.pos)->next;
2018 simple_cut_buffer->previous = 0;
2019 sel_end_cursor.par->previous->next = 0;
2021 // cut the selection
2022 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2023 = sel_end_cursor.par;
2025 sel_end_cursor.par->previous
2026 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2028 // care about footnotes
2029 if (simple_cut_buffer->footnoteflag) {
2030 LyXParagraph * tmppar = simple_cut_buffer;
2032 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2033 tmppar = tmppar->next;
2037 // the cut selection should begin with standard layout
2038 simple_cut_buffer->Clear();
2040 // paste the paragraphs again, if possible
2042 sel_start_cursor.par->Next()->ClearParagraph();
2043 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2045 !sel_start_cursor.par->Next()->Last())
2046 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2048 #ifndef FIX_DOUBLE_SPACE
2049 // maybe a forgotten blank
2050 if (sel_start_cursor.pos
2051 && sel_start_cursor.par
2052 ->IsLineSeparator(sel_start_cursor.pos)
2053 && sel_start_cursor.par
2054 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2055 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2060 // sometimes necessary
2062 sel_start_cursor.par->ClearParagraph();
2064 RedoParagraphs(sel_start_cursor, endpar);
2067 cursor = sel_start_cursor;
2068 SetCursor(cursor.par, cursor.pos);
2069 sel_cursor = cursor;
2070 UpdateCounters(cursor.row);
2074 void LyXText::CopySelection()
2076 // this doesnt make sense, if there is no selection
2080 // ok we have a selection. This is always between sel_start_cursor
2081 // and sel_end cursor
2082 LyXParagraph * tmppar;
2084 /* check wether there are half footnotes in the selection */
2085 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2086 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2087 tmppar = sel_start_cursor.par;
2088 while (tmppar != sel_end_cursor.par) {
2089 if (tmppar->footnoteflag !=
2090 sel_end_cursor.par->footnoteflag) {
2091 WriteAlert(_("Impossible operation"),
2092 _("Don't know what to do"
2093 " with half floats."),
2097 tmppar = tmppar->Next();
2101 /* table stuff -- begin */
2102 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2103 if ( sel_start_cursor.par != sel_end_cursor.par){
2104 WriteAlert(_("Impossible operation"),
2105 _("Don't know what to do with half tables."),
2110 /* table stuff -- end */
2112 // delete the simple_cut_buffer
2113 DeleteSimpleCutBuffer();
2115 // set the textclass
2116 simple_cut_buffer_textclass = parameters->textclass;
2118 #ifdef FIX_DOUBLE_SPACE
2119 // copy behind a space if there is one
2120 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2121 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2122 && (sel_start_cursor.par != sel_end_cursor.par
2123 || sel_start_cursor.pos < sel_end_cursor.pos))
2124 sel_start_cursor.pos++;
2126 // there are two cases: copy only within one paragraph
2127 // or more than one paragraph
2128 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2129 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2130 // only within one paragraph
2131 simple_cut_buffer = new LyXParagraph;
2132 LyXParagraph::size_type i = 0;
2133 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2134 sel_start_cursor.par->CopyIntoMinibuffer(i);
2135 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2138 // copy more than one paragraph
2139 // clone the paragraphs within the selection
2141 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2142 simple_cut_buffer = tmppar->Clone();
2143 LyXParagraph *tmppar2 = simple_cut_buffer;
2145 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2147 tmppar = tmppar->next;
2148 tmppar2->next = tmppar->Clone();
2149 tmppar2->next->previous = tmppar2;
2150 tmppar2 = tmppar2->next;
2154 // care about footnotes
2155 if (simple_cut_buffer->footnoteflag) {
2156 tmppar = simple_cut_buffer;
2158 tmppar->footnoteflag =
2159 LyXParagraph::NO_FOOTNOTE;
2160 tmppar = tmppar->next;
2164 // the simple_cut_buffer paragraph is too big
2165 LyXParagraph::size_type tmpi2 =
2166 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2167 for (; tmpi2; --tmpi2)
2168 simple_cut_buffer->Erase(0);
2170 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2172 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2173 while (tmppar2->size() > tmpi2) {
2174 tmppar2->Erase(tmppar2->text.size() - 1);
2180 void LyXText::PasteSelection()
2182 // this does not make sense, if there is nothing to paste
2183 if (!simple_cut_buffer)
2186 LyXParagraph * tmppar;
2187 LyXParagraph * endpar;
2189 LyXCursor tmpcursor;
2191 // be carefull with footnotes in footnotes
2192 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2194 // check whether the cut_buffer includes a footnote
2195 tmppar = simple_cut_buffer;
2197 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2198 tmppar = tmppar->next;
2201 WriteAlert(_("Impossible operation"),
2202 _("Can't paste float into float!"),
2208 /* table stuff -- begin */
2209 if (cursor.par->table) {
2210 if (simple_cut_buffer->next) {
2211 WriteAlert(_("Impossible operation"),
2212 _("Table cell cannot include more than one paragraph!"),
2217 /* table stuff -- end */
2219 SetUndo(Undo::INSERT,
2220 cursor.par->ParFromPos(cursor.pos)->previous,
2221 cursor.par->ParFromPos(cursor.pos)->next);
2225 // There are two cases: cutbuffer only one paragraph or many
2226 if (!simple_cut_buffer->next) {
2227 // only within a paragraph
2229 #ifndef FIX_DOUBLE_SPACE
2230 // please break behind a space, if there is one
2231 while (tmpcursor.par->Last() > tmpcursor.pos
2232 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2235 tmppar = simple_cut_buffer->Clone();
2236 /* table stuff -- begin */
2237 bool table_too_small = false;
2238 if (tmpcursor.par->table) {
2239 while (simple_cut_buffer->text.size()
2240 && !table_too_small) {
2241 if (simple_cut_buffer->IsNewline(0)){
2242 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2244 simple_cut_buffer->Erase(0);
2245 if (tmpcursor.pos < tmpcursor.par->Last())
2248 table_too_small = true;
2250 #ifdef FIX_DOUBLE_SPACE
2251 // This is an attempt to fix the
2252 // "never insert a space at the
2253 // beginning of a paragraph" problem.
2254 if (tmpcursor.pos == 0
2255 && simple_cut_buffer->IsLineSeparator(0)) {
2256 simple_cut_buffer->Erase(0);
2258 simple_cut_buffer->CutIntoMinibuffer(0);
2259 simple_cut_buffer->Erase(0);
2260 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2264 simple_cut_buffer->CutIntoMinibuffer(0);
2265 simple_cut_buffer->Erase(0);
2266 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2272 /* table stuff -- end */
2273 // Some provisions should be done here for checking
2274 // if we are inserting at the beginning of a
2275 // paragraph. If there are a space at the beginning
2276 // of the text to insert and we are inserting at
2277 // the beginning of the paragraph the space should
2279 while (simple_cut_buffer->text.size()) {
2280 #ifdef FIX_DOUBLE_SPACE
2281 // This is an attempt to fix the
2282 // "never insert a space at the
2283 // beginning of a paragraph" problem.
2284 if (tmpcursor.pos == 0
2285 && simple_cut_buffer->IsLineSeparator(0)) {
2286 simple_cut_buffer->Erase(0);
2288 simple_cut_buffer->CutIntoMinibuffer(0);
2289 simple_cut_buffer->Erase(0);
2290 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2294 simple_cut_buffer->CutIntoMinibuffer(0);
2295 simple_cut_buffer->Erase(0);
2296 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2301 delete simple_cut_buffer;
2302 simple_cut_buffer = tmppar;
2303 endpar = tmpcursor.par->Next();
2307 // make a copy of the simple cut_buffer
2308 tmppar = simple_cut_buffer;
2309 LyXParagraph * simple_cut_clone = tmppar->Clone();
2310 LyXParagraph * tmppar2 = simple_cut_clone;
2311 if (cursor.par->footnoteflag){
2312 tmppar->footnoteflag = cursor.par->footnoteflag;
2313 tmppar->footnotekind = cursor.par->footnotekind;
2315 while (tmppar->next) {
2316 tmppar = tmppar->next;
2317 tmppar2->next = tmppar->Clone();
2318 tmppar2->next->previous = tmppar2;
2319 tmppar2 = tmppar2->next;
2320 if (cursor.par->footnoteflag){
2321 tmppar->footnoteflag = cursor.par->footnoteflag;
2322 tmppar->footnotekind = cursor.par->footnotekind;
2326 // make sure there is no class difference
2327 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2328 parameters->textclass,
2331 // make the simple_cut_buffer exactly the same layout than
2332 // the cursor paragraph
2333 simple_cut_buffer->MakeSameLayout(cursor.par);
2335 // find the end of the buffer
2336 LyXParagraph * lastbuffer = simple_cut_buffer;
2337 while (lastbuffer->Next())
2338 lastbuffer = lastbuffer->Next();
2340 #ifndef FIX_DOUBLE_SPACE
2341 // Please break behind a space, if there is one. The space
2342 // should be copied too.
2343 if (cursor.par->Last() > cursor.pos
2344 && cursor.par->IsLineSeparator(cursor.pos))
2347 bool paste_the_end = false;
2349 // open the paragraph for inserting the simple_cut_buffer
2351 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2352 cursor.par->BreakParagraphConservative(cursor.pos);
2353 paste_the_end = true;
2356 #ifndef FIX_DOUBLE_SPACE
2357 // be careful with double spaces
2358 if ((!cursor.par->Last()
2359 || cursor.par->IsLineSeparator(cursor.pos - 1)
2360 || cursor.par->IsNewline(cursor.pos - 1))
2361 && simple_cut_buffer->text.size()
2362 && simple_cut_buffer->IsLineSeparator(0))
2363 simple_cut_buffer->Erase(0);
2365 // set the end for redoing later
2366 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2369 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2370 cursor.par->ParFromPos(cursor.pos)->next;
2371 cursor.par->ParFromPos(cursor.pos)->next->previous =
2372 lastbuffer->ParFromPos(lastbuffer->Last());
2374 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2375 simple_cut_buffer->previous =
2376 cursor.par->ParFromPos(cursor.pos);
2378 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2379 lastbuffer = cursor.par;
2381 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2383 // store the new cursor position
2384 tmpcursor.par = lastbuffer;
2385 tmpcursor.pos = lastbuffer->Last();
2387 // maybe some pasting
2388 if (lastbuffer->Next() && paste_the_end) {
2389 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2390 #ifndef FIX_DOUBLE_SPACE
2391 // be careful with double spaces
2392 if ((!lastbuffer->Last()
2393 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2394 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2395 && lastbuffer->Next()->Last()
2396 && lastbuffer->Next()->IsLineSeparator(0))
2397 lastbuffer->Next()->Erase(0);
2399 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2401 } else if (!lastbuffer->Next()->Last()) {
2402 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2403 #ifndef FIX_DOUBLE_SPACE
2404 // be careful witth double spaces
2405 if ((!lastbuffer->Last()
2406 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2407 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2408 && lastbuffer->Next()->Last()
2409 && lastbuffer->Next()->IsLineSeparator(0))
2410 lastbuffer->Next()->Erase(0);
2412 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2414 } else if (!lastbuffer->Last()) {
2415 lastbuffer->MakeSameLayout(lastbuffer->next);
2416 #ifndef FIX_DOUBLE_SPACE
2417 // be careful witth double spaces
2418 if ((!lastbuffer->Last()
2419 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2420 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2421 && lastbuffer->Next()->Last()
2422 && lastbuffer->Next()->IsLineSeparator(0))
2423 lastbuffer->Next()->Erase(0);
2425 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2428 lastbuffer->Next()->ClearParagraph();
2431 // restore the simple cut buffer
2432 simple_cut_buffer = simple_cut_clone;
2435 RedoParagraphs(cursor, endpar);
2437 SetCursor(cursor.par, cursor.pos);
2440 sel_cursor = cursor;
2441 SetCursor(tmpcursor.par, tmpcursor.pos);
2443 UpdateCounters(cursor.row);
2447 // returns a pointer to the very first LyXParagraph
2448 LyXParagraph * LyXText::FirstParagraph() const
2450 return params->paragraph;
2454 // returns true if the specified string is at the specified position
2455 bool LyXText::IsStringInText(LyXParagraph * par,
2456 LyXParagraph::size_type pos,
2457 char const * str) const
2461 while (pos + i < par->Last() && str[i] &&
2462 str[i] == par->GetChar(pos + i)) {
2472 // sets the selection over the number of characters of string, no check!!
2473 void LyXText::SetSelectionOverString(char const * string)
2475 sel_cursor = cursor;
2476 for (int i = 0; string[i]; ++i)
2482 // simple replacing. The font of the first selected character is used
2483 void LyXText::ReplaceSelectionWithString(char const * str)
2488 if (!selection) { // create a dummy selection
2489 sel_end_cursor = cursor;
2490 sel_start_cursor = cursor;
2493 // Get font setting before we cut
2494 LyXParagraph::size_type pos = sel_end_cursor.pos;
2495 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2497 // Insert the new string
2498 for (int i = 0; str[i]; ++i) {
2499 sel_end_cursor.par->InsertChar(pos, str[i]);
2500 sel_end_cursor.par->SetFont(pos, font);
2504 // Cut the selection
2511 // if the string can be found: return true and set the cursor to
2513 bool LyXText::SearchForward(char const * str) const
2515 LyXParagraph * par = cursor.par;
2516 LyXParagraph::size_type pos = cursor.pos;
2517 while (par && !IsStringInText(par, pos, str)) {
2518 if (pos < par->Last() - 1)
2526 SetCursor(par, pos);
2534 bool LyXText::SearchBackward(char const * string) const
2536 LyXParagraph * par = cursor.par;
2537 int pos = cursor.pos;
2543 // We skip empty paragraphs (Asger)
2545 par = par->Previous();
2547 pos = par->Last() - 1;
2548 } while (par && pos < 0);
2550 } while (par && !IsStringInText(par, pos, string));
2553 SetCursor(par, pos);
2560 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2562 char * str = new char[text.size() + 1];
2563 copy(text.begin(), text.end(), str);
2564 str[text.size()] = '\0';
2570 // needed to insert the selection
2571 void LyXText::InsertStringA(char const * s)
2574 LyXParagraph * par = cursor.par;
2575 LyXParagraph::size_type pos = cursor.pos;
2576 LyXParagraph::size_type a = 0;
2578 LyXParagraph * endpar = cursor.par->Next();
2583 textclasslist.Style(parameters->textclass,
2584 cursor.par->GetLayout()).isEnvironment();
2585 // only to be sure, should not be neccessary
2588 // insert the string, don't insert doublespace
2589 string::size_type i = 0;
2590 while (i < str.length()) {
2591 if (str[i] != '\n') {
2593 && i + 1 < str.length() && str[i + 1] != ' '
2594 && pos && par->GetChar(pos - 1)!= ' ') {
2595 par->InsertChar(pos,' ');
2597 } else if (par->table) {
2598 if (str[i] == '\t') {
2599 while((pos < par->size()) &&
2600 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2602 if (pos < par->size())
2604 else // no more fields to fill skip the rest
2606 } else if ((str[i] != 13) &&
2607 ((str[i] & 127) >= ' ')) {
2608 par->InsertChar(pos, str[i]);
2611 } else if (str[i] == ' ') {
2612 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2614 } else if (str[i] == '\t') {
2615 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2616 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2619 } else if (str[i]!= 13 &&
2620 // Ignore unprintables
2621 (str[i] & 127) >= ' ') {
2622 par->InsertChar(pos, str[i]);
2627 if (i + 1 >= str.length()) {
2631 while((pos < par->size()) &&
2632 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2635 cell = NumberOfCell(par, pos);
2636 while((pos < par->size()) &&
2637 !(par->table->IsFirstCell(cell))) {
2638 while((pos < par->size()) &&
2639 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2642 cell = NumberOfCell(par, pos);
2644 if (pos >= par->size())
2645 // no more fields to fill skip the rest
2648 if (!par->text.size()) {
2649 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2652 par->BreakParagraph(pos, flag);
2660 RedoParagraphs(cursor, endpar);
2661 SetCursor(cursor.par, cursor.pos);
2662 sel_cursor = cursor;
2663 SetCursor(par, pos);
2668 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2670 char * str = new char[text.size() + 1];
2671 copy(text.begin(), text.end(), str);
2672 str[text.size()] = '\0';
2678 /* turns double-CR to single CR, others where converted into one blank and 13s
2679 * that are ignored .Double spaces are also converted into one. Spaces at
2680 * the beginning of a paragraph are forbidden. tabs are converted into one
2681 * space. then InsertStringA is called */
2682 void LyXText::InsertStringB(char const * s)
2685 LyXParagraph * par = cursor.par;
2686 string::size_type i = 1;
2687 while (i < str.length()) {
2688 if (str[i] == '\t' && !par->table)
2690 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2692 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2693 if (str[i + 1] != '\n') {
2694 if (str[i - 1] != ' ')
2699 while (i + 1 < str.length()
2700 && (str[i + 1] == ' '
2701 || str[i + 1] == '\t'
2702 || str[i + 1] == '\n'
2703 || str[i + 1] == 13)) {
2710 InsertStringA(str.c_str());
2714 bool LyXText::GotoNextError() const
2716 LyXCursor res = cursor;
2718 if (res.pos < res.par->Last() - 1) {
2722 res.par = res.par->Next();
2727 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2728 && res.par->GetInset(res.pos)->AutoDelete()));
2731 SetCursor(res.par, res.pos);
2738 bool LyXText::GotoNextNote() const
2740 LyXCursor res = cursor;
2742 if (res.pos < res.par->Last() - 1) {
2745 res.par = res.par->Next();
2750 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2751 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2754 SetCursor(res.par, res.pos);
2761 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2765 if (!par || class1 == class2)
2767 par = par->FirstPhysicalPar();
2769 string name = textclasslist.NameOfLayout(class1, par->layout);
2771 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2772 textclasslist.NumberOfLayout(class2, name);
2775 } else { // layout not found
2776 // use default layout "Standard" (0)
2781 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2783 string s = "Layout had to be changed from\n"
2784 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2785 + "\nbecause of class conversion from\n"
2786 + textclasslist.NameOfClass(class1) + " to "
2787 + textclasslist.NameOfClass(class2);
2788 InsetError * new_inset = new InsetError(s);
2789 par->InsertChar(0, LyXParagraph::META_INSET);
2790 par->InsertInset(0, new_inset);
2799 void LyXText::CheckParagraph(LyXParagraph * par,
2800 LyXParagraph::size_type pos)
2803 LyXCursor tmpcursor;
2805 /* table stuff -- begin*/
2808 CheckParagraphInTable(par, pos);
2811 /* table stuff -- end*/
2814 LyXParagraph::size_type z;
2815 Row * row = GetRow(par, pos, y);
2817 // is there a break one row above
2818 if (row->previous && row->previous->par == row->par) {
2819 z = NextBreakPoint(row->previous, paperwidth);
2820 if ( z >= row->pos) {
2821 // set the dimensions of the row above
2822 y -= row->previous->height;
2824 refresh_row = row->previous;
2825 status = LyXText::NEED_MORE_REFRESH;
2827 BreakAgain(row->previous);
2829 // set the cursor again. Otherwise
2830 // dangling pointers are possible
2831 SetCursor(cursor.par, cursor.pos);
2832 sel_cursor = cursor;
2837 int tmpheight = row->height;
2838 LyXParagraph::size_type tmplast = RowLast(row);
2843 if (row->height == tmpheight && RowLast(row) == tmplast)
2844 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2846 status = LyXText::NEED_MORE_REFRESH;
2848 // check the special right address boxes
2849 if (textclasslist.Style(parameters->textclass,
2850 par->GetLayout()).margintype
2851 == MARGIN_RIGHT_ADDRESS_BOX) {
2852 tmpcursor.par = par;
2853 tmpcursor.row = row;
2856 tmpcursor.x_fix = 0;
2857 tmpcursor.pos = pos;
2858 RedoDrawingOfParagraph(tmpcursor);
2863 // set the cursor again. Otherwise dangling pointers are possible
2864 // also set the selection
2868 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2869 sel_cursor = cursor;
2870 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2871 sel_start_cursor = cursor;
2872 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2873 sel_end_cursor = cursor;
2874 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2875 last_sel_cursor = cursor;
2878 SetCursorIntern(cursor.par, cursor.pos);
2882 // returns 0 if inset wasn't found
2883 int LyXText::UpdateInset(Inset * inset)
2885 // first check the current paragraph
2886 int pos = cursor.par->GetPositionOfInset(inset);
2888 CheckParagraph(cursor.par, pos);
2892 // check every paragraph
2894 LyXParagraph * par = FirstParagraph();
2896 // make sure the paragraph is open
2897 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2898 pos = par->GetPositionOfInset(inset);
2900 CheckParagraph(par, pos);
2911 void LyXText::SetCursor(LyXParagraph * par,
2912 LyXParagraph::size_type pos, bool setfont) const
2914 LyXCursor old_cursor = cursor;
2915 SetCursorIntern(par, pos, setfont);
2916 DeleteEmptyParagraphMechanism(old_cursor);
2920 void LyXText::SetCursorIntern(LyXParagraph * par,
2921 LyXParagraph::size_type pos, bool setfont) const
2925 LyXParagraph * tmppar;
2926 LyXParagraph::size_type vpos,cursor_vpos;
2928 // correct the cursor position if impossible
2929 if (pos > par->Last()){
2930 tmppar = par->ParFromPos(pos);
2931 pos = par->PositionInParFromPos(pos);
2934 if (par->IsDummy() && par->previous &&
2935 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2936 while (par->previous &&
2937 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2938 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2939 par = par->previous ;
2940 if (par->IsDummy() &&
2941 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2942 pos += par->text.size() + 1;
2944 if (par->previous) {
2945 par = par->previous;
2947 pos += par->text.size() + 1;
2955 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2956 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2957 && !cursor.par->IsSeparator(cursor.pos))
2958 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2960 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2961 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2963 current_font = cursor.par->GetFontSettings(cursor.pos);
2964 real_current_font = GetFont(cursor.par, cursor.pos);
2965 if (pos == 0 && par->size() == 0
2966 && current_view->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2967 current_font.setDirection(LyXFont::RTL_DIR);
2968 real_current_font.setDirection(LyXFont::RTL_DIR);
2972 /* get the cursor y position in text */
2973 row = GetRow(par, pos, y);
2974 /* y is now the beginning of the cursor row */
2976 /* y is now the cursor baseline */
2979 /* now get the cursors x position */
2981 float fill_separator, fill_hfill, fill_label_hfill;
2982 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2984 LyXParagraph::size_type last = RowLast(row);
2985 if (row->pos > last)
2987 else if (pos <= last ) {
2988 LyXDirection letter_direction =
2989 row->par->getLetterDirection(pos);
2990 LyXDirection font_direction =
2991 real_current_font.getFontDirection();
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 = (row->par->getLetterDirection(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;
3381 LyXParagraph * LyXText::GetParFromID(int id)
3383 LyXParagraph * result = FirstParagraph();
3384 while (result && result->id() != id)
3385 result = result->next;
3391 bool LyXText::TextUndo()
3393 // returns false if no undo possible
3394 Undo * undo = params->undostack.pop();
3399 .push(CreateUndo(undo->kind,
3400 GetParFromID(undo->number_of_before_par),
3401 GetParFromID(undo->number_of_behind_par)));
3403 return TextHandleUndo(undo);
3407 bool LyXText::TextRedo()
3409 // returns false if no redo possible
3410 Undo * undo = params->redostack.pop();
3415 .push(CreateUndo(undo->kind,
3416 GetParFromID(undo->number_of_before_par),
3417 GetParFromID(undo->number_of_behind_par)));
3419 return TextHandleUndo(undo);
3423 bool LyXText::TextHandleUndo(Undo * undo)
3425 // returns false if no undo possible
3426 bool result = false;
3428 LyXParagraph * before =
3429 GetParFromID(undo->number_of_before_par);
3430 LyXParagraph * behind =
3431 GetParFromID(undo->number_of_behind_par);
3432 LyXParagraph * tmppar;
3433 LyXParagraph * tmppar2;
3434 LyXParagraph * tmppar3;
3435 LyXParagraph * tmppar4;
3436 LyXParagraph * endpar;
3437 LyXParagraph * tmppar5;
3439 // if there's no before take the beginning
3440 // of the document for redoing
3442 SetCursorIntern(FirstParagraph(), 0);
3444 // replace the paragraphs with the undo informations
3446 tmppar3 = undo->par;
3447 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3450 while (tmppar4->next)
3451 tmppar4 = tmppar4->next;
3452 } // get last undo par
3454 // now remove the old text if there is any
3455 if (before != behind || (!behind && !before)){
3457 tmppar5 = before->next;
3459 tmppar5 = params->paragraph;
3461 while (tmppar5 && tmppar5 != behind){
3463 tmppar5 = tmppar5->next;
3464 // a memory optimization for edit: Only layout information
3465 // is stored in the undo. So restore the text informations.
3466 if (undo->kind == Undo::EDIT){
3467 tmppar2->text = tmppar->text;
3468 tmppar->text.clear();
3469 tmppar2 = tmppar2->next;
3471 if ( currentrow && currentrow->par == tmppar )
3472 currentrow = currentrow -> previous;
3473 // Commenting out this might remove the error
3474 // reported by Purify, but it might also
3475 // introduce a memory leak. We need to
3481 // put the new stuff in the list if there is one
3484 before->next = tmppar3;
3486 params->paragraph = tmppar3;
3487 tmppar3->previous = before;
3491 params->paragraph = behind;
3494 tmppar4->next = behind;
3496 behind->previous = tmppar4;
3500 // Set the cursor for redoing
3502 SetCursorIntern(before->FirstSelfrowPar(), 0);
3503 // check wether before points to a closed float and open it if necessary
3504 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3505 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3507 while (tmppar4->previous &&
3508 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3509 tmppar4 = tmppar4->previous;
3510 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3511 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3512 tmppar4 = tmppar4->next;
3517 // open a cosed footnote at the end if necessary
3518 if (behind && behind->previous &&
3519 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3520 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3521 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3522 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3523 behind = behind->next;
3527 // calculate the endpar for redoing the paragraphs.
3529 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3530 endpar = behind->LastPhysicalPar()->Next();
3532 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3537 tmppar = GetParFromID(undo->number_of_cursor_par);
3538 RedoParagraphs(cursor, endpar);
3540 SetCursorIntern(tmppar, undo->cursor_pos);
3541 UpdateCounters(cursor.row);
3551 void LyXText::FinishUndo()
3553 // makes sure the next operation will be stored
3554 undo_finished = True;
3558 void LyXText::FreezeUndo()
3560 // this is dangerous and for internal use only
3565 void LyXText::UnFreezeUndo()
3567 // this is dangerous and for internal use only
3568 undo_frozen = false;
3572 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3573 LyXParagraph const * behind) const
3576 params->undostack.push(CreateUndo(kind, before, behind));
3577 params->redostack.clear();
3581 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3582 LyXParagraph const * behind)
3584 params->redostack.push(CreateUndo(kind, before, behind));
3588 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3589 LyXParagraph const * behind) const
3591 int before_number = -1;
3592 int behind_number = -1;
3594 before_number = before->id();
3596 behind_number = behind->id();
3597 // Undo::EDIT and Undo::FINISH are
3598 // always finished. (no overlapping there)
3599 // overlapping only with insert and delete inside one paragraph:
3600 // Nobody wants all removed character
3601 // appear one by one when undoing.
3602 // EDIT is special since only layout information, not the
3603 // contents of a paragaph are stored.
3604 if (!undo_finished && kind != Undo::EDIT &&
3605 kind != Undo::FINISH){
3606 // check wether storing is needed
3607 if (!params->undostack.empty() &&
3608 params->undostack.top()->kind == kind &&
3609 params->undostack.top()->number_of_before_par == before_number &&
3610 params->undostack.top()->number_of_behind_par == behind_number ){
3615 // create a new Undo
3616 LyXParagraph * undopar;
3617 LyXParagraph * tmppar;
3618 LyXParagraph * tmppar2;
3620 LyXParagraph * start = 0;
3621 LyXParagraph * end = 0;
3624 start = before->next;
3626 start = FirstParagraph();
3628 end = behind->previous;
3630 end = FirstParagraph();
3636 && start != end->next
3637 && (before != behind || (!before && !behind))) {
3639 tmppar2 = tmppar->Clone();
3640 tmppar2->id(tmppar->id());
3642 // a memory optimization: Just store the layout information
3644 if (kind == Undo::EDIT){
3645 tmppar2->text.clear();
3650 while (tmppar != end && tmppar->next) {
3651 tmppar = tmppar->next;
3652 tmppar2->next = tmppar->Clone();
3653 tmppar2->next->id(tmppar->id());
3654 // a memory optimization: Just store the layout
3655 // information when only edit
3656 if (kind == Undo::EDIT){
3657 tmppar2->next->text.clear();
3659 tmppar2->next->previous = tmppar2;
3660 tmppar2 = tmppar2->next;
3664 undopar = 0; // nothing to replace (undo of delete maybe)
3666 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3667 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3669 Undo * undo = new Undo(kind,
3670 before_number, behind_number,
3671 cursor_par, cursor_pos,
3674 undo_finished = false;
3679 void LyXText::SetCursorParUndo()
3681 SetUndo(Undo::FINISH,
3682 cursor.par->ParFromPos(cursor.pos)->previous,
3683 cursor.par->ParFromPos(cursor.pos)->next);
3687 void LyXText::RemoveTableRow(LyXCursor * cur) const
3693 // move to the previous row
3694 int cell_act = NumberOfCell(cur->par, cur->pos);
3697 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3700 !cur->par->table->IsFirstCell(cell_act)) {
3702 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3707 // now we have to pay attention if the actual table is the
3708 // main row of TableContRows and if yes to delete all of them
3713 // delete up to the next row
3714 while (cur->pos < cur->par->Last() &&
3716 || !cur->par->table->IsFirstCell(cell_act))) {
3717 while (cur->pos < cur->par->Last() &&
3718 !cur->par->IsNewline(cur->pos))
3719 cur->par->Erase(cur->pos);
3722 if (cur->pos < cur->par->Last())
3723 cur->par->Erase(cur->pos);
3725 if (cur->pos && cur->pos == cur->par->Last()) {
3727 cur->par->Erase(cur->pos); // no newline at very end!
3729 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3730 !cur->par->table->IsContRow(cell_org) &&
3731 cur->par->table->IsContRow(cell));
3732 cur->par->table->DeleteRow(cell_org);
3737 bool LyXText::IsEmptyTableCell() const
3739 LyXParagraph::size_type pos = cursor.pos - 1;
3740 while (pos >= 0 && pos < cursor.par->Last()
3741 && !cursor.par->IsNewline(pos))
3743 return cursor.par->IsNewline(pos + 1);
3747 void LyXText::toggleAppendix(){
3748 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3749 bool start = !par->start_of_appendix;
3751 // ensure that we have only one start_of_appendix in this document
3752 LyXParagraph * tmp = FirstParagraph();
3753 for (; tmp; tmp = tmp->next)
3754 tmp->start_of_appendix = 0;
3755 par->start_of_appendix = start;
3757 // we can set the refreshing parameters now
3758 status = LyXText::NEED_MORE_REFRESH;
3760 refresh_row = 0; // not needed for full update
3762 SetCursor(cursor.par, cursor.pos);