1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include FORMS_H_LOCATION
18 #pragma implementation "lyxtext.h"
22 #include "lyxparagraph.h"
23 #include "insets/inseterror.h"
26 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
39 #define FIX_DOUBLE_SPACE 1
41 extern BufferView * current_view;
45 LyXText::LyXText(int pw, Buffer * p)
52 parameters = &p->params;
56 status = LyXText::UNCHANGED;
57 LyXParagraph * par = p->paragraph;
58 current_font = GetFont(par, 0);
63 InsertParagraph(par, lastrow);
66 // set cursor at the very top position
67 selection = true; /* these setting is necessary
68 because of the delete-empty-
69 paragraph mechanism in
71 SetCursor(firstrow->par, 0);
76 // no rebreak necessary
82 // Default layouttype for copy environment type
89 // Delete all rows, this does not touch the paragraphs!
90 Row * tmprow = firstrow;
92 tmprow = firstrow->next;
99 // Gets the fully instantiated font at a given position in a paragraph
100 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
101 // The difference is that this one is used for displaying, and thus we
102 // are allowed to make cosmetic improvements. For instance make footnotes
104 // If position is -1, we get the layout font of the paragraph.
105 // If position is -2, we get the font of the manual label of the paragraph.
106 LyXFont LyXText::GetFont(LyXParagraph * par,
107 LyXParagraph::size_type pos) const
109 LyXLayout const & layout =
110 textclasslist.Style(parameters->textclass, par->GetLayout());
112 char par_depth = par->GetDepth();
113 // We specialize the 95% common case:
114 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
117 if (layout.labeltype == LABEL_MANUAL
118 && pos < BeginningOfMainBody(par)) {
120 return par->GetFontSettings(pos).
121 realize(layout.reslabelfont);
123 return par->GetFontSettings(pos).
124 realize(layout.resfont);
127 // process layoutfont for pos == -1 and labelfont for pos < -1
129 return layout.resfont;
131 return layout.reslabelfont;
135 // The uncommon case need not be optimized as much
137 LyXFont layoutfont, tmpfont;
141 if (pos < BeginningOfMainBody(par)) {
143 layoutfont = layout.labelfont;
146 layoutfont = layout.font;
148 tmpfont = par->GetFontSettings(pos);
149 tmpfont.realize(layoutfont);
152 // process layoutfont for pos == -1 and labelfont for pos < -1
154 tmpfont = layout.font;
156 tmpfont = layout.labelfont;
159 // Resolve against environment font information
160 while (par && par_depth && !tmpfont.resolved()) {
161 par = par->DepthHook(par_depth - 1);
163 tmpfont.realize(textclasslist.
164 Style(parameters->textclass,
165 par->GetLayout()).font);
166 par_depth = par->GetDepth();
170 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
172 // Cosmetic improvement: If this is an open footnote, make the font
174 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
175 && par->footnotekind == LyXParagraph::FOOTNOTE) {
183 void LyXText::SetCharFont(LyXParagraph * par,
184 LyXParagraph::size_type pos,
188 // Let the insets convert their font
189 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
190 if (par->GetInset(pos))
191 font = par->GetInset(pos)->ConvertFont(font);
194 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
197 // Get concrete layout font to reduce against
200 if (pos < BeginningOfMainBody(par))
201 layoutfont = layout.labelfont;
203 layoutfont = layout.font;
205 // Realize against environment font information
206 if (par->GetDepth()){
207 LyXParagraph * tp = par;
208 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
209 tp = tp->DepthHook(tp->GetDepth()-1);
211 layoutfont.realize(textclasslist.
212 Style(parameters->textclass,
213 tp->GetLayout()).font);
217 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
219 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
220 && par->footnotekind == LyXParagraph::FOOTNOTE) {
221 layoutfont.decSize();
224 // Now, reduce font against full layout font
225 font.reduce(layoutfont);
227 par->SetFont(pos, font);
231 /* inserts a new row behind the specified row, increments
232 * the touched counters */
233 void LyXText::InsertRow(Row * row, LyXParagraph * par,
234 LyXParagraph::size_type pos) const
236 Row * tmprow = new Row;
238 tmprow->previous = 0;
239 tmprow->next = firstrow;
243 tmprow->previous = row;
244 tmprow->next = row->next;
249 tmprow->next->previous = tmprow;
251 if (tmprow->previous)
252 tmprow->previous->next = tmprow;
260 ++number_of_rows; // one more row
264 // removes the row and reset the touched counters
265 void LyXText::RemoveRow(Row * row) const
267 /* this must not happen before the currentrow for clear reasons.
268 so the trick is just to set the current row onto the previous
271 GetRow(row->par, row->pos, unused_y);
272 currentrow = currentrow->previous;
274 currentrow_y -= currentrow->height;
279 row->next->previous = row->previous;
280 if (!row->previous) {
281 firstrow = row->next;
284 row->previous->next = row->next;
287 lastrow = row->previous;
289 height -= row->height; // the text becomes smaller
292 --number_of_rows; // one row less
296 // remove all following rows of the paragraph of the specified row.
297 void LyXText::RemoveParagraph(Row * row) const
299 LyXParagraph * tmppar = row->par;
303 while (row && row->par == tmppar) {
311 // insert the specified paragraph behind the specified row
312 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
314 InsertRow(row, par, 0); /* insert a new row, starting
317 SetCounter(par); // set the counters
319 // and now append the whole paragraph behind the new row
321 firstrow->height = 0;
322 AppendParagraph(firstrow);
325 row->next->height = 0;
326 AppendParagraph(row->next);
331 void LyXText::ToggleFootnote()
333 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
335 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
337 current_view->owner()->getMiniBuffer()->Set(_("Opened float"));
339 current_view->owner()->getMiniBuffer()->Set(_("Closed float"));
345 void LyXText::OpenStuff()
347 if (cursor.pos == 0 && cursor.par->bibkey){
348 cursor.par->bibkey->Edit(0, 0);
350 else if (cursor.pos < cursor.par->Last()
351 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
352 && cursor.par->GetInset(cursor.pos)->Editable()) {
353 current_view->owner()->getMiniBuffer()
354 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
355 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
357 cursor.par->GetInset(cursor.pos)->Edit(0, 0);
364 void LyXText::CloseFootnote()
366 LyXParagraph * tmppar;
367 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
369 // if the cursor is not in an open footnote, or
370 // there is no open footnote in this paragraph, just return.
371 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
374 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
375 current_view->owner()->getMiniBuffer()
376 ->Set(_("Nothing to do"));
380 // ok, move the cursor right before the footnote
381 // just a little faster than using CursorRight()
383 cursor.par->ParFromPos(cursor.pos) != par;
387 // now the cursor is at the beginning of the physical par
388 SetCursor(cursor.par,
390 cursor.par->ParFromPos(cursor.pos)->text.size());
392 /* we are in a footnote, so let us move at the beginning */
393 /* this is just faster than using just CursorLeft() */
396 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
397 // just a little bit faster than movin the cursor
398 tmppar = tmppar->Previous();
400 SetCursor(tmppar, tmppar->Last());
403 // the cursor must be exactly before the footnote
404 par = cursor.par->ParFromPos(cursor.pos);
406 status = LyXText::NEED_MORE_REFRESH;
407 refresh_row = cursor.row;
408 refresh_y = cursor.y - cursor.row->baseline;
411 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
412 Row * row = cursor.row;
414 tmppar->CloseFootnote(cursor.pos);
416 while (tmppar != endpar) {
417 RemoveRow(row->next);
419 tmppar = row->next->par;
424 AppendParagraph(cursor.row);
426 SetCursor(cursor.par, cursor.pos);
430 if (cursor.row->next)
431 SetHeightOfRow(cursor.row->next);
435 /* used in setlayout */
436 // Asger is not sure we want to do this...
437 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
439 LyXFont layoutfont, tmpfont;
441 LyXLayout const & layout =
442 textclasslist.Style(parameters->textclass, par->GetLayout());
444 for (LyXParagraph::size_type pos = 0;
445 pos < par->Last(); ++pos) {
446 if (pos < BeginningOfMainBody(par))
447 layoutfont = layout.labelfont;
449 layoutfont = layout.font;
451 tmpfont = par->GetFontSettings(pos);
452 tmpfont.reduce(layoutfont);
453 par->SetFont(pos, tmpfont);
458 // set layout over selection and make a total rebreak of those paragraphs
459 void LyXText::SetLayout(char layout)
463 // if there is no selection just set the layout
464 // of the current paragraph */
466 sel_start_cursor = cursor; // dummy selection
467 sel_end_cursor = cursor;
470 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
471 LyXParagraph * undoendpar = endpar;
473 if (endpar && endpar->GetDepth()) {
474 while (endpar && endpar->GetDepth()) {
475 endpar = endpar->LastPhysicalPar()->Next();
480 endpar = endpar->Next(); // because of parindents etc.
484 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
487 tmpcursor = cursor; /* store the current cursor */
489 /* ok we have a selection. This is always between sel_start_cursor
490 * and sel_end cursor */
491 cursor = sel_start_cursor;
493 LyXLayout const & lyxlayout =
494 textclasslist.Style(parameters->textclass, layout);
496 while (cursor.par != sel_end_cursor.par) {
497 if (cursor.par->footnoteflag ==
498 sel_start_cursor.par->footnoteflag) {
499 cursor.par->SetLayout(layout);
500 MakeFontEntriesLayoutSpecific(cursor.par);
501 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
502 fppar->added_space_top = lyxlayout.fill_top ?
503 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
504 fppar->added_space_bottom = lyxlayout.fill_bottom ?
505 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
506 if (lyxlayout.margintype == MARGIN_MANUAL)
507 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
508 if (lyxlayout.labeltype != LABEL_BIBLIO
510 delete fppar->bibkey;
514 cursor.par = cursor.par->Next();
516 if (cursor.par->footnoteflag ==
517 sel_start_cursor.par->footnoteflag) {
518 cursor.par->SetLayout(layout);
519 MakeFontEntriesLayoutSpecific(cursor.par);
520 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
521 fppar->added_space_top = lyxlayout.fill_top ?
522 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
523 fppar->added_space_bottom = lyxlayout.fill_bottom ?
524 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
525 if (lyxlayout.margintype == MARGIN_MANUAL)
526 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
527 if (lyxlayout.labeltype != LABEL_BIBLIO
529 delete fppar->bibkey;
534 RedoParagraphs(sel_start_cursor, endpar);
536 // we have to reset the selection, because the
537 // geometry could have changed */
538 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
540 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
541 UpdateCounters(cursor.row);
544 SetCursor(tmpcursor.par, tmpcursor.pos);
548 // increment depth over selection and
549 // make a total rebreak of those paragraphs
550 void LyXText::IncDepth()
552 // If there is no selection, just use the current paragraph
554 sel_start_cursor = cursor; // dummy selection
555 sel_end_cursor = cursor;
558 // We end at the next paragraph with depth 0
559 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
560 LyXParagraph * undoendpar = endpar;
562 if (endpar && endpar->GetDepth()) {
563 while (endpar && endpar->GetDepth()) {
564 endpar = endpar->LastPhysicalPar()->Next();
569 endpar = endpar->Next(); // because of parindents etc.
574 .par->ParFromPos(sel_start_cursor.pos)->previous,
577 LyXCursor tmpcursor = cursor; // store the current cursor
579 // ok we have a selection. This is always between sel_start_cursor
580 // and sel_end cursor
581 cursor = sel_start_cursor;
583 bool anything_changed = false;
586 // NOTE: you can't change the depth of a bibliography entry
587 if (cursor.par->footnoteflag ==
588 sel_start_cursor.par->footnoteflag
589 && textclasslist.Style(parameters->textclass,
590 cursor.par->GetLayout()
591 ).labeltype != LABEL_BIBLIO) {
592 LyXParagraph * prev =
593 cursor.par->FirstPhysicalPar()->Previous();
595 && (prev->GetDepth() - cursor.par->GetDepth() > 0
596 || (prev->GetDepth() == cursor.par->GetDepth()
597 && textclasslist.Style(parameters->textclass,
598 prev->GetLayout()).isEnvironment()))) {
599 cursor.par->FirstPhysicalPar()->depth++;
600 anything_changed = true;
603 if (cursor.par == sel_end_cursor.par)
605 cursor.par = cursor.par->Next();
608 // if nothing changed set all depth to 0
609 if (!anything_changed) {
610 cursor = sel_start_cursor;
611 while (cursor.par != sel_end_cursor.par) {
612 cursor.par->FirstPhysicalPar()->depth = 0;
613 cursor.par = cursor.par->Next();
615 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
616 cursor.par->FirstPhysicalPar()->depth = 0;
619 RedoParagraphs(sel_start_cursor, endpar);
621 // we have to reset the selection, because the
622 // geometry could have changed
623 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
625 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
626 UpdateCounters(cursor.row);
629 SetCursor(tmpcursor.par, tmpcursor.pos);
633 // decrement depth over selection and
634 // make a total rebreak of those paragraphs
635 void LyXText::DecDepth()
637 // if there is no selection just set the layout
638 // of the current paragraph
640 sel_start_cursor = cursor; // dummy selection
641 sel_end_cursor = cursor;
644 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
645 LyXParagraph * undoendpar = endpar;
647 if (endpar && endpar->GetDepth()) {
648 while (endpar && endpar->GetDepth()) {
649 endpar = endpar->LastPhysicalPar()->Next();
654 endpar = endpar->Next(); // because of parindents etc.
659 .par->ParFromPos(sel_start_cursor.pos)->previous,
662 LyXCursor tmpcursor = cursor; // store the current cursor
664 // ok we have a selection. This is always between sel_start_cursor
665 // and sel_end cursor
666 cursor = sel_start_cursor;
669 if (cursor.par->footnoteflag ==
670 sel_start_cursor.par->footnoteflag) {
671 if (cursor.par->FirstPhysicalPar()->depth)
672 cursor.par->FirstPhysicalPar()->depth--;
674 if (cursor.par == sel_end_cursor.par)
676 cursor.par = cursor.par->Next();
679 RedoParagraphs(sel_start_cursor, endpar);
681 // we have to reset the selection, because the
682 // geometry could have changed
683 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
685 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
686 UpdateCounters(cursor.row);
689 SetCursor(tmpcursor.par, tmpcursor.pos);
693 // set font over selection and make a total rebreak of those paragraphs
694 void LyXText::SetFont(LyXFont const & font, bool toggleall)
696 // if there is no selection just set the current_font
698 // Determine basis font
700 if (cursor.pos < BeginningOfMainBody(cursor.par))
701 layoutfont = GetFont(cursor.par, -2);
703 layoutfont = GetFont(cursor.par, -1);
704 // Update current font
705 real_current_font.update(font, toggleall);
707 // Reduce to implicit settings
708 current_font = real_current_font;
709 current_font.reduce(layoutfont);
710 // And resolve it completely
711 real_current_font.realize(layoutfont);
715 LyXCursor tmpcursor = cursor; // store the current cursor
717 // ok we have a selection. This is always between sel_start_cursor
718 // and sel_end cursor
721 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
722 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
723 cursor = sel_start_cursor;
724 while (cursor.par != sel_end_cursor.par ||
725 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
726 && cursor.pos < sel_end_cursor.pos))
728 if (cursor.pos < cursor.par->Last()
729 && cursor.par->footnoteflag
730 == sel_start_cursor.par->footnoteflag) {
731 // an open footnote should behave
733 LyXFont newfont = GetFont(cursor.par, cursor.pos);
734 newfont.update(font, toggleall);
735 SetCharFont(cursor.par, cursor.pos, newfont);
739 cursor.par = cursor.par->Next();
743 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
745 // we have to reset the selection, because the
746 // geometry could have changed
747 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
749 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
752 SetCursor(tmpcursor.par, tmpcursor.pos);
756 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
758 Row * tmprow = cur.row;
759 long y = cur.y - tmprow->baseline;
761 SetHeightOfRow(tmprow);
762 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
763 // find the first row of the paragraph
764 if (first_phys_par != tmprow->par)
765 while (tmprow->previous
766 && tmprow->previous->par != first_phys_par) {
767 tmprow = tmprow->previous;
769 SetHeightOfRow(tmprow);
771 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
772 tmprow = tmprow->previous;
774 SetHeightOfRow(tmprow);
777 // we can set the refreshing parameters now
778 status = LyXText::NEED_MORE_REFRESH;
780 refresh_row = tmprow;
781 SetCursor(cur.par, cur.pos);
785 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
787 Row * tmprow = cur.row;
789 long y = cur.y - tmprow->baseline;
790 SetHeightOfRow(tmprow);
791 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
792 // find the first row of the paragraph
793 if (first_phys_par != tmprow->par)
794 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
795 tmprow = tmprow->previous;
798 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
799 tmprow = tmprow->previous;
803 // we can set the refreshing parameters now
804 if (status == LyXText::UNCHANGED || y < refresh_y) {
806 refresh_row = tmprow;
808 status = LyXText::NEED_MORE_REFRESH;
809 SetCursor(cur.par, cur.pos);
813 /* deletes and inserts again all paragaphs between the cursor
814 * and the specified par
815 * This function is needed after SetLayout and SetFont etc. */
816 void LyXText::RedoParagraphs(LyXCursor const & cur,
817 LyXParagraph const * endpar) const
820 LyXParagraph * tmppar, * first_phys_par;
822 Row * tmprow = cur.row;
824 long y = cur.y - tmprow->baseline;
826 if (!tmprow->previous){
827 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
829 first_phys_par = tmprow->par->FirstPhysicalPar();
830 // find the first row of the paragraph
831 if (first_phys_par != tmprow->par)
832 while (tmprow->previous
833 && tmprow->previous->par != first_phys_par) {
834 tmprow = tmprow->previous;
837 while (tmprow->previous
838 && tmprow->previous->par == first_phys_par) {
839 tmprow = tmprow->previous;
844 // we can set the refreshing parameters now
845 status = LyXText::NEED_MORE_REFRESH;
847 refresh_row = tmprow->previous; /* the real refresh row will
848 be deleted, so I store
852 tmppar = tmprow->next->par;
855 while (tmppar != endpar) {
856 RemoveRow(tmprow->next);
858 tmppar = tmprow->next->par;
863 // remove the first one
864 tmprow2 = tmprow; /* this is because tmprow->previous
866 tmprow = tmprow->previous;
869 tmppar = first_phys_par;
873 InsertParagraph(tmppar, tmprow);
876 while (tmprow->next && tmprow->next->par == tmppar)
877 tmprow = tmprow->next;
878 tmppar = tmppar->Next();
880 } while (tmppar != endpar);
882 // this is because of layout changes
884 refresh_y -= refresh_row->height;
885 SetHeightOfRow(refresh_row);
887 refresh_row = firstrow;
889 SetHeightOfRow(refresh_row);
892 if (tmprow && tmprow->next)
893 SetHeightOfRow(tmprow->next);
897 int LyXText::FullRebreak()
899 if (need_break_row) {
900 BreakAgain(need_break_row);
908 /* important for the screen */
911 /* the cursor set functions have a special mechanism. When they
912 * realize, that you left an empty paragraph, they will delete it.
913 * They also delet the corresponding row */
915 // need the selection cursor:
916 void LyXText::SetSelection()
919 last_sel_cursor = sel_cursor;
920 sel_start_cursor = sel_cursor;
921 sel_end_cursor = sel_cursor;
926 // first the toggling area
927 if (cursor.y < last_sel_cursor.y ||
928 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
929 toggle_end_cursor = last_sel_cursor;
930 toggle_cursor = cursor;
933 toggle_end_cursor = cursor;
934 toggle_cursor = last_sel_cursor;
937 last_sel_cursor = cursor;
939 // and now the whole selection
941 if (sel_cursor.par == cursor.par)
942 if (sel_cursor.pos < cursor.pos) {
943 sel_end_cursor = cursor;
944 sel_start_cursor = sel_cursor;
946 sel_end_cursor = sel_cursor;
947 sel_start_cursor = cursor;
949 else if (sel_cursor.y < cursor.y ||
950 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
951 sel_end_cursor = cursor;
952 sel_start_cursor = sel_cursor;
955 sel_end_cursor = sel_cursor;
956 sel_start_cursor = cursor;
959 // a selection with no contents is not a selection
960 if (sel_start_cursor.x == sel_end_cursor.x &&
961 sel_start_cursor.y == sel_end_cursor.y)
966 void LyXText::ClearSelection() const
973 void LyXText::CursorHome() const
975 SetCursor(cursor.par, cursor.row->pos);
979 void LyXText::CursorEnd() const
981 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
982 SetCursor(cursor.par, RowLast(cursor.row) + 1);
984 if (cursor.par->Last() &&
985 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
986 || cursor.par->IsNewline(RowLast(cursor.row))))
987 SetCursor(cursor.par, RowLast(cursor.row));
989 SetCursor(cursor.par, RowLast(cursor.row) + 1);
991 if (cursor.par->table) {
992 int cell = NumberOfCell(cursor.par, cursor.pos);
993 if (cursor.par->table->RowHasContRow(cell) &&
994 cursor.par->table->CellHasContRow(cell)<0) {
995 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
996 SetCursor(cursor.par, RowLast(cursor.row) + 1);
998 if (cursor.par->Last() &&
999 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1000 || cursor.par->IsNewline(RowLast(cursor.row))))
1001 SetCursor(cursor.par, RowLast(cursor.row));
1003 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1010 void LyXText::CursorTop() const
1012 while (cursor.par->Previous())
1013 cursor.par = cursor.par->Previous();
1014 SetCursor(cursor.par, 0);
1018 void LyXText::CursorBottom() const
1020 while (cursor.par->Next())
1021 cursor.par = cursor.par->Next();
1022 SetCursor(cursor.par, cursor.par->Last());
1026 /* returns a pointer to the row near the specified y-coordinate
1027 * (relative to the whole text). y is set to the real beginning
1029 Row * LyXText::GetRowNearY(long & y) const
1035 tmprow = currentrow;
1036 tmpy = currentrow_y;
1043 while (tmprow->next && tmpy + tmprow->height <= y) {
1044 tmpy += tmprow->height;
1045 tmprow = tmprow->next;
1048 while (tmprow->previous && tmpy > y) {
1049 tmprow = tmprow->previous;
1050 tmpy -= tmprow->height;
1053 currentrow = tmprow;
1054 currentrow_y = tmpy;
1056 y = tmpy; // return the real y
1061 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1063 // If the mask is completely neutral, tell user
1064 if (font == LyXFont(LyXFont::ALL_IGNORE)){
1065 // Could only happen with user style
1066 current_view->owner()->getMiniBuffer()
1067 ->Set(_("No font change defined. Use Character under"
1068 " the Layout menu to define font change."));
1072 // Try implicit word selection
1073 LyXCursor resetCursor = cursor;
1074 int implicitSelection = SelectWordWhenUnderCursor();
1077 SetFont(font, toggleall);
1079 /* Implicit selections are cleared afterwards and cursor is set to the
1080 original position. */
1081 if (implicitSelection) {
1083 cursor = resetCursor;
1084 SetCursor( cursor.par, cursor.pos );
1085 sel_cursor = cursor;
1090 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1092 if (textclasslist.Style(parameters->textclass,
1093 par->GetLayout()).labeltype != LABEL_MANUAL)
1096 return par->BeginningOfMainBody();
1100 /* if there is a selection, reset every environment you can find
1101 * in the selection, otherwise just the environment you are in */
1102 void LyXText::MeltFootnoteEnvironment()
1104 LyXParagraph * tmppar, * firsttmppar;
1108 /* is is only allowed, if the cursor is IN an open footnote.
1109 * Otherwise it is too dangerous */
1110 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1113 SetUndo(Undo::FINISH,
1114 cursor.par->PreviousBeforeFootnote()->previous,
1115 cursor.par->NextAfterFootnote()->next);
1117 /* ok, move to the beginning of the footnote. */
1118 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1119 cursor.par = cursor.par->Previous();
1121 SetCursor(cursor.par, cursor.par->Last());
1122 /* this is just faster than using CursorLeft(); */
1124 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1125 tmppar = firsttmppar;
1126 /* tmppar is now the paragraph right before the footnote */
1128 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1131 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1132 tmppar = tmppar->next; /* I use next instead of Next(),
1133 * because there cannot be any
1134 * footnotes in a footnote
1136 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1138 /* remember the captions and empty paragraphs */
1139 if ((textclasslist.Style(parameters->textclass,
1140 tmppar->GetLayout())
1141 .labeltype == LABEL_SENSITIVE)
1143 tmppar->SetLayout(0);
1146 // now we will paste the ex-footnote, if the layouts allow it
1147 // first restore the layout of the paragraph right behind
1150 tmppar->next->MakeSameLayout(cursor.par);
1153 if ((!tmppar->GetLayout() && !tmppar->table)
1155 && (!tmppar->Next()->Last()
1156 || tmppar->Next()->HasSameLayout(tmppar)))) {
1157 if (tmppar->Next()->Last()
1158 && tmppar->Next()->IsLineSeparator(0))
1159 tmppar->Next()->Erase(0);
1160 tmppar->PasteParagraph();
1163 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1164 * by the pasting of the beginning */
1166 /* then the beginning */
1167 /* if there is no space between the text and the footnote, so we insert
1169 * (only if the previous par and the footnotepar are not empty!) */
1170 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1171 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1172 if (firsttmppar->text.size()
1173 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1174 && first_footnote_par_is_not_empty) {
1175 firsttmppar->next->InsertChar(0, ' ');
1177 firsttmppar->PasteParagraph();
1180 /* now redo the paragaphs */
1181 RedoParagraphs(cursor, tmppar);
1183 SetCursor(cursor.par, cursor.pos);
1185 /* sometimes it can happen, that there is a counter change */
1186 Row * row = cursor.row;
1187 while (row->next && row->par != tmppar && row->next->par != tmppar)
1189 UpdateCounters(row);
1196 /* the DTP switches for paragraphs. LyX will store them in the
1197 * first physicla paragraph. When a paragraph is broken, the top settings
1198 * rest, the bottom settings are given to the new one. So I can make shure,
1199 * they do not duplicate themself and you cannnot make dirty things with
1202 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1203 bool pagebreak_top, bool pagebreak_bottom,
1204 VSpace const & space_top,
1205 VSpace const & space_bottom,
1207 string labelwidthstring,
1210 LyXCursor tmpcursor = cursor;
1212 sel_start_cursor = cursor;
1213 sel_end_cursor = cursor;
1216 // make sure that the depth behind the selection are restored, too
1217 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1218 LyXParagraph * undoendpar = endpar;
1220 if (endpar && endpar->GetDepth()) {
1221 while (endpar && endpar->GetDepth()) {
1222 endpar = endpar->LastPhysicalPar()->Next();
1223 undoendpar = endpar;
1227 endpar = endpar->Next(); // because of parindents etc.
1232 .par->ParFromPos(sel_start_cursor.pos)->previous,
1236 LyXParagraph * tmppar = sel_end_cursor.par;
1237 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1238 SetCursor(tmppar->FirstPhysicalPar(), 0);
1239 status = LyXText::NEED_MORE_REFRESH;
1240 refresh_row = cursor.row;
1241 refresh_y = cursor.y - cursor.row->baseline;
1242 if (cursor.par->footnoteflag ==
1243 sel_start_cursor.par->footnoteflag) {
1244 cursor.par->line_top = line_top;
1245 cursor.par->line_bottom = line_bottom;
1246 cursor.par->pagebreak_top = pagebreak_top;
1247 cursor.par->pagebreak_bottom = pagebreak_bottom;
1248 cursor.par->added_space_top = space_top;
1249 cursor.par->added_space_bottom = space_bottom;
1250 // does the layout allow the new alignment?
1251 if (align == LYX_ALIGN_LAYOUT)
1252 align = textclasslist
1253 .Style(parameters->textclass,
1254 cursor.par->GetLayout()).align;
1255 if (align & textclasslist
1256 .Style(parameters->textclass,
1257 cursor.par->GetLayout()).alignpossible) {
1258 if (align == textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).align)
1261 cursor.par->align = LYX_ALIGN_LAYOUT;
1263 cursor.par->align = align;
1265 cursor.par->SetLabelWidthString(labelwidthstring);
1266 cursor.par->noindent = noindent;
1269 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1272 RedoParagraphs(sel_start_cursor, endpar);
1275 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1276 sel_cursor = cursor;
1277 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1279 SetCursor(tmpcursor.par, tmpcursor.pos);
1283 void LyXText::SetParagraphExtraOpt(int type,
1285 char const * widthp,
1286 int alignment, bool hfill,
1287 bool start_minipage)
1289 LyXCursor tmpcursor = cursor;
1290 LyXParagraph * tmppar;
1292 sel_start_cursor = cursor;
1293 sel_end_cursor = cursor;
1296 // make sure that the depth behind the selection are restored, too
1297 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1298 LyXParagraph * undoendpar = endpar;
1300 if (endpar && endpar->GetDepth()) {
1301 while (endpar && endpar->GetDepth()) {
1302 endpar = endpar->LastPhysicalPar()->Next();
1303 undoendpar = endpar;
1307 endpar = endpar->Next(); // because of parindents etc.
1312 .par->ParFromPos(sel_start_cursor.pos)->previous,
1315 tmppar = sel_end_cursor.par;
1316 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1317 SetCursor(tmppar->FirstPhysicalPar(), 0);
1318 status = LyXText::NEED_MORE_REFRESH;
1319 refresh_row = cursor.row;
1320 refresh_y = cursor.y - cursor.row->baseline;
1321 if (cursor.par->footnoteflag ==
1322 sel_start_cursor.par->footnoteflag) {
1323 if (type == LyXParagraph::PEXTRA_NONE) {
1324 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1325 cursor.par->UnsetPExtraType();
1326 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1329 cursor.par->SetPExtraType(type, width, widthp);
1330 cursor.par->pextra_hfill = hfill;
1331 cursor.par->pextra_start_minipage = start_minipage;
1332 cursor.par->pextra_alignment = alignment;
1335 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1337 RedoParagraphs(sel_start_cursor, endpar);
1339 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1340 sel_cursor = cursor;
1341 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1343 SetCursor(tmpcursor.par, tmpcursor.pos);
1347 static char const * alphaCounter(int n) {
1348 static char result[2];
1353 result[0] = 'A' + n;
1361 // set the counter of a paragraph. This includes the labels
1362 void LyXText::SetCounter(LyXParagraph * par) const
1364 // this is only relevant for the beginning of paragraph
1365 par = par->FirstPhysicalPar();
1367 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1370 LyXTextClass const & textclass =
1371 textclasslist.TextClass(parameters->textclass);
1373 /* copy the prev-counters to this one, unless this is the start of a
1374 footnote or of a bibliography or the very first paragraph */
1376 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1377 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1378 && par->footnotekind == LyXParagraph::FOOTNOTE)
1379 && !(textclasslist.Style(parameters->textclass,
1380 par->Previous()->GetLayout()
1381 ).labeltype != LABEL_BIBLIO
1382 && layout.labeltype == LABEL_BIBLIO)) {
1383 for (int i = 0; i < 10; ++i) {
1384 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1386 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1387 if (!par->appendix && par->start_of_appendix){
1388 par->appendix = true;
1389 for (int i = 0; i < 10; ++i) {
1390 par->setCounter(i, 0);
1393 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1394 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1397 for (int i = 0; i < 10; ++i) {
1398 par->setCounter(i, 0);
1400 par->appendix = par->start_of_appendix;
1405 // if this is an open marginnote and this is the first
1406 // entry in the marginnote and the enclosing
1407 // environment is an enum/item then correct for the
1408 // LaTeX behaviour (ARRae)
1409 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1410 && par->footnotekind == LyXParagraph::MARGIN
1412 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1413 && (par->PreviousBeforeFootnote()
1414 && textclasslist.Style(parameters->textclass,
1415 par->PreviousBeforeFootnote()->GetLayout()
1416 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1417 // Any itemize or enumerate environment in a marginnote
1418 // that is embedded in an itemize or enumerate
1419 // paragraph is seen by LaTeX as being at a deeper
1420 // level within that enclosing itemization/enumeration
1421 // even if there is a "standard" layout at the start of
1427 /* Maybe we have to increment the enumeration depth.
1428 * BUT, enumeration in a footnote is considered in isolation from its
1429 * surrounding paragraph so don't increment if this is the
1430 * first line of the footnote
1431 * AND, bibliographies can't have their depth changed ie. they
1432 * are always of depth 0
1435 && par->Previous()->GetDepth() < par->GetDepth()
1436 && textclasslist.Style(parameters->textclass,
1437 par->Previous()->GetLayout()
1438 ).labeltype == LABEL_COUNTER_ENUMI
1439 && par->enumdepth < 3
1440 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1441 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1442 && par->footnotekind == LyXParagraph::FOOTNOTE)
1443 && layout.labeltype != LABEL_BIBLIO) {
1447 /* Maybe we have to decrement the enumeration depth, see note above */
1449 && par->Previous()->GetDepth() > par->GetDepth()
1450 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1451 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1452 && par->footnotekind == LyXParagraph::FOOTNOTE)
1453 && layout.labeltype != LABEL_BIBLIO) {
1454 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1455 par->setCounter(6 + par->enumdepth,
1456 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1457 /* reset the counters.
1458 * A depth change is like a breaking layout
1460 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1461 par->setCounter(i, 0);
1464 if (!par->labelstring.empty()) {
1465 par->labelstring.clear();
1468 if (layout.margintype == MARGIN_MANUAL) {
1469 if (par->labelwidthstring.empty()) {
1470 par->SetLabelWidthString(layout.labelstring());
1474 par->SetLabelWidthString(string());
1477 /* is it a layout that has an automatic label ? */
1478 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1480 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1481 if (i >= 0 && i<= parameters->secnumdepth) {
1482 par->incCounter(i); // increment the counter
1484 char * s = new char[50];
1486 // Is there a label? Useful for Chapter layout
1487 if (!par->appendix){
1488 if (!layout.labelstring().empty())
1489 par->labelstring = layout.labelstring();
1491 par->labelstring.clear();
1493 if (!layout.labelstring_appendix().empty())
1494 par->labelstring = layout.labelstring_appendix();
1496 par->labelstring.clear();
1499 if (!par->appendix){
1500 switch (2 * LABEL_FIRST_COUNTER -
1501 textclass.maxcounter() + i) {
1502 case LABEL_COUNTER_CHAPTER:
1504 par->getCounter(i));
1506 case LABEL_COUNTER_SECTION:
1508 par->getCounter(i - 1),
1509 par->getCounter(i));
1511 case LABEL_COUNTER_SUBSECTION:
1512 sprintf(s, "%d.%d.%d",
1513 par->getCounter(i-2),
1514 par->getCounter(i-1),
1515 par->getCounter(i));
1517 case LABEL_COUNTER_SUBSUBSECTION:
1518 sprintf(s, "%d.%d.%d.%d",
1519 par->getCounter(i-3),
1520 par->getCounter(i-2),
1521 par->getCounter(i-1),
1522 par->getCounter(i));
1524 case LABEL_COUNTER_PARAGRAPH:
1525 sprintf(s, "%d.%d.%d.%d.%d",
1526 par->getCounter(i-4),
1527 par->getCounter(i-3),
1528 par->getCounter(i-2),
1529 par->getCounter(i-1),
1530 par->getCounter(i));
1532 case LABEL_COUNTER_SUBPARAGRAPH:
1533 sprintf(s, "%d.%d.%d.%d.%d.%d",
1534 par->getCounter(i-5),
1535 par->getCounter(i-4),
1536 par->getCounter(i-3),
1537 par->getCounter(i-2),
1538 par->getCounter(i-1),
1539 par->getCounter(i));
1542 sprintf(s, "%d.", par->getCounter(i));
1546 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1547 case LABEL_COUNTER_CHAPTER:
1549 alphaCounter(par->getCounter(i)));
1551 case LABEL_COUNTER_SECTION:
1553 alphaCounter(par->getCounter(i - 1)),
1554 par->getCounter(i));
1556 case LABEL_COUNTER_SUBSECTION:
1557 sprintf(s, "%s.%d.%d",
1558 alphaCounter(par->getCounter(i-2)),
1559 par->getCounter(i-1),
1560 par->getCounter(i));
1562 case LABEL_COUNTER_SUBSUBSECTION:
1563 sprintf(s, "%s.%d.%d.%d",
1564 alphaCounter(par->getCounter(i-3)),
1565 par->getCounter(i-2),
1566 par->getCounter(i-1),
1567 par->getCounter(i));
1569 case LABEL_COUNTER_PARAGRAPH:
1570 sprintf(s, "%s.%d.%d.%d.%d",
1571 alphaCounter(par->getCounter(i-4)),
1572 par->getCounter(i-3),
1573 par->getCounter(i-2),
1574 par->getCounter(i-1),
1575 par->getCounter(i));
1577 case LABEL_COUNTER_SUBPARAGRAPH:
1578 sprintf(s, "%s.%d.%d.%d.%d.%d",
1579 alphaCounter(par->getCounter(i-5)),
1580 par->getCounter(i-4),
1581 par->getCounter(i-3),
1582 par->getCounter(i-2),
1583 par->getCounter(i-1),
1584 par->getCounter(i));
1587 sprintf(s, "%c.", par->getCounter(i));
1592 par->labelstring += s;
1595 for (i++; i < 10; ++i) {
1596 // reset the following counters
1597 par->setCounter(i, 0);
1599 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1600 for (i++; i < 10; ++i) {
1601 // reset the following counters
1602 par->setCounter(i, 0);
1604 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1605 par->incCounter(i + par->enumdepth);
1606 char * s = new char[25];
1607 int number = par->getCounter(i + par->enumdepth);
1609 static const char *roman[20] = {
1610 "i", "ii", "iii", "iv", "v",
1611 "vi", "vii", "viii", "ix", "x",
1612 "xi", "xii", "xiii", "xiv", "xv",
1613 "xvi", "xvii", "xviii", "xix", "xx"
1615 static const char hebrew[22] = {
1616 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1617 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1618 '÷', 'ø', 'ù', 'ú'
1621 switch (par->enumdepth) {
1623 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1624 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1626 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1629 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1630 sprintf(s, "%s.", roman[(number-1) % 20]);
1632 sprintf(s, ".%s", roman[(number-1) % 20]);
1635 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1636 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1638 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1641 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1642 sprintf(s, "%d.", number);
1644 sprintf(s, ".%d", number);
1647 par->labelstring = s;
1650 for (i += par->enumdepth + 1; i < 10; ++i)
1651 par->setCounter(i, 0); /* reset the following counters */
1654 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1655 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1657 int number = par->getCounter(i);
1659 par->bibkey = new InsetBibKey();
1660 par->bibkey->setCounter(number);
1661 par->labelstring = layout.labelstring();
1663 // In biblio should't be following counters but...
1665 string s = layout.labelstring();
1667 // the caption hack:
1669 if (layout.labeltype == LABEL_SENSITIVE) {
1670 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1671 && (par->footnotekind == LyXParagraph::FIG
1672 || par->footnotekind == LyXParagraph::WIDE_FIG))
1673 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1677 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1678 && (par->footnotekind == LyXParagraph::TAB
1679 || par->footnotekind == LyXParagraph::WIDE_TAB))
1680 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1684 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1685 && par->footnotekind == LyXParagraph::ALGORITHM)
1686 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1689 s = ":Ãúéøåâìà ";
1691 /* par->SetLayout(0);
1692 s = layout->labelstring; */
1693 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1696 s = " :úåòîùî øñç";
1700 par->labelstring = s;
1702 /* reset the enumeration counter. They are always resetted
1703 * when there is any other layout between */
1704 for (int i = 6 + par->enumdepth; i < 10; ++i)
1705 par->setCounter(i, 0);
1710 /* Updates all counters BEHIND the row. Changed paragraphs
1711 * with a dynamic left margin will be rebroken. */
1712 void LyXText::UpdateCounters(Row * row) const
1721 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1722 par = row->par->LastPhysicalPar()->Next();
1724 par = row->par->next;
1729 while (row->par != par)
1734 /* now check for the headline layouts. remember that they
1735 * have a dynamic left margin */
1737 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1738 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1741 /* Rebreak the paragraph */
1742 RemoveParagraph(row);
1743 AppendParagraph(row);
1745 /* think about the damned open footnotes! */
1746 while (par->Next() &&
1747 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1748 || par->Next()->IsDummy())){
1750 if (par->IsDummy()) {
1751 while (row->par != par)
1753 RemoveParagraph(row);
1754 AppendParagraph(row);
1759 par = par->LastPhysicalPar()->Next();
1765 /* insets an inset. */
1766 void LyXText::InsertInset(Inset *inset)
1768 SetUndo(Undo::INSERT,
1769 cursor.par->ParFromPos(cursor.pos)->previous,
1770 cursor.par->ParFromPos(cursor.pos)->next);
1771 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1772 cursor.par->InsertInset(cursor.pos, inset);
1773 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1774 * The character will not be inserted a
1779 // this is for the simple cut and paste mechanism
1780 static LyXParagraph * simple_cut_buffer = 0;
1781 static char simple_cut_buffer_textclass = 0;
1783 void DeleteSimpleCutBuffer()
1785 if (!simple_cut_buffer)
1787 LyXParagraph * tmppar;
1789 while (simple_cut_buffer) {
1790 tmppar = simple_cut_buffer;
1791 simple_cut_buffer = simple_cut_buffer->next;
1794 simple_cut_buffer = 0;
1798 void LyXText::copyEnvironmentType()
1800 copylayouttype = cursor.par->GetLayout();
1804 void LyXText::pasteEnvironmentType()
1806 SetLayout(copylayouttype);
1810 void LyXText::CutSelection(bool doclear)
1812 // This doesn't make sense, if there is no selection
1816 // OK, we have a selection. This is always between sel_start_cursor
1817 // and sel_end cursor
1818 LyXParagraph * tmppar;
1820 // Check whether there are half footnotes in the selection
1821 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1822 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1823 tmppar = sel_start_cursor.par;
1824 while (tmppar != sel_end_cursor.par){
1825 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1826 WriteAlert(_("Impossible operation"),
1827 _("Don't know what to do with half floats."),
1831 tmppar = tmppar->Next();
1835 /* table stuff -- begin */
1836 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1837 if ( sel_start_cursor.par != sel_end_cursor.par) {
1838 WriteAlert(_("Impossible operation"),
1839 _("Don't know what to do with half tables."),
1843 sel_start_cursor.par->table->Reinit();
1845 /* table stuff -- end */
1847 // make sure that the depth behind the selection are restored, too
1848 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1849 LyXParagraph * undoendpar = endpar;
1851 if (endpar && endpar->GetDepth()) {
1852 while (endpar && endpar->GetDepth()) {
1853 endpar = endpar->LastPhysicalPar()->Next();
1854 undoendpar = endpar;
1856 } else if (endpar) {
1857 endpar = endpar->Next(); // because of parindents etc.
1860 SetUndo(Undo::DELETE,
1862 .par->ParFromPos(sel_start_cursor.pos)->previous,
1865 // clear the simple_cut_buffer
1866 DeleteSimpleCutBuffer();
1868 // set the textclass
1869 simple_cut_buffer_textclass = parameters->textclass;
1871 #ifdef WITH_WARNINGS
1872 #warning Asger: Make cut more intelligent here.
1875 White paper for "intelligent" cutting:
1877 Example: "This is our text."
1878 Using " our " as selection, cutting will give "This istext.".
1879 Using "our" as selection, cutting will give "This is text.".
1880 Using " our" as selection, cutting will give "This is text.".
1881 Using "our " as selection, cutting will give "This is text.".
1883 All those four selections will (however) paste identically:
1884 Pasting with the cursor right after the "is" will give the
1885 original text with all four selections.
1887 The rationale is to be intelligent such that words are copied,
1888 cut and pasted in a functional manner.
1890 This is not implemented yet. (Asger)
1892 The changes below sees to do a lot of what you want. However
1893 I have not verified that all cases work as they should:
1895 - cut in multiple row
1897 - cut across footnotes and paragraph
1898 My simplistic tests show that the idea are basically sound but
1899 there are some items to fix up...we only need to find them
1902 As do redo Asger's example above (with | beeing the cursor in the
1903 result after cutting.):
1905 Example: "This is our text."
1906 Using " our " as selection, cutting will give "This is|text.".
1907 Using "our" as selection, cutting will give "This is | text.".
1908 Using " our" as selection, cutting will give "This is| text.".
1909 Using "our " as selection, cutting will give "This is |text.".
1914 #ifndef FIX_DOUBLE_SPACE
1915 bool space_wrapped =
1916 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1917 if (sel_end_cursor.pos > 0
1918 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1919 // please break before a space at the end
1920 sel_end_cursor.pos--;
1921 space_wrapped = true;
1923 // cut behind a space if there is one
1924 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1925 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1926 && (sel_start_cursor.par != sel_end_cursor.par
1927 || sel_start_cursor.pos < sel_end_cursor.pos))
1928 sel_start_cursor.pos++;
1930 // there are two cases: cut only within one paragraph or
1931 // more than one paragraph
1933 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1934 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1935 // only within one paragraph
1936 simple_cut_buffer = new LyXParagraph;
1937 LyXParagraph::size_type i =
1938 sel_start_cursor.pos;
1939 for (; i < sel_end_cursor.pos; ++i) {
1940 /* table stuff -- begin */
1941 if (sel_start_cursor.par->table
1942 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1943 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1944 sel_start_cursor.pos++;
1946 /* table stuff -- end */
1947 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1948 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1950 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1952 #ifndef FIX_DOUBLE_SPACE
1953 // check for double spaces
1954 if (sel_start_cursor.pos &&
1955 sel_start_cursor.par->Last() > sel_start_cursor.pos
1956 && sel_start_cursor.par
1957 ->IsLineSeparator(sel_start_cursor.pos - 1)
1958 && sel_start_cursor.par
1959 ->IsLineSeparator(sel_start_cursor.pos)) {
1960 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1963 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1966 endpar = sel_end_cursor.par->Next();
1968 // cut more than one paragraph
1971 ->BreakParagraphConservative(sel_end_cursor.pos);
1972 #ifndef FIX_DOUBLE_SPACE
1973 // insert a space at the end if there was one
1976 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1978 sel_end_cursor.par = sel_end_cursor.par->Next();
1979 sel_end_cursor.pos = 0;
1981 cursor = sel_end_cursor;
1983 #ifndef FIX_DOUBLE_SPACE
1984 // please break behind a space, if there is one.
1985 // The space should be copied too
1986 if (sel_start_cursor.par
1987 ->IsLineSeparator(sel_start_cursor.pos))
1988 sel_start_cursor.pos++;
1990 sel_start_cursor.par
1991 ->BreakParagraphConservative(sel_start_cursor.pos);
1992 #ifndef FIX_DOUBLE_SPACE
1993 if (!sel_start_cursor.pos
1994 || sel_start_cursor.par
1995 ->IsLineSeparator(sel_start_cursor.pos - 1)
1996 || sel_start_cursor.par
1997 ->IsNewline(sel_start_cursor.pos - 1)) {
1998 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2001 // store the endparagraph for redoing later
2002 endpar = sel_end_cursor.par->Next(); /* needed because
2007 // store the selection
2008 simple_cut_buffer = sel_start_cursor.par
2009 ->ParFromPos(sel_start_cursor.pos)->next;
2010 simple_cut_buffer->previous = 0;
2011 sel_end_cursor.par->previous->next = 0;
2013 // cut the selection
2014 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2015 = sel_end_cursor.par;
2017 sel_end_cursor.par->previous
2018 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2020 // care about footnotes
2021 if (simple_cut_buffer->footnoteflag) {
2022 LyXParagraph * tmppar = simple_cut_buffer;
2024 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2025 tmppar = tmppar->next;
2029 // the cut selection should begin with standard layout
2030 simple_cut_buffer->Clear();
2032 // paste the paragraphs again, if possible
2034 sel_start_cursor.par->Next()->ClearParagraph();
2035 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2037 !sel_start_cursor.par->Next()->Last())
2038 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2040 #ifndef FIX_DOUBLE_SPACE
2041 // maybe a forgotten blank
2042 if (sel_start_cursor.pos
2043 && sel_start_cursor.par
2044 ->IsLineSeparator(sel_start_cursor.pos)
2045 && sel_start_cursor.par
2046 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2047 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2052 // sometimes necessary
2054 sel_start_cursor.par->ClearParagraph();
2056 RedoParagraphs(sel_start_cursor, endpar);
2059 cursor = sel_start_cursor;
2060 SetCursor(cursor.par, cursor.pos);
2061 sel_cursor = cursor;
2062 UpdateCounters(cursor.row);
2066 void LyXText::CopySelection()
2068 // this doesnt make sense, if there is no selection
2072 // ok we have a selection. This is always between sel_start_cursor
2073 // and sel_end cursor
2074 LyXParagraph * tmppar;
2076 /* check wether there are half footnotes in the selection */
2077 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2078 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2079 tmppar = sel_start_cursor.par;
2080 while (tmppar != sel_end_cursor.par) {
2081 if (tmppar->footnoteflag !=
2082 sel_end_cursor.par->footnoteflag) {
2083 WriteAlert(_("Impossible operation"),
2084 _("Don't know what to do"
2085 " with half floats."),
2089 tmppar = tmppar->Next();
2093 /* table stuff -- begin */
2094 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2095 if ( sel_start_cursor.par != sel_end_cursor.par){
2096 WriteAlert(_("Impossible operation"),
2097 _("Don't know what to do with half tables."),
2102 /* table stuff -- end */
2104 // delete the simple_cut_buffer
2105 DeleteSimpleCutBuffer();
2107 // set the textclass
2108 simple_cut_buffer_textclass = parameters->textclass;
2110 #ifdef FIX_DOUBLE_SPACE
2111 // copy behind a space if there is one
2112 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2113 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2114 && (sel_start_cursor.par != sel_end_cursor.par
2115 || sel_start_cursor.pos < sel_end_cursor.pos))
2116 sel_start_cursor.pos++;
2118 // there are two cases: copy only within one paragraph
2119 // or more than one paragraph
2120 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2121 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2122 // only within one paragraph
2123 simple_cut_buffer = new LyXParagraph;
2124 LyXParagraph::size_type i = 0;
2125 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2126 sel_start_cursor.par->CopyIntoMinibuffer(i);
2127 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2130 // copy more than one paragraph
2131 // clone the paragraphs within the selection
2133 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2134 simple_cut_buffer = tmppar->Clone();
2135 LyXParagraph *tmppar2 = simple_cut_buffer;
2137 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2139 tmppar = tmppar->next;
2140 tmppar2->next = tmppar->Clone();
2141 tmppar2->next->previous = tmppar2;
2142 tmppar2 = tmppar2->next;
2146 // care about footnotes
2147 if (simple_cut_buffer->footnoteflag) {
2148 tmppar = simple_cut_buffer;
2150 tmppar->footnoteflag =
2151 LyXParagraph::NO_FOOTNOTE;
2152 tmppar = tmppar->next;
2156 // the simple_cut_buffer paragraph is too big
2157 LyXParagraph::size_type tmpi2 =
2158 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2159 for (; tmpi2; --tmpi2)
2160 simple_cut_buffer->Erase(0);
2162 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2164 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2165 while (tmppar2->size() > tmpi2) {
2166 tmppar2->Erase(tmppar2->text.size() - 1);
2172 void LyXText::PasteSelection()
2174 // this does not make sense, if there is nothing to paste
2175 if (!simple_cut_buffer)
2178 LyXParagraph * tmppar;
2179 LyXParagraph * endpar;
2181 LyXCursor tmpcursor;
2183 // be carefull with footnotes in footnotes
2184 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2186 // check whether the cut_buffer includes a footnote
2187 tmppar = simple_cut_buffer;
2189 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2190 tmppar = tmppar->next;
2193 WriteAlert(_("Impossible operation"),
2194 _("Can't paste float into float!"),
2200 /* table stuff -- begin */
2201 if (cursor.par->table) {
2202 if (simple_cut_buffer->next) {
2203 WriteAlert(_("Impossible operation"),
2204 _("Table cell cannot include more than one paragraph!"),
2209 /* table stuff -- end */
2211 SetUndo(Undo::INSERT,
2212 cursor.par->ParFromPos(cursor.pos)->previous,
2213 cursor.par->ParFromPos(cursor.pos)->next);
2217 // There are two cases: cutbuffer only one paragraph or many
2218 if (!simple_cut_buffer->next) {
2219 // only within a paragraph
2221 #ifndef FIX_DOUBLE_SPACE
2222 // please break behind a space, if there is one
2223 while (tmpcursor.par->Last() > tmpcursor.pos
2224 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2227 tmppar = simple_cut_buffer->Clone();
2228 /* table stuff -- begin */
2229 bool table_too_small = false;
2230 if (tmpcursor.par->table) {
2231 while (simple_cut_buffer->text.size()
2232 && !table_too_small) {
2233 if (simple_cut_buffer->IsNewline(0)){
2234 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2236 simple_cut_buffer->Erase(0);
2237 if (tmpcursor.pos < tmpcursor.par->Last())
2240 table_too_small = true;
2242 #ifdef FIX_DOUBLE_SPACE
2243 // This is an attempt to fix the
2244 // "never insert a space at the
2245 // beginning of a paragraph" problem.
2246 if (tmpcursor.pos == 0
2247 && simple_cut_buffer->IsLineSeparator(0)) {
2248 simple_cut_buffer->Erase(0);
2250 simple_cut_buffer->CutIntoMinibuffer(0);
2251 simple_cut_buffer->Erase(0);
2252 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2256 simple_cut_buffer->CutIntoMinibuffer(0);
2257 simple_cut_buffer->Erase(0);
2258 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2264 /* table stuff -- end */
2265 // Some provisions should be done here for checking
2266 // if we are inserting at the beginning of a
2267 // paragraph. If there are a space at the beginning
2268 // of the text to insert and we are inserting at
2269 // the beginning of the paragraph the space should
2271 while (simple_cut_buffer->text.size()) {
2272 #ifdef FIX_DOUBLE_SPACE
2273 // This is an attempt to fix the
2274 // "never insert a space at the
2275 // beginning of a paragraph" problem.
2276 if (tmpcursor.pos == 0
2277 && simple_cut_buffer->IsLineSeparator(0)) {
2278 simple_cut_buffer->Erase(0);
2280 simple_cut_buffer->CutIntoMinibuffer(0);
2281 simple_cut_buffer->Erase(0);
2282 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2286 simple_cut_buffer->CutIntoMinibuffer(0);
2287 simple_cut_buffer->Erase(0);
2288 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2293 delete simple_cut_buffer;
2294 simple_cut_buffer = tmppar;
2295 endpar = tmpcursor.par->Next();
2299 // make a copy of the simple cut_buffer
2300 tmppar = simple_cut_buffer;
2301 LyXParagraph * simple_cut_clone = tmppar->Clone();
2302 LyXParagraph * tmppar2 = simple_cut_clone;
2303 if (cursor.par->footnoteflag){
2304 tmppar->footnoteflag = cursor.par->footnoteflag;
2305 tmppar->footnotekind = cursor.par->footnotekind;
2307 while (tmppar->next) {
2308 tmppar = tmppar->next;
2309 tmppar2->next = tmppar->Clone();
2310 tmppar2->next->previous = tmppar2;
2311 tmppar2 = tmppar2->next;
2312 if (cursor.par->footnoteflag){
2313 tmppar->footnoteflag = cursor.par->footnoteflag;
2314 tmppar->footnotekind = cursor.par->footnotekind;
2318 // make sure there is no class difference
2319 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2320 parameters->textclass,
2323 // make the simple_cut_buffer exactly the same layout than
2324 // the cursor paragraph
2325 simple_cut_buffer->MakeSameLayout(cursor.par);
2327 // find the end of the buffer
2328 LyXParagraph * lastbuffer = simple_cut_buffer;
2329 while (lastbuffer->Next())
2330 lastbuffer = lastbuffer->Next();
2332 #ifndef FIX_DOUBLE_SPACE
2333 // Please break behind a space, if there is one. The space
2334 // should be copied too.
2335 if (cursor.par->Last() > cursor.pos
2336 && cursor.par->IsLineSeparator(cursor.pos))
2339 bool paste_the_end = false;
2341 // open the paragraph for inserting the simple_cut_buffer
2343 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2344 cursor.par->BreakParagraphConservative(cursor.pos);
2345 paste_the_end = true;
2348 #ifndef FIX_DOUBLE_SPACE
2349 // be careful with double spaces
2350 if ((!cursor.par->Last()
2351 || cursor.par->IsLineSeparator(cursor.pos - 1)
2352 || cursor.par->IsNewline(cursor.pos - 1))
2353 && simple_cut_buffer->text.size()
2354 && simple_cut_buffer->IsLineSeparator(0))
2355 simple_cut_buffer->Erase(0);
2357 // set the end for redoing later
2358 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2361 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2362 cursor.par->ParFromPos(cursor.pos)->next;
2363 cursor.par->ParFromPos(cursor.pos)->next->previous =
2364 lastbuffer->ParFromPos(lastbuffer->Last());
2366 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2367 simple_cut_buffer->previous =
2368 cursor.par->ParFromPos(cursor.pos);
2370 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2371 lastbuffer = cursor.par;
2373 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2375 // store the new cursor position
2376 tmpcursor.par = lastbuffer;
2377 tmpcursor.pos = lastbuffer->Last();
2379 // maybe some pasting
2380 if (lastbuffer->Next() && paste_the_end) {
2381 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2382 #ifndef FIX_DOUBLE_SPACE
2383 // be careful with double spaces
2384 if ((!lastbuffer->Last()
2385 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2386 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2387 && lastbuffer->Next()->Last()
2388 && lastbuffer->Next()->IsLineSeparator(0))
2389 lastbuffer->Next()->Erase(0);
2391 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2393 } else if (!lastbuffer->Next()->Last()) {
2394 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2395 #ifndef FIX_DOUBLE_SPACE
2396 // be careful witth double spaces
2397 if ((!lastbuffer->Last()
2398 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2399 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2400 && lastbuffer->Next()->Last()
2401 && lastbuffer->Next()->IsLineSeparator(0))
2402 lastbuffer->Next()->Erase(0);
2404 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2406 } else if (!lastbuffer->Last()) {
2407 lastbuffer->MakeSameLayout(lastbuffer->next);
2408 #ifndef FIX_DOUBLE_SPACE
2409 // be careful witth double spaces
2410 if ((!lastbuffer->Last()
2411 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2412 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2413 && lastbuffer->Next()->Last()
2414 && lastbuffer->Next()->IsLineSeparator(0))
2415 lastbuffer->Next()->Erase(0);
2417 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2420 lastbuffer->Next()->ClearParagraph();
2423 // restore the simple cut buffer
2424 simple_cut_buffer = simple_cut_clone;
2427 RedoParagraphs(cursor, endpar);
2429 SetCursor(cursor.par, cursor.pos);
2432 sel_cursor = cursor;
2433 SetCursor(tmpcursor.par, tmpcursor.pos);
2435 UpdateCounters(cursor.row);
2439 // returns a pointer to the very first LyXParagraph
2440 LyXParagraph * LyXText::FirstParagraph() const
2442 return params->paragraph;
2446 // returns true if the specified string is at the specified position
2447 bool LyXText::IsStringInText(LyXParagraph * par,
2448 LyXParagraph::size_type pos,
2449 char const * str) const
2453 while (pos + i < par->Last() && str[i] &&
2454 str[i] == par->GetChar(pos + i)) {
2464 // sets the selection over the number of characters of string, no check!!
2465 void LyXText::SetSelectionOverString(char const * string)
2467 sel_cursor = cursor;
2468 for (int i = 0; string[i]; ++i)
2474 // simple replacing. The font of the first selected character is used
2475 void LyXText::ReplaceSelectionWithString(char const * str)
2480 if (!selection) { // create a dummy selection
2481 sel_end_cursor = cursor;
2482 sel_start_cursor = cursor;
2485 // Get font setting before we cut
2486 LyXParagraph::size_type pos = sel_end_cursor.pos;
2487 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2489 // Insert the new string
2490 for (int i = 0; str[i]; ++i) {
2491 sel_end_cursor.par->InsertChar(pos, str[i]);
2492 sel_end_cursor.par->SetFont(pos, font);
2496 // Cut the selection
2503 // if the string can be found: return true and set the cursor to
2505 bool LyXText::SearchForward(char const * str) const
2507 LyXParagraph * par = cursor.par;
2508 LyXParagraph::size_type pos = cursor.pos;
2509 while (par && !IsStringInText(par, pos, str)) {
2510 if (pos < par->Last() - 1)
2518 SetCursor(par, pos);
2526 bool LyXText::SearchBackward(char const * string) const
2528 LyXParagraph * par = cursor.par;
2529 int pos = cursor.pos;
2535 // We skip empty paragraphs (Asger)
2537 par = par->Previous();
2539 pos = par->Last() - 1;
2540 } while (par && pos < 0);
2542 } while (par && !IsStringInText(par, pos, string));
2545 SetCursor(par, pos);
2552 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2554 char * str = new char[text.size() + 1];
2555 copy(text.begin(), text.end(), str);
2556 str[text.size()] = '\0';
2562 // needed to insert the selection
2563 void LyXText::InsertStringA(char const * s)
2566 LyXParagraph * par = cursor.par;
2567 LyXParagraph::size_type pos = cursor.pos;
2568 LyXParagraph::size_type a = 0;
2570 LyXParagraph * endpar = cursor.par->Next();
2575 textclasslist.Style(parameters->textclass,
2576 cursor.par->GetLayout()).isEnvironment();
2577 // only to be sure, should not be neccessary
2580 // insert the string, don't insert doublespace
2581 string::size_type i = 0;
2582 while (i < str.length()) {
2583 if (str[i] != '\n') {
2585 && i + 1 < str.length() && str[i + 1] != ' '
2586 && pos && par->GetChar(pos - 1)!= ' ') {
2587 par->InsertChar(pos,' ');
2589 } else if (par->table) {
2590 if (str[i] == '\t') {
2591 while((pos < par->size()) &&
2592 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2594 if (pos < par->size())
2596 else // no more fields to fill skip the rest
2598 } else if ((str[i] != 13) &&
2599 ((str[i] & 127) >= ' ')) {
2600 par->InsertChar(pos, str[i]);
2603 } else if (str[i] == ' ') {
2604 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2606 } else if (str[i] == '\t') {
2607 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2608 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2611 } else if (str[i]!= 13 &&
2612 // Ignore unprintables
2613 (str[i] & 127) >= ' ') {
2614 par->InsertChar(pos, str[i]);
2619 if (i + 1 >= str.length()) {
2623 while((pos < par->size()) &&
2624 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2627 cell = NumberOfCell(par, pos);
2628 while((pos < par->size()) &&
2629 !(par->table->IsFirstCell(cell))) {
2630 while((pos < par->size()) &&
2631 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2634 cell = NumberOfCell(par, pos);
2636 if (pos >= par->size())
2637 // no more fields to fill skip the rest
2640 if (!par->text.size()) {
2641 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2644 par->BreakParagraph(pos, flag);
2652 RedoParagraphs(cursor, endpar);
2653 SetCursor(cursor.par, cursor.pos);
2654 sel_cursor = cursor;
2655 SetCursor(par, pos);
2660 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2662 char * str = new char[text.size() + 1];
2663 copy(text.begin(), text.end(), str);
2664 str[text.size()] = '\0';
2670 /* turns double-CR to single CR, others where converted into one blank and 13s
2671 * that are ignored .Double spaces are also converted into one. Spaces at
2672 * the beginning of a paragraph are forbidden. tabs are converted into one
2673 * space. then InsertStringA is called */
2674 void LyXText::InsertStringB(char const * s)
2677 LyXParagraph * par = cursor.par;
2678 string::size_type i = 1;
2679 while (i < str.length()) {
2680 if (str[i] == '\t' && !par->table)
2682 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2684 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2685 if (str[i + 1] != '\n') {
2686 if (str[i - 1] != ' ')
2691 while (i + 1 < str.length()
2692 && (str[i + 1] == ' '
2693 || str[i + 1] == '\t'
2694 || str[i + 1] == '\n'
2695 || str[i + 1] == 13)) {
2702 InsertStringA(str.c_str());
2706 bool LyXText::GotoNextError() const
2708 LyXCursor res = cursor;
2710 if (res.pos < res.par->Last() - 1) {
2714 res.par = res.par->Next();
2719 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2720 && res.par->GetInset(res.pos)->AutoDelete()));
2723 SetCursor(res.par, res.pos);
2730 bool LyXText::GotoNextNote() const
2732 LyXCursor res = cursor;
2734 if (res.pos < res.par->Last() - 1) {
2737 res.par = res.par->Next();
2742 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2743 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2746 SetCursor(res.par, res.pos);
2753 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2757 if (!par || class1 == class2)
2759 par = par->FirstPhysicalPar();
2761 string name = textclasslist.NameOfLayout(class1, par->layout);
2763 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2764 textclasslist.NumberOfLayout(class2, name);
2767 } else { // layout not found
2768 // use default layout "Standard" (0)
2773 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2775 string s = "Layout had to be changed from\n"
2776 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2777 + "\nbecause of class conversion from\n"
2778 + textclasslist.NameOfClass(class1) + " to "
2779 + textclasslist.NameOfClass(class2);
2780 InsetError * new_inset = new InsetError(s);
2781 par->InsertChar(0, LyXParagraph::META_INSET);
2782 par->InsertInset(0, new_inset);
2791 void LyXText::CheckParagraph(LyXParagraph * par,
2792 LyXParagraph::size_type pos)
2795 LyXCursor tmpcursor;
2797 /* table stuff -- begin*/
2800 CheckParagraphInTable(par, pos);
2803 /* table stuff -- end*/
2806 LyXParagraph::size_type z;
2807 Row * row = GetRow(par, pos, y);
2809 // is there a break one row above
2810 if (row->previous && row->previous->par == row->par) {
2811 z = NextBreakPoint(row->previous, paperwidth);
2812 if ( z >= row->pos) {
2813 // set the dimensions of the row above
2814 y -= row->previous->height;
2816 refresh_row = row->previous;
2817 status = LyXText::NEED_MORE_REFRESH;
2819 BreakAgain(row->previous);
2821 // set the cursor again. Otherwise
2822 // dangling pointers are possible
2823 SetCursor(cursor.par, cursor.pos);
2824 sel_cursor = cursor;
2829 int tmpheight = row->height;
2830 LyXParagraph::size_type tmplast = RowLast(row);
2835 if (row->height == tmpheight && RowLast(row) == tmplast)
2836 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2838 status = LyXText::NEED_MORE_REFRESH;
2840 // check the special right address boxes
2841 if (textclasslist.Style(parameters->textclass,
2842 par->GetLayout()).margintype
2843 == MARGIN_RIGHT_ADDRESS_BOX) {
2844 tmpcursor.par = par;
2845 tmpcursor.row = row;
2848 tmpcursor.x_fix = 0;
2849 tmpcursor.pos = pos;
2850 RedoDrawingOfParagraph(tmpcursor);
2855 // set the cursor again. Otherwise dangling pointers are possible
2856 // also set the selection
2860 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2861 sel_cursor = cursor;
2862 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2863 sel_start_cursor = cursor;
2864 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2865 sel_end_cursor = cursor;
2866 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2867 last_sel_cursor = cursor;
2870 SetCursorIntern(cursor.par, cursor.pos);
2874 // returns 0 if inset wasn't found
2875 int LyXText::UpdateInset(Inset * inset)
2877 // first check the current paragraph
2878 int pos = cursor.par->GetPositionOfInset(inset);
2880 CheckParagraph(cursor.par, pos);
2884 // check every paragraph
2886 LyXParagraph * par = FirstParagraph();
2888 // make sure the paragraph is open
2889 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2890 pos = par->GetPositionOfInset(inset);
2892 CheckParagraph(par, pos);
2903 void LyXText::SetCursor(LyXParagraph * par,
2904 LyXParagraph::size_type pos, bool setfont) const
2906 LyXCursor old_cursor = cursor;
2907 SetCursorIntern(par, pos, setfont);
2908 DeleteEmptyParagraphMechanism(old_cursor);
2912 void LyXText::SetCursorIntern(LyXParagraph * par,
2913 LyXParagraph::size_type pos, bool setfont) const
2917 LyXParagraph * tmppar;
2918 LyXParagraph::size_type vpos,cursor_vpos;
2920 // correct the cursor position if impossible
2921 if (pos > par->Last()){
2922 tmppar = par->ParFromPos(pos);
2923 pos = par->PositionInParFromPos(pos);
2926 if (par->IsDummy() && par->previous &&
2927 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2928 while (par->previous &&
2929 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2930 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2931 par = par->previous ;
2932 if (par->IsDummy() &&
2933 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2934 pos += par->text.size() + 1;
2936 if (par->previous) {
2937 par = par->previous;
2939 pos += par->text.size() + 1;
2947 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2948 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2949 && !cursor.par->IsSeparator(cursor.pos))
2950 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2952 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2953 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2955 current_font = cursor.par->GetFontSettings(cursor.pos);
2956 real_current_font = GetFont(cursor.par, cursor.pos);
2957 if (pos == 0 && par->size() == 0
2958 && current_view->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2959 current_font.setDirection(LyXFont::RTL_DIR);
2960 real_current_font.setDirection(LyXFont::RTL_DIR);
2964 /* get the cursor y position in text */
2965 row = GetRow(par, pos, y);
2966 /* y is now the beginning of the cursor row */
2968 /* y is now the cursor baseline */
2971 /* now get the cursors x position */
2973 float fill_separator, fill_hfill, fill_label_hfill;
2974 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2976 LyXParagraph::size_type last = RowLast(row);
2977 if (row->pos > last)
2979 else if (pos <= last ) {
2980 LyXDirection letter_direction =
2981 row->par->getLetterDirection(pos);
2982 LyXDirection font_direction =
2983 real_current_font.getFontDirection();
2984 if (letter_direction == font_direction || pos == 0)
2985 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
2986 ? log2vis(pos) : log2vis(pos)+1;
2988 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
2989 ? log2vis(pos-1)+1 : log2vis(pos-1);
2991 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
2992 ? log2vis(last)+1 : log2vis(last);
2994 /* table stuff -- begin*/
2995 if (row->par->table) {
2996 int cell = NumberOfCell(row->par, row->pos);
2998 x += row->par->table->GetBeginningOfTextInCell(cell);
2999 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3000 pos = vis2log(vpos);
3001 if (row->par->IsNewline(pos)) {
3002 x = x_old + row->par->table->WidthOfColumn(cell);
3005 x += row->par->table->GetBeginningOfTextInCell(cell);
3007 x += SingleWidth(row->par, pos);
3011 /* table stuff -- end*/
3012 LyXParagraph::size_type main_body =
3013 BeginningOfMainBody(row->par);
3014 if (main_body > 0 &&
3015 (main_body-1 > last ||
3016 !row->par->IsLineSeparator(main_body-1)))
3019 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3020 pos = vis2log(vpos);
3021 if (main_body > 0 && pos == main_body-1) {
3022 x += fill_label_hfill +
3023 GetFont(row->par, -2).stringWidth(
3024 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3025 if (row->par->IsLineSeparator(main_body-1))
3026 x -= SingleWidth(row->par, main_body-1);
3029 x += SingleWidth(row->par, pos);
3030 if (HfillExpansion(row, pos)) {
3031 if (pos >= main_body)
3034 x += fill_label_hfill;
3036 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3044 cursor.x_fix = cursor.x;
3049 void LyXText::SetCursorFromCoordinates(int x, long y) const
3051 LyXCursor old_cursor = cursor;
3053 /* get the row first */
3055 Row * row = GetRowNearY(y);
3057 cursor.par = row->par;
3059 int column = GetColumnNearX(row, x);
3060 cursor.pos = row->pos + column;
3062 cursor.y = y + row->baseline;
3067 (cursor.pos == cursor.par->Last()
3068 || cursor.par->IsSeparator(cursor.pos)
3069 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3070 && !cursor.par->IsSeparator(cursor.pos))
3071 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3073 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3074 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3076 current_font = cursor.par->GetFontSettings(cursor.pos);
3077 real_current_font = GetFont(cursor.par, cursor.pos);
3079 DeleteEmptyParagraphMechanism(old_cursor);
3083 void LyXText::CursorLeft() const
3086 if (cursor.par->table) {
3087 int cell = NumberOfCell(cursor.par, cursor.pos);
3088 if (cursor.par->table->IsContRow(cell) &&
3089 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3096 void LyXText::CursorLeftIntern() const
3098 if (cursor.pos > 0) {
3099 SetCursor(cursor.par, cursor.pos - 1);
3101 else if (cursor.par->Previous()) {
3102 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3107 void LyXText::CursorRight() const
3109 CursorRightIntern();
3110 if (cursor.par->table) {
3111 int cell = NumberOfCell(cursor.par, cursor.pos);
3112 if (cursor.par->table->IsContRow(cell) &&
3113 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3120 void LyXText::CursorRightIntern() const
3122 if (cursor.pos < cursor.par->Last()) {
3123 SetCursor(cursor.par, cursor.pos + 1);
3125 else if (cursor.par->Next()) {
3126 SetCursor(cursor.par->Next(), 0);
3131 void LyXText::CursorUp() const
3133 SetCursorFromCoordinates(cursor.x_fix,
3134 cursor.y - cursor.row->baseline - 1);
3135 if (cursor.par->table) {
3136 int cell = NumberOfCell(cursor.par, cursor.pos);
3137 if (cursor.par->table->IsContRow(cell) &&
3138 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3145 void LyXText::CursorDown() const
3147 if (cursor.par->table &&
3148 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3151 SetCursorFromCoordinates(cursor.x_fix,
3152 cursor.y - cursor.row->baseline
3153 + cursor.row->height + 1);
3154 if (cursor.par->table) {
3155 int cell = NumberOfCell(cursor.par, cursor.pos);
3156 int cell_above = cursor.par->table->GetCellAbove(cell);
3157 while(cursor.par->table &&
3158 cursor.par->table->IsContRow(cell) &&
3159 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3160 SetCursorFromCoordinates(cursor.x_fix,
3161 cursor.y - cursor.row->baseline
3162 + cursor.row->height + 1);
3163 if (cursor.par->table) {
3164 cell = NumberOfCell(cursor.par, cursor.pos);
3165 cell_above = cursor.par->table->GetCellAbove(cell);
3172 void LyXText::CursorUpParagraph() const
3174 if (cursor.pos > 0) {
3175 SetCursor(cursor.par, 0);
3177 else if (cursor.par->Previous()) {
3178 SetCursor(cursor.par->Previous(), 0);
3183 void LyXText::CursorDownParagraph() const
3185 if (cursor.par->Next()) {
3186 SetCursor(cursor.par->Next(), 0);
3188 SetCursor(cursor.par, cursor.par->Last());
3194 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3196 bool deleted = false;
3198 // this is the delete-empty-paragraph-mechanism.
3199 if (selection) return;
3201 #ifdef FIX_DOUBLE_SPACE
3202 /* Ok I'll put some comments here about what is missing.
3203 I have fixed BackSpace (and thus Delete) to not delete
3204 double-spaces automagically. I have also changed Cut,
3205 Copy and Paste to hopefully do some sensible things.
3206 There are still some small problems that can lead to
3207 double spaces stored in the document file or space at
3208 the beginning of paragraphs. This happens if you have
3209 the cursor betwenn to spaces and then save. Or if you
3210 cut and paste and the selection have a space at the
3211 beginning and then save right after the paste. I am
3212 sure none of these are very hard to fix, but I will
3213 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3214 that I can get some feedback. (Lgb)
3217 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3218 // delete the LineSeparator.
3221 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3222 // delete the LineSeparator.
3225 // If the pos around the old_cursor were spaces, delete one of them.
3226 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3227 && old_cursor.pos > 0
3228 && old_cursor.pos < old_cursor.par->Last()
3229 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3230 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3231 old_cursor.par->Erase(old_cursor.pos - 1);
3232 RedoParagraphs(old_cursor, old_cursor.par->Next());
3233 // or RedoDrawingOfParagraph(old_cursor);
3235 if (old_cursor.par == cursor.par &&
3236 cursor.pos > old_cursor.pos)
3237 SetCursor(cursor.par, cursor.pos - 1);
3239 SetCursor(cursor.par, cursor.pos);
3244 // Paragraph should not be deleted if empty
3245 if ((textclasslist.Style(parameters->textclass,
3246 old_cursor.par->GetLayout())).keepempty)
3249 LyXCursor tmpcursor;
3251 if (old_cursor.par != cursor.par) {
3252 if ( (old_cursor.par->Last() == 0
3253 || (old_cursor.par->Last() == 1
3254 && (old_cursor.par->IsLineSeparator(0))))
3255 && old_cursor.par->FirstPhysicalPar()
3256 == old_cursor.par->LastPhysicalPar()) {
3258 // ok, we will delete anything
3260 // make sure that you do not delete any environments
3261 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3262 !(old_cursor.row->previous
3263 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3264 && !(old_cursor.row->next
3265 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3267 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3268 ((old_cursor.row->previous
3269 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3271 (old_cursor.row->next
3272 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3274 status = LyXText::NEED_MORE_REFRESH;
3277 if (old_cursor.row->previous) {
3278 refresh_row = old_cursor.row->previous;
3279 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3281 cursor = old_cursor; // that undo can restore the right cursor position
3282 LyXParagraph * endpar = old_cursor.par->next;
3283 if (endpar && endpar->GetDepth()) {
3284 while (endpar && endpar->GetDepth()) {
3285 endpar = endpar->LastPhysicalPar()->Next();
3288 SetUndo(Undo::DELETE,
3289 old_cursor.par->previous,
3294 RemoveRow(old_cursor.row);
3295 if (params->paragraph == old_cursor.par) {
3296 params->paragraph = params->paragraph->next;
3299 delete old_cursor.par;
3301 /* Breakagain the next par. Needed
3302 * because of the parindent that
3303 * can occur or dissappear. The
3304 * next row can change its height,
3305 * if there is another layout before */
3306 if (refresh_row->next) {
3307 BreakAgain(refresh_row->next);
3308 UpdateCounters(refresh_row);
3310 SetHeightOfRow(refresh_row);
3312 refresh_row = old_cursor.row->next;
3313 refresh_y = old_cursor.y - old_cursor.row->baseline;
3316 cursor = old_cursor; // that undo can restore the right cursor position
3317 LyXParagraph *endpar = old_cursor.par->next;
3318 if (endpar && endpar->GetDepth()) {
3319 while (endpar && endpar->GetDepth()) {
3320 endpar = endpar->LastPhysicalPar()->Next();
3323 SetUndo(Undo::DELETE,
3324 old_cursor.par->previous,
3329 RemoveRow(old_cursor.row);
3331 if (params->paragraph == old_cursor.par) {
3332 params->paragraph = params->paragraph->next;
3334 delete old_cursor.par;
3336 /* Breakagain the next par. Needed
3337 because of the parindent that can
3338 occur or dissappear.
3339 The next row can change its height,
3340 if there is another layout before
3343 BreakAgain(refresh_row);
3344 UpdateCounters(refresh_row->previous);
3349 SetCursor(cursor.par, cursor.pos);
3351 /* if (cursor.y > old_cursor.y)
3352 cursor.y -= old_cursor.row->height; */
3354 if (sel_cursor.par == old_cursor.par
3355 && sel_cursor.pos == sel_cursor.pos) {
3356 // correct selection
3357 sel_cursor = cursor;
3362 if (old_cursor.par->ClearParagraph()){
3363 RedoParagraphs(old_cursor, old_cursor.par->Next());
3365 SetCursor(cursor.par, cursor.pos);
3366 sel_cursor = cursor;
3373 LyXParagraph * LyXText::GetParFromID(int id)
3375 LyXParagraph * result = FirstParagraph();
3376 while (result && result->id() != id)
3377 result = result->next;
3383 bool LyXText::TextUndo()
3385 // returns false if no undo possible
3386 Undo * undo = params->undostack.pop();
3391 .push(CreateUndo(undo->kind,
3392 GetParFromID(undo->number_of_before_par),
3393 GetParFromID(undo->number_of_behind_par)));
3395 return TextHandleUndo(undo);
3399 bool LyXText::TextRedo()
3401 // returns false if no redo possible
3402 Undo * undo = params->redostack.pop();
3407 .push(CreateUndo(undo->kind,
3408 GetParFromID(undo->number_of_before_par),
3409 GetParFromID(undo->number_of_behind_par)));
3411 return TextHandleUndo(undo);
3415 bool LyXText::TextHandleUndo(Undo * undo)
3417 // returns false if no undo possible
3418 bool result = false;
3420 LyXParagraph * before =
3421 GetParFromID(undo->number_of_before_par);
3422 LyXParagraph * behind =
3423 GetParFromID(undo->number_of_behind_par);
3424 LyXParagraph * tmppar;
3425 LyXParagraph * tmppar2;
3426 LyXParagraph * tmppar3;
3427 LyXParagraph * tmppar4;
3428 LyXParagraph * endpar;
3429 LyXParagraph * tmppar5;
3431 // if there's no before take the beginning
3432 // of the document for redoing
3434 SetCursorIntern(FirstParagraph(), 0);
3436 // replace the paragraphs with the undo informations
3438 tmppar3 = undo->par;
3439 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3442 while (tmppar4->next)
3443 tmppar4 = tmppar4->next;
3444 } // get last undo par
3446 // now remove the old text if there is any
3447 if (before != behind || (!behind && !before)){
3449 tmppar5 = before->next;
3451 tmppar5 = params->paragraph;
3453 while (tmppar5 && tmppar5 != behind){
3455 tmppar5 = tmppar5->next;
3456 // a memory optimization for edit: Only layout information
3457 // is stored in the undo. So restore the text informations.
3458 if (undo->kind == Undo::EDIT){
3459 tmppar2->text = tmppar->text;
3460 tmppar->text.clear();
3461 tmppar2 = tmppar2->next;
3463 if ( currentrow && currentrow->par == tmppar )
3464 currentrow = currentrow -> previous;
3465 // Commenting out this might remove the error
3466 // reported by Purify, but it might also
3467 // introduce a memory leak. We need to
3473 // put the new stuff in the list if there is one
3476 before->next = tmppar3;
3478 params->paragraph = tmppar3;
3479 tmppar3->previous = before;
3483 params->paragraph = behind;
3486 tmppar4->next = behind;
3488 behind->previous = tmppar4;
3492 // Set the cursor for redoing
3494 SetCursorIntern(before->FirstSelfrowPar(), 0);
3495 // check wether before points to a closed float and open it if necessary
3496 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3497 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3499 while (tmppar4->previous &&
3500 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3501 tmppar4 = tmppar4->previous;
3502 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3503 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3504 tmppar4 = tmppar4->next;
3509 // open a cosed footnote at the end if necessary
3510 if (behind && behind->previous &&
3511 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3512 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3513 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3514 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3515 behind = behind->next;
3519 // calculate the endpar for redoing the paragraphs.
3521 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3522 endpar = behind->LastPhysicalPar()->Next();
3524 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3529 tmppar = GetParFromID(undo->number_of_cursor_par);
3530 RedoParagraphs(cursor, endpar);
3532 SetCursorIntern(tmppar, undo->cursor_pos);
3533 UpdateCounters(cursor.row);
3543 void LyXText::FinishUndo()
3545 // makes sure the next operation will be stored
3546 undo_finished = True;
3550 void LyXText::FreezeUndo()
3552 // this is dangerous and for internal use only
3557 void LyXText::UnFreezeUndo()
3559 // this is dangerous and for internal use only
3560 undo_frozen = false;
3564 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3565 LyXParagraph const * behind) const
3568 params->undostack.push(CreateUndo(kind, before, behind));
3569 params->redostack.clear();
3573 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3574 LyXParagraph const * behind)
3576 params->redostack.push(CreateUndo(kind, before, behind));
3580 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3581 LyXParagraph const * behind) const
3583 int before_number = -1;
3584 int behind_number = -1;
3586 before_number = before->id();
3588 behind_number = behind->id();
3589 // Undo::EDIT and Undo::FINISH are
3590 // always finished. (no overlapping there)
3591 // overlapping only with insert and delete inside one paragraph:
3592 // Nobody wants all removed character
3593 // appear one by one when undoing.
3594 // EDIT is special since only layout information, not the
3595 // contents of a paragaph are stored.
3596 if (!undo_finished && kind != Undo::EDIT &&
3597 kind != Undo::FINISH){
3598 // check wether storing is needed
3599 if (!params->undostack.empty() &&
3600 params->undostack.top()->kind == kind &&
3601 params->undostack.top()->number_of_before_par == before_number &&
3602 params->undostack.top()->number_of_behind_par == behind_number ){
3607 // create a new Undo
3608 LyXParagraph * undopar;
3609 LyXParagraph * tmppar;
3610 LyXParagraph * tmppar2;
3612 LyXParagraph * start = 0;
3613 LyXParagraph * end = 0;
3616 start = before->next;
3618 start = FirstParagraph();
3620 end = behind->previous;
3622 end = FirstParagraph();
3628 && start != end->next
3629 && (before != behind || (!before && !behind))) {
3631 tmppar2 = tmppar->Clone();
3632 tmppar2->id(tmppar->id());
3634 // a memory optimization: Just store the layout information
3636 if (kind == Undo::EDIT){
3637 tmppar2->text.clear();
3642 while (tmppar != end && tmppar->next) {
3643 tmppar = tmppar->next;
3644 tmppar2->next = tmppar->Clone();
3645 tmppar2->next->id(tmppar->id());
3646 // a memory optimization: Just store the layout
3647 // information when only edit
3648 if (kind == Undo::EDIT){
3649 tmppar2->next->text.clear();
3651 tmppar2->next->previous = tmppar2;
3652 tmppar2 = tmppar2->next;
3656 undopar = 0; // nothing to replace (undo of delete maybe)
3658 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3659 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3661 Undo * undo = new Undo(kind,
3662 before_number, behind_number,
3663 cursor_par, cursor_pos,
3666 undo_finished = false;
3671 void LyXText::SetCursorParUndo()
3673 SetUndo(Undo::FINISH,
3674 cursor.par->ParFromPos(cursor.pos)->previous,
3675 cursor.par->ParFromPos(cursor.pos)->next);
3679 void LyXText::RemoveTableRow(LyXCursor * cur) const
3685 // move to the previous row
3686 int cell_act = NumberOfCell(cur->par, cur->pos);
3689 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3692 !cur->par->table->IsFirstCell(cell_act)) {
3694 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3699 // now we have to pay attention if the actual table is the
3700 // main row of TableContRows and if yes to delete all of them
3705 // delete up to the next row
3706 while (cur->pos < cur->par->Last() &&
3708 || !cur->par->table->IsFirstCell(cell_act))) {
3709 while (cur->pos < cur->par->Last() &&
3710 !cur->par->IsNewline(cur->pos))
3711 cur->par->Erase(cur->pos);
3714 if (cur->pos < cur->par->Last())
3715 cur->par->Erase(cur->pos);
3717 if (cur->pos && cur->pos == cur->par->Last()) {
3719 cur->par->Erase(cur->pos); // no newline at very end!
3721 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3722 !cur->par->table->IsContRow(cell_org) &&
3723 cur->par->table->IsContRow(cell));
3724 cur->par->table->DeleteRow(cell_org);
3729 bool LyXText::IsEmptyTableCell() const
3731 LyXParagraph::size_type pos = cursor.pos - 1;
3732 while (pos >= 0 && pos < cursor.par->Last()
3733 && !cursor.par->IsNewline(pos))
3735 return cursor.par->IsNewline(pos + 1);
3739 void LyXText::toggleAppendix(){
3740 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3741 bool start = !par->start_of_appendix;
3743 // ensure that we have only one start_of_appendix in this document
3744 LyXParagraph * tmp = FirstParagraph();
3745 for (; tmp; tmp = tmp->next)
3746 tmp->start_of_appendix = 0;
3747 par->start_of_appendix = start;
3749 // we can set the refreshing parameters now
3750 status = LyXText::NEED_MORE_REFRESH;
3752 refresh_row = 0; // not needed for full update
3754 SetCursor(cursor.par, cursor.pos);