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(BufferView * bv, int pw, Buffer * p)
53 parameters = &p->params;
57 status = LyXText::UNCHANGED;
58 LyXParagraph * par = p->paragraph;
59 current_font = GetFont(par, 0);
64 InsertParagraph(par, lastrow);
67 // set cursor at the very top position
68 selection = true; /* these setting is necessary
69 because of the delete-empty-
70 paragraph mechanism in
72 SetCursor(firstrow->par, 0);
77 // no rebreak necessary
83 // Default layouttype for copy environment type
90 // Delete all rows, this does not touch the paragraphs!
91 Row * tmprow = firstrow;
93 tmprow = firstrow->next;
100 void LyXText::owner(BufferView * bv)
102 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
106 // Gets the fully instantiated font at a given position in a paragraph
107 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
108 // The difference is that this one is used for displaying, and thus we
109 // are allowed to make cosmetic improvements. For instance make footnotes
111 // If position is -1, we get the layout font of the paragraph.
112 // If position is -2, we get the font of the manual label of the paragraph.
113 LyXFont LyXText::GetFont(LyXParagraph * par,
114 LyXParagraph::size_type pos) const
116 LyXLayout const & layout =
117 textclasslist.Style(parameters->textclass, par->GetLayout());
119 char par_depth = par->GetDepth();
120 // We specialize the 95% common case:
121 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
124 if (layout.labeltype == LABEL_MANUAL
125 && pos < BeginningOfMainBody(par)) {
127 return par->GetFontSettings(pos).
128 realize(layout.reslabelfont);
130 return par->GetFontSettings(pos).
131 realize(layout.resfont);
134 // process layoutfont for pos == -1 and labelfont for pos < -1
136 return layout.resfont;
138 return layout.reslabelfont;
142 // The uncommon case need not be optimized as much
144 LyXFont layoutfont, tmpfont;
148 if (pos < BeginningOfMainBody(par)) {
150 layoutfont = layout.labelfont;
153 layoutfont = layout.font;
155 tmpfont = par->GetFontSettings(pos);
156 tmpfont.realize(layoutfont);
159 // process layoutfont for pos == -1 and labelfont for pos < -1
161 tmpfont = layout.font;
163 tmpfont = layout.labelfont;
166 // Resolve against environment font information
167 while (par && par_depth && !tmpfont.resolved()) {
168 par = par->DepthHook(par_depth - 1);
170 tmpfont.realize(textclasslist.
171 Style(parameters->textclass,
172 par->GetLayout()).font);
173 par_depth = par->GetDepth();
177 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
179 // Cosmetic improvement: If this is an open footnote, make the font
181 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
182 && par->footnotekind == LyXParagraph::FOOTNOTE) {
190 void LyXText::SetCharFont(LyXParagraph * par,
191 LyXParagraph::size_type pos,
195 // Let the insets convert their font
196 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
197 if (par->GetInset(pos))
198 font = par->GetInset(pos)->ConvertFont(font);
201 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
204 // Get concrete layout font to reduce against
207 if (pos < BeginningOfMainBody(par))
208 layoutfont = layout.labelfont;
210 layoutfont = layout.font;
212 // Realize against environment font information
213 if (par->GetDepth()){
214 LyXParagraph * tp = par;
215 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
216 tp = tp->DepthHook(tp->GetDepth()-1);
218 layoutfont.realize(textclasslist.
219 Style(parameters->textclass,
220 tp->GetLayout()).font);
224 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
226 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
227 && par->footnotekind == LyXParagraph::FOOTNOTE) {
228 layoutfont.decSize();
231 // Now, reduce font against full layout font
232 font.reduce(layoutfont);
234 par->SetFont(pos, font);
238 /* inserts a new row behind the specified row, increments
239 * the touched counters */
240 void LyXText::InsertRow(Row * row, LyXParagraph * par,
241 LyXParagraph::size_type pos) const
243 Row * tmprow = new Row;
245 tmprow->previous = 0;
246 tmprow->next = firstrow;
250 tmprow->previous = row;
251 tmprow->next = row->next;
256 tmprow->next->previous = tmprow;
258 if (tmprow->previous)
259 tmprow->previous->next = tmprow;
267 ++number_of_rows; // one more row
271 // removes the row and reset the touched counters
272 void LyXText::RemoveRow(Row * row) const
274 /* this must not happen before the currentrow for clear reasons.
275 so the trick is just to set the current row onto the previous
278 GetRow(row->par, row->pos, unused_y);
279 currentrow = currentrow->previous;
281 currentrow_y -= currentrow->height;
286 row->next->previous = row->previous;
287 if (!row->previous) {
288 firstrow = row->next;
291 row->previous->next = row->next;
294 lastrow = row->previous;
296 height -= row->height; // the text becomes smaller
299 --number_of_rows; // one row less
303 // remove all following rows of the paragraph of the specified row.
304 void LyXText::RemoveParagraph(Row * row) const
306 LyXParagraph * tmppar = row->par;
310 while (row && row->par == tmppar) {
318 // insert the specified paragraph behind the specified row
319 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
321 InsertRow(row, par, 0); /* insert a new row, starting
324 SetCounter(par); // set the counters
326 // and now append the whole paragraph behind the new row
328 firstrow->height = 0;
329 AppendParagraph(firstrow);
332 row->next->height = 0;
333 AppendParagraph(row->next);
338 void LyXText::ToggleFootnote()
340 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
342 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
344 current_view->owner()->getMiniBuffer()->Set(_("Opened float"));
346 current_view->owner()->getMiniBuffer()->Set(_("Closed float"));
352 void LyXText::OpenStuff()
354 if (cursor.pos == 0 && cursor.par->bibkey){
355 cursor.par->bibkey->Edit(0, 0);
357 else if (cursor.pos < cursor.par->Last()
358 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
359 && cursor.par->GetInset(cursor.pos)->Editable()) {
360 current_view->owner()->getMiniBuffer()
361 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
362 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
364 cursor.par->GetInset(cursor.pos)->Edit(0, 0);
371 void LyXText::CloseFootnote()
373 LyXParagraph * tmppar;
374 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
376 // if the cursor is not in an open footnote, or
377 // there is no open footnote in this paragraph, just return.
378 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
381 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
382 current_view->owner()->getMiniBuffer()
383 ->Set(_("Nothing to do"));
387 // ok, move the cursor right before the footnote
388 // just a little faster than using CursorRight()
390 cursor.par->ParFromPos(cursor.pos) != par;
394 // now the cursor is at the beginning of the physical par
395 SetCursor(cursor.par,
397 cursor.par->ParFromPos(cursor.pos)->text.size());
399 /* we are in a footnote, so let us move at the beginning */
400 /* this is just faster than using just CursorLeft() */
403 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
404 // just a little bit faster than movin the cursor
405 tmppar = tmppar->Previous();
407 SetCursor(tmppar, tmppar->Last());
410 // the cursor must be exactly before the footnote
411 par = cursor.par->ParFromPos(cursor.pos);
413 status = LyXText::NEED_MORE_REFRESH;
414 refresh_row = cursor.row;
415 refresh_y = cursor.y - cursor.row->baseline;
418 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
419 Row * row = cursor.row;
421 tmppar->CloseFootnote(cursor.pos);
423 while (tmppar != endpar) {
424 RemoveRow(row->next);
426 tmppar = row->next->par;
431 AppendParagraph(cursor.row);
433 SetCursor(cursor.par, cursor.pos);
437 if (cursor.row->next)
438 SetHeightOfRow(cursor.row->next);
442 /* used in setlayout */
443 // Asger is not sure we want to do this...
444 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
446 LyXFont layoutfont, tmpfont;
448 LyXLayout const & layout =
449 textclasslist.Style(parameters->textclass, par->GetLayout());
451 for (LyXParagraph::size_type pos = 0;
452 pos < par->Last(); ++pos) {
453 if (pos < BeginningOfMainBody(par))
454 layoutfont = layout.labelfont;
456 layoutfont = layout.font;
458 tmpfont = par->GetFontSettings(pos);
459 tmpfont.reduce(layoutfont);
460 par->SetFont(pos, tmpfont);
465 // set layout over selection and make a total rebreak of those paragraphs
466 void LyXText::SetLayout(char layout)
470 // if there is no selection just set the layout
471 // of the current paragraph */
473 sel_start_cursor = cursor; // dummy selection
474 sel_end_cursor = cursor;
477 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
478 LyXParagraph * undoendpar = endpar;
480 if (endpar && endpar->GetDepth()) {
481 while (endpar && endpar->GetDepth()) {
482 endpar = endpar->LastPhysicalPar()->Next();
487 endpar = endpar->Next(); // because of parindents etc.
491 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
494 tmpcursor = cursor; /* store the current cursor */
496 /* ok we have a selection. This is always between sel_start_cursor
497 * and sel_end cursor */
498 cursor = sel_start_cursor;
500 LyXLayout const & lyxlayout =
501 textclasslist.Style(parameters->textclass, layout);
503 while (cursor.par != sel_end_cursor.par) {
504 if (cursor.par->footnoteflag ==
505 sel_start_cursor.par->footnoteflag) {
506 cursor.par->SetLayout(layout);
507 MakeFontEntriesLayoutSpecific(cursor.par);
508 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
509 fppar->added_space_top = lyxlayout.fill_top ?
510 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
511 fppar->added_space_bottom = lyxlayout.fill_bottom ?
512 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
513 if (lyxlayout.margintype == MARGIN_MANUAL)
514 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
515 if (lyxlayout.labeltype != LABEL_BIBLIO
517 delete fppar->bibkey;
521 cursor.par = cursor.par->Next();
523 if (cursor.par->footnoteflag ==
524 sel_start_cursor.par->footnoteflag) {
525 cursor.par->SetLayout(layout);
526 MakeFontEntriesLayoutSpecific(cursor.par);
527 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
528 fppar->added_space_top = lyxlayout.fill_top ?
529 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
530 fppar->added_space_bottom = lyxlayout.fill_bottom ?
531 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
532 if (lyxlayout.margintype == MARGIN_MANUAL)
533 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
534 if (lyxlayout.labeltype != LABEL_BIBLIO
536 delete fppar->bibkey;
541 RedoParagraphs(sel_start_cursor, endpar);
543 // we have to reset the selection, because the
544 // geometry could have changed */
545 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
547 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
548 UpdateCounters(cursor.row);
551 SetCursor(tmpcursor.par, tmpcursor.pos);
555 // increment depth over selection and
556 // make a total rebreak of those paragraphs
557 void LyXText::IncDepth()
559 // If there is no selection, just use the current paragraph
561 sel_start_cursor = cursor; // dummy selection
562 sel_end_cursor = cursor;
565 // We end at the next paragraph with depth 0
566 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
567 LyXParagraph * undoendpar = endpar;
569 if (endpar && endpar->GetDepth()) {
570 while (endpar && endpar->GetDepth()) {
571 endpar = endpar->LastPhysicalPar()->Next();
576 endpar = endpar->Next(); // because of parindents etc.
581 .par->ParFromPos(sel_start_cursor.pos)->previous,
584 LyXCursor tmpcursor = cursor; // store the current cursor
586 // ok we have a selection. This is always between sel_start_cursor
587 // and sel_end cursor
588 cursor = sel_start_cursor;
590 bool anything_changed = false;
593 // NOTE: you can't change the depth of a bibliography entry
594 if (cursor.par->footnoteflag ==
595 sel_start_cursor.par->footnoteflag
596 && textclasslist.Style(parameters->textclass,
597 cursor.par->GetLayout()
598 ).labeltype != LABEL_BIBLIO) {
599 LyXParagraph * prev =
600 cursor.par->FirstPhysicalPar()->Previous();
602 && (prev->GetDepth() - cursor.par->GetDepth() > 0
603 || (prev->GetDepth() == cursor.par->GetDepth()
604 && textclasslist.Style(parameters->textclass,
605 prev->GetLayout()).isEnvironment()))) {
606 cursor.par->FirstPhysicalPar()->depth++;
607 anything_changed = true;
610 if (cursor.par == sel_end_cursor.par)
612 cursor.par = cursor.par->Next();
615 // if nothing changed set all depth to 0
616 if (!anything_changed) {
617 cursor = sel_start_cursor;
618 while (cursor.par != sel_end_cursor.par) {
619 cursor.par->FirstPhysicalPar()->depth = 0;
620 cursor.par = cursor.par->Next();
622 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
623 cursor.par->FirstPhysicalPar()->depth = 0;
626 RedoParagraphs(sel_start_cursor, endpar);
628 // we have to reset the selection, because the
629 // geometry could have changed
630 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
632 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
633 UpdateCounters(cursor.row);
636 SetCursor(tmpcursor.par, tmpcursor.pos);
640 // decrement depth over selection and
641 // make a total rebreak of those paragraphs
642 void LyXText::DecDepth()
644 // if there is no selection just set the layout
645 // of the current paragraph
647 sel_start_cursor = cursor; // dummy selection
648 sel_end_cursor = cursor;
651 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
652 LyXParagraph * undoendpar = endpar;
654 if (endpar && endpar->GetDepth()) {
655 while (endpar && endpar->GetDepth()) {
656 endpar = endpar->LastPhysicalPar()->Next();
661 endpar = endpar->Next(); // because of parindents etc.
666 .par->ParFromPos(sel_start_cursor.pos)->previous,
669 LyXCursor tmpcursor = cursor; // store the current cursor
671 // ok we have a selection. This is always between sel_start_cursor
672 // and sel_end cursor
673 cursor = sel_start_cursor;
676 if (cursor.par->footnoteflag ==
677 sel_start_cursor.par->footnoteflag) {
678 if (cursor.par->FirstPhysicalPar()->depth)
679 cursor.par->FirstPhysicalPar()->depth--;
681 if (cursor.par == sel_end_cursor.par)
683 cursor.par = cursor.par->Next();
686 RedoParagraphs(sel_start_cursor, endpar);
688 // we have to reset the selection, because the
689 // geometry could have changed
690 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
692 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
693 UpdateCounters(cursor.row);
696 SetCursor(tmpcursor.par, tmpcursor.pos);
700 // set font over selection and make a total rebreak of those paragraphs
701 void LyXText::SetFont(LyXFont const & font, bool toggleall)
703 // if there is no selection just set the current_font
705 // Determine basis font
707 if (cursor.pos < BeginningOfMainBody(cursor.par))
708 layoutfont = GetFont(cursor.par, -2);
710 layoutfont = GetFont(cursor.par, -1);
711 // Update current font
712 real_current_font.update(font, toggleall);
714 // Reduce to implicit settings
715 current_font = real_current_font;
716 current_font.reduce(layoutfont);
717 // And resolve it completely
718 real_current_font.realize(layoutfont);
722 LyXCursor tmpcursor = cursor; // store the current cursor
724 // ok we have a selection. This is always between sel_start_cursor
725 // and sel_end cursor
728 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
729 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
730 cursor = sel_start_cursor;
731 while (cursor.par != sel_end_cursor.par ||
732 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
733 && cursor.pos < sel_end_cursor.pos))
735 if (cursor.pos < cursor.par->Last()
736 && cursor.par->footnoteflag
737 == sel_start_cursor.par->footnoteflag) {
738 // an open footnote should behave
740 LyXFont newfont = GetFont(cursor.par, cursor.pos);
741 newfont.update(font, toggleall);
742 SetCharFont(cursor.par, cursor.pos, newfont);
746 cursor.par = cursor.par->Next();
750 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
752 // we have to reset the selection, because the
753 // geometry could have changed
754 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
756 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
759 SetCursor(tmpcursor.par, tmpcursor.pos);
763 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
765 Row * tmprow = cur.row;
766 long y = cur.y - tmprow->baseline;
768 SetHeightOfRow(tmprow);
769 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
770 // find the first row of the paragraph
771 if (first_phys_par != tmprow->par)
772 while (tmprow->previous
773 && tmprow->previous->par != first_phys_par) {
774 tmprow = tmprow->previous;
776 SetHeightOfRow(tmprow);
778 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
779 tmprow = tmprow->previous;
781 SetHeightOfRow(tmprow);
784 // we can set the refreshing parameters now
785 status = LyXText::NEED_MORE_REFRESH;
787 refresh_row = tmprow;
788 SetCursor(cur.par, cur.pos);
792 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
794 Row * tmprow = cur.row;
796 long y = cur.y - tmprow->baseline;
797 SetHeightOfRow(tmprow);
798 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
799 // find the first row of the paragraph
800 if (first_phys_par != tmprow->par)
801 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
802 tmprow = tmprow->previous;
805 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
806 tmprow = tmprow->previous;
810 // we can set the refreshing parameters now
811 if (status == LyXText::UNCHANGED || y < refresh_y) {
813 refresh_row = tmprow;
815 status = LyXText::NEED_MORE_REFRESH;
816 SetCursor(cur.par, cur.pos);
820 /* deletes and inserts again all paragaphs between the cursor
821 * and the specified par
822 * This function is needed after SetLayout and SetFont etc. */
823 void LyXText::RedoParagraphs(LyXCursor const & cur,
824 LyXParagraph const * endpar) const
827 LyXParagraph * tmppar, * first_phys_par;
829 Row * tmprow = cur.row;
831 long y = cur.y - tmprow->baseline;
833 if (!tmprow->previous){
834 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
836 first_phys_par = tmprow->par->FirstPhysicalPar();
837 // find the first row of the paragraph
838 if (first_phys_par != tmprow->par)
839 while (tmprow->previous
840 && tmprow->previous->par != first_phys_par) {
841 tmprow = tmprow->previous;
844 while (tmprow->previous
845 && tmprow->previous->par == first_phys_par) {
846 tmprow = tmprow->previous;
851 // we can set the refreshing parameters now
852 status = LyXText::NEED_MORE_REFRESH;
854 refresh_row = tmprow->previous; /* the real refresh row will
855 be deleted, so I store
859 tmppar = tmprow->next->par;
862 while (tmppar != endpar) {
863 RemoveRow(tmprow->next);
865 tmppar = tmprow->next->par;
870 // remove the first one
871 tmprow2 = tmprow; /* this is because tmprow->previous
873 tmprow = tmprow->previous;
876 tmppar = first_phys_par;
880 InsertParagraph(tmppar, tmprow);
883 while (tmprow->next && tmprow->next->par == tmppar)
884 tmprow = tmprow->next;
885 tmppar = tmppar->Next();
887 } while (tmppar != endpar);
889 // this is because of layout changes
891 refresh_y -= refresh_row->height;
892 SetHeightOfRow(refresh_row);
894 refresh_row = firstrow;
896 SetHeightOfRow(refresh_row);
899 if (tmprow && tmprow->next)
900 SetHeightOfRow(tmprow->next);
904 int LyXText::FullRebreak()
906 if (need_break_row) {
907 BreakAgain(need_break_row);
915 /* important for the screen */
918 /* the cursor set functions have a special mechanism. When they
919 * realize, that you left an empty paragraph, they will delete it.
920 * They also delet the corresponding row */
922 // need the selection cursor:
923 void LyXText::SetSelection()
926 last_sel_cursor = sel_cursor;
927 sel_start_cursor = sel_cursor;
928 sel_end_cursor = sel_cursor;
933 // first the toggling area
934 if (cursor.y < last_sel_cursor.y ||
935 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
936 toggle_end_cursor = last_sel_cursor;
937 toggle_cursor = cursor;
940 toggle_end_cursor = cursor;
941 toggle_cursor = last_sel_cursor;
944 last_sel_cursor = cursor;
946 // and now the whole selection
948 if (sel_cursor.par == cursor.par)
949 if (sel_cursor.pos < cursor.pos) {
950 sel_end_cursor = cursor;
951 sel_start_cursor = sel_cursor;
953 sel_end_cursor = sel_cursor;
954 sel_start_cursor = cursor;
956 else if (sel_cursor.y < cursor.y ||
957 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
958 sel_end_cursor = cursor;
959 sel_start_cursor = sel_cursor;
962 sel_end_cursor = sel_cursor;
963 sel_start_cursor = cursor;
966 // a selection with no contents is not a selection
967 if (sel_start_cursor.x == sel_end_cursor.x &&
968 sel_start_cursor.y == sel_end_cursor.y)
973 void LyXText::ClearSelection() const
980 void LyXText::CursorHome() const
982 SetCursor(cursor.par, cursor.row->pos);
986 void LyXText::CursorEnd() const
988 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
989 SetCursor(cursor.par, RowLast(cursor.row) + 1);
991 if (cursor.par->Last() &&
992 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
993 || cursor.par->IsNewline(RowLast(cursor.row))))
994 SetCursor(cursor.par, RowLast(cursor.row));
996 SetCursor(cursor.par, RowLast(cursor.row) + 1);
998 if (cursor.par->table) {
999 int cell = NumberOfCell(cursor.par, cursor.pos);
1000 if (cursor.par->table->RowHasContRow(cell) &&
1001 cursor.par->table->CellHasContRow(cell)<0) {
1002 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1003 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1005 if (cursor.par->Last() &&
1006 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1007 || cursor.par->IsNewline(RowLast(cursor.row))))
1008 SetCursor(cursor.par, RowLast(cursor.row));
1010 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1017 void LyXText::CursorTop() const
1019 while (cursor.par->Previous())
1020 cursor.par = cursor.par->Previous();
1021 SetCursor(cursor.par, 0);
1025 void LyXText::CursorBottom() const
1027 while (cursor.par->Next())
1028 cursor.par = cursor.par->Next();
1029 SetCursor(cursor.par, cursor.par->Last());
1033 /* returns a pointer to the row near the specified y-coordinate
1034 * (relative to the whole text). y is set to the real beginning
1036 Row * LyXText::GetRowNearY(long & y) const
1042 tmprow = currentrow;
1043 tmpy = currentrow_y;
1050 while (tmprow->next && tmpy + tmprow->height <= y) {
1051 tmpy += tmprow->height;
1052 tmprow = tmprow->next;
1055 while (tmprow->previous && tmpy > y) {
1056 tmprow = tmprow->previous;
1057 tmpy -= tmprow->height;
1060 currentrow = tmprow;
1061 currentrow_y = tmpy;
1063 y = tmpy; // return the real y
1068 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1070 // If the mask is completely neutral, tell user
1071 if (font == LyXFont(LyXFont::ALL_IGNORE)){
1072 // Could only happen with user style
1073 current_view->owner()->getMiniBuffer()
1074 ->Set(_("No font change defined. Use Character under"
1075 " the Layout menu to define font change."));
1079 // Try implicit word selection
1080 LyXCursor resetCursor = cursor;
1081 int implicitSelection = SelectWordWhenUnderCursor();
1084 SetFont(font, toggleall);
1086 /* Implicit selections are cleared afterwards and cursor is set to the
1087 original position. */
1088 if (implicitSelection) {
1090 cursor = resetCursor;
1091 SetCursor( cursor.par, cursor.pos );
1092 sel_cursor = cursor;
1097 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1099 if (textclasslist.Style(parameters->textclass,
1100 par->GetLayout()).labeltype != LABEL_MANUAL)
1103 return par->BeginningOfMainBody();
1107 /* if there is a selection, reset every environment you can find
1108 * in the selection, otherwise just the environment you are in */
1109 void LyXText::MeltFootnoteEnvironment()
1111 LyXParagraph * tmppar, * firsttmppar;
1115 /* is is only allowed, if the cursor is IN an open footnote.
1116 * Otherwise it is too dangerous */
1117 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1120 SetUndo(Undo::FINISH,
1121 cursor.par->PreviousBeforeFootnote()->previous,
1122 cursor.par->NextAfterFootnote()->next);
1124 /* ok, move to the beginning of the footnote. */
1125 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1126 cursor.par = cursor.par->Previous();
1128 SetCursor(cursor.par, cursor.par->Last());
1129 /* this is just faster than using CursorLeft(); */
1131 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1132 tmppar = firsttmppar;
1133 /* tmppar is now the paragraph right before the footnote */
1135 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1138 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1139 tmppar = tmppar->next; /* I use next instead of Next(),
1140 * because there cannot be any
1141 * footnotes in a footnote
1143 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1145 /* remember the captions and empty paragraphs */
1146 if ((textclasslist.Style(parameters->textclass,
1147 tmppar->GetLayout())
1148 .labeltype == LABEL_SENSITIVE)
1150 tmppar->SetLayout(0);
1153 // now we will paste the ex-footnote, if the layouts allow it
1154 // first restore the layout of the paragraph right behind
1157 tmppar->next->MakeSameLayout(cursor.par);
1160 if ((!tmppar->GetLayout() && !tmppar->table)
1162 && (!tmppar->Next()->Last()
1163 || tmppar->Next()->HasSameLayout(tmppar)))) {
1164 if (tmppar->Next()->Last()
1165 && tmppar->Next()->IsLineSeparator(0))
1166 tmppar->Next()->Erase(0);
1167 tmppar->PasteParagraph();
1170 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1171 * by the pasting of the beginning */
1173 /* then the beginning */
1174 /* if there is no space between the text and the footnote, so we insert
1176 * (only if the previous par and the footnotepar are not empty!) */
1177 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1178 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1179 if (firsttmppar->text.size()
1180 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1181 && first_footnote_par_is_not_empty) {
1182 firsttmppar->next->InsertChar(0, ' ');
1184 firsttmppar->PasteParagraph();
1187 /* now redo the paragaphs */
1188 RedoParagraphs(cursor, tmppar);
1190 SetCursor(cursor.par, cursor.pos);
1192 /* sometimes it can happen, that there is a counter change */
1193 Row * row = cursor.row;
1194 while (row->next && row->par != tmppar && row->next->par != tmppar)
1196 UpdateCounters(row);
1203 /* the DTP switches for paragraphs. LyX will store them in the
1204 * first physicla paragraph. When a paragraph is broken, the top settings
1205 * rest, the bottom settings are given to the new one. So I can make shure,
1206 * they do not duplicate themself and you cannnot make dirty things with
1209 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1210 bool pagebreak_top, bool pagebreak_bottom,
1211 VSpace const & space_top,
1212 VSpace const & space_bottom,
1214 string labelwidthstring,
1217 LyXCursor tmpcursor = cursor;
1219 sel_start_cursor = cursor;
1220 sel_end_cursor = cursor;
1223 // make sure that the depth behind the selection are restored, too
1224 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1225 LyXParagraph * undoendpar = endpar;
1227 if (endpar && endpar->GetDepth()) {
1228 while (endpar && endpar->GetDepth()) {
1229 endpar = endpar->LastPhysicalPar()->Next();
1230 undoendpar = endpar;
1234 endpar = endpar->Next(); // because of parindents etc.
1239 .par->ParFromPos(sel_start_cursor.pos)->previous,
1243 LyXParagraph * tmppar = sel_end_cursor.par;
1244 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1245 SetCursor(tmppar->FirstPhysicalPar(), 0);
1246 status = LyXText::NEED_MORE_REFRESH;
1247 refresh_row = cursor.row;
1248 refresh_y = cursor.y - cursor.row->baseline;
1249 if (cursor.par->footnoteflag ==
1250 sel_start_cursor.par->footnoteflag) {
1251 cursor.par->line_top = line_top;
1252 cursor.par->line_bottom = line_bottom;
1253 cursor.par->pagebreak_top = pagebreak_top;
1254 cursor.par->pagebreak_bottom = pagebreak_bottom;
1255 cursor.par->added_space_top = space_top;
1256 cursor.par->added_space_bottom = space_bottom;
1257 // does the layout allow the new alignment?
1258 if (align == LYX_ALIGN_LAYOUT)
1259 align = textclasslist
1260 .Style(parameters->textclass,
1261 cursor.par->GetLayout()).align;
1262 if (align & textclasslist
1263 .Style(parameters->textclass,
1264 cursor.par->GetLayout()).alignpossible) {
1265 if (align == textclasslist
1266 .Style(parameters->textclass,
1267 cursor.par->GetLayout()).align)
1268 cursor.par->align = LYX_ALIGN_LAYOUT;
1270 cursor.par->align = align;
1272 cursor.par->SetLabelWidthString(labelwidthstring);
1273 cursor.par->noindent = noindent;
1276 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1279 RedoParagraphs(sel_start_cursor, endpar);
1282 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1283 sel_cursor = cursor;
1284 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1286 SetCursor(tmpcursor.par, tmpcursor.pos);
1290 void LyXText::SetParagraphExtraOpt(int type,
1292 char const * widthp,
1293 int alignment, bool hfill,
1294 bool start_minipage)
1296 LyXCursor tmpcursor = cursor;
1297 LyXParagraph * tmppar;
1299 sel_start_cursor = cursor;
1300 sel_end_cursor = cursor;
1303 // make sure that the depth behind the selection are restored, too
1304 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1305 LyXParagraph * undoendpar = endpar;
1307 if (endpar && endpar->GetDepth()) {
1308 while (endpar && endpar->GetDepth()) {
1309 endpar = endpar->LastPhysicalPar()->Next();
1310 undoendpar = endpar;
1314 endpar = endpar->Next(); // because of parindents etc.
1319 .par->ParFromPos(sel_start_cursor.pos)->previous,
1322 tmppar = sel_end_cursor.par;
1323 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1324 SetCursor(tmppar->FirstPhysicalPar(), 0);
1325 status = LyXText::NEED_MORE_REFRESH;
1326 refresh_row = cursor.row;
1327 refresh_y = cursor.y - cursor.row->baseline;
1328 if (cursor.par->footnoteflag ==
1329 sel_start_cursor.par->footnoteflag) {
1330 if (type == LyXParagraph::PEXTRA_NONE) {
1331 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1332 cursor.par->UnsetPExtraType();
1333 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1336 cursor.par->SetPExtraType(type, width, widthp);
1337 cursor.par->pextra_hfill = hfill;
1338 cursor.par->pextra_start_minipage = start_minipage;
1339 cursor.par->pextra_alignment = alignment;
1342 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1344 RedoParagraphs(sel_start_cursor, endpar);
1346 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1347 sel_cursor = cursor;
1348 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1350 SetCursor(tmpcursor.par, tmpcursor.pos);
1354 static char const * alphaCounter(int n) {
1355 static char result[2];
1360 result[0] = 'A' + n;
1368 // set the counter of a paragraph. This includes the labels
1369 void LyXText::SetCounter(LyXParagraph * par) const
1371 // this is only relevant for the beginning of paragraph
1372 par = par->FirstPhysicalPar();
1374 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1377 LyXTextClass const & textclass =
1378 textclasslist.TextClass(parameters->textclass);
1380 /* copy the prev-counters to this one, unless this is the start of a
1381 footnote or of a bibliography or the very first paragraph */
1383 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1384 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1385 && par->footnotekind == LyXParagraph::FOOTNOTE)
1386 && !(textclasslist.Style(parameters->textclass,
1387 par->Previous()->GetLayout()
1388 ).labeltype != LABEL_BIBLIO
1389 && layout.labeltype == LABEL_BIBLIO)) {
1390 for (int i = 0; i < 10; ++i) {
1391 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1393 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1394 if (!par->appendix && par->start_of_appendix){
1395 par->appendix = true;
1396 for (int i = 0; i < 10; ++i) {
1397 par->setCounter(i, 0);
1400 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1401 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1404 for (int i = 0; i < 10; ++i) {
1405 par->setCounter(i, 0);
1407 par->appendix = par->start_of_appendix;
1412 // if this is an open marginnote and this is the first
1413 // entry in the marginnote and the enclosing
1414 // environment is an enum/item then correct for the
1415 // LaTeX behaviour (ARRae)
1416 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1417 && par->footnotekind == LyXParagraph::MARGIN
1419 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1420 && (par->PreviousBeforeFootnote()
1421 && textclasslist.Style(parameters->textclass,
1422 par->PreviousBeforeFootnote()->GetLayout()
1423 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1424 // Any itemize or enumerate environment in a marginnote
1425 // that is embedded in an itemize or enumerate
1426 // paragraph is seen by LaTeX as being at a deeper
1427 // level within that enclosing itemization/enumeration
1428 // even if there is a "standard" layout at the start of
1434 /* Maybe we have to increment the enumeration depth.
1435 * BUT, enumeration in a footnote is considered in isolation from its
1436 * surrounding paragraph so don't increment if this is the
1437 * first line of the footnote
1438 * AND, bibliographies can't have their depth changed ie. they
1439 * are always of depth 0
1442 && par->Previous()->GetDepth() < par->GetDepth()
1443 && textclasslist.Style(parameters->textclass,
1444 par->Previous()->GetLayout()
1445 ).labeltype == LABEL_COUNTER_ENUMI
1446 && par->enumdepth < 3
1447 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1448 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1449 && par->footnotekind == LyXParagraph::FOOTNOTE)
1450 && layout.labeltype != LABEL_BIBLIO) {
1454 /* Maybe we have to decrement the enumeration depth, see note above */
1456 && par->Previous()->GetDepth() > par->GetDepth()
1457 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1458 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1459 && par->footnotekind == LyXParagraph::FOOTNOTE)
1460 && layout.labeltype != LABEL_BIBLIO) {
1461 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1462 par->setCounter(6 + par->enumdepth,
1463 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1464 /* reset the counters.
1465 * A depth change is like a breaking layout
1467 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1468 par->setCounter(i, 0);
1471 if (!par->labelstring.empty()) {
1472 par->labelstring.clear();
1475 if (layout.margintype == MARGIN_MANUAL) {
1476 if (par->labelwidthstring.empty()) {
1477 par->SetLabelWidthString(layout.labelstring());
1481 par->SetLabelWidthString(string());
1484 /* is it a layout that has an automatic label ? */
1485 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1487 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1488 if (i >= 0 && i<= parameters->secnumdepth) {
1489 par->incCounter(i); // increment the counter
1491 char * s = new char[50];
1493 // Is there a label? Useful for Chapter layout
1494 if (!par->appendix){
1495 if (!layout.labelstring().empty())
1496 par->labelstring = layout.labelstring();
1498 par->labelstring.clear();
1500 if (!layout.labelstring_appendix().empty())
1501 par->labelstring = layout.labelstring_appendix();
1503 par->labelstring.clear();
1506 if (!par->appendix){
1507 switch (2 * LABEL_FIRST_COUNTER -
1508 textclass.maxcounter() + i) {
1509 case LABEL_COUNTER_CHAPTER:
1511 par->getCounter(i));
1513 case LABEL_COUNTER_SECTION:
1515 par->getCounter(i - 1),
1516 par->getCounter(i));
1518 case LABEL_COUNTER_SUBSECTION:
1519 sprintf(s, "%d.%d.%d",
1520 par->getCounter(i-2),
1521 par->getCounter(i-1),
1522 par->getCounter(i));
1524 case LABEL_COUNTER_SUBSUBSECTION:
1525 sprintf(s, "%d.%d.%d.%d",
1526 par->getCounter(i-3),
1527 par->getCounter(i-2),
1528 par->getCounter(i-1),
1529 par->getCounter(i));
1531 case LABEL_COUNTER_PARAGRAPH:
1532 sprintf(s, "%d.%d.%d.%d.%d",
1533 par->getCounter(i-4),
1534 par->getCounter(i-3),
1535 par->getCounter(i-2),
1536 par->getCounter(i-1),
1537 par->getCounter(i));
1539 case LABEL_COUNTER_SUBPARAGRAPH:
1540 sprintf(s, "%d.%d.%d.%d.%d.%d",
1541 par->getCounter(i-5),
1542 par->getCounter(i-4),
1543 par->getCounter(i-3),
1544 par->getCounter(i-2),
1545 par->getCounter(i-1),
1546 par->getCounter(i));
1549 sprintf(s, "%d.", par->getCounter(i));
1553 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1554 case LABEL_COUNTER_CHAPTER:
1556 alphaCounter(par->getCounter(i)));
1558 case LABEL_COUNTER_SECTION:
1560 alphaCounter(par->getCounter(i - 1)),
1561 par->getCounter(i));
1563 case LABEL_COUNTER_SUBSECTION:
1564 sprintf(s, "%s.%d.%d",
1565 alphaCounter(par->getCounter(i-2)),
1566 par->getCounter(i-1),
1567 par->getCounter(i));
1569 case LABEL_COUNTER_SUBSUBSECTION:
1570 sprintf(s, "%s.%d.%d.%d",
1571 alphaCounter(par->getCounter(i-3)),
1572 par->getCounter(i-2),
1573 par->getCounter(i-1),
1574 par->getCounter(i));
1576 case LABEL_COUNTER_PARAGRAPH:
1577 sprintf(s, "%s.%d.%d.%d.%d",
1578 alphaCounter(par->getCounter(i-4)),
1579 par->getCounter(i-3),
1580 par->getCounter(i-2),
1581 par->getCounter(i-1),
1582 par->getCounter(i));
1584 case LABEL_COUNTER_SUBPARAGRAPH:
1585 sprintf(s, "%s.%d.%d.%d.%d.%d",
1586 alphaCounter(par->getCounter(i-5)),
1587 par->getCounter(i-4),
1588 par->getCounter(i-3),
1589 par->getCounter(i-2),
1590 par->getCounter(i-1),
1591 par->getCounter(i));
1594 sprintf(s, "%c.", par->getCounter(i));
1599 par->labelstring += s;
1602 for (i++; i < 10; ++i) {
1603 // reset the following counters
1604 par->setCounter(i, 0);
1606 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1607 for (i++; i < 10; ++i) {
1608 // reset the following counters
1609 par->setCounter(i, 0);
1611 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1612 par->incCounter(i + par->enumdepth);
1613 char * s = new char[25];
1614 int number = par->getCounter(i + par->enumdepth);
1616 static const char *roman[20] = {
1617 "i", "ii", "iii", "iv", "v",
1618 "vi", "vii", "viii", "ix", "x",
1619 "xi", "xii", "xiii", "xiv", "xv",
1620 "xvi", "xvii", "xviii", "xix", "xx"
1622 static const char hebrew[22] = {
1623 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1624 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1625 '÷', 'ø', 'ù', 'ú'
1628 switch (par->enumdepth) {
1630 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1631 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1633 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1636 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1637 sprintf(s, "%s.", roman[(number-1) % 20]);
1639 sprintf(s, ".%s", roman[(number-1) % 20]);
1642 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1643 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1645 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1648 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1649 sprintf(s, "%d.", number);
1651 sprintf(s, ".%d", number);
1654 par->labelstring = s;
1657 for (i += par->enumdepth + 1; i < 10; ++i)
1658 par->setCounter(i, 0); /* reset the following counters */
1661 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1662 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1664 int number = par->getCounter(i);
1666 par->bibkey = new InsetBibKey();
1667 par->bibkey->setCounter(number);
1668 par->labelstring = layout.labelstring();
1670 // In biblio should't be following counters but...
1672 string s = layout.labelstring();
1674 // the caption hack:
1676 if (layout.labeltype == LABEL_SENSITIVE) {
1677 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1678 && (par->footnotekind == LyXParagraph::FIG
1679 || par->footnotekind == LyXParagraph::WIDE_FIG))
1680 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1684 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1685 && (par->footnotekind == LyXParagraph::TAB
1686 || par->footnotekind == LyXParagraph::WIDE_TAB))
1687 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1691 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1692 && par->footnotekind == LyXParagraph::ALGORITHM)
1693 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1696 s = ":Ãúéøåâìà ";
1698 /* par->SetLayout(0);
1699 s = layout->labelstring; */
1700 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1703 s = " :úåòîùî øñç";
1707 par->labelstring = s;
1709 /* reset the enumeration counter. They are always resetted
1710 * when there is any other layout between */
1711 for (int i = 6 + par->enumdepth; i < 10; ++i)
1712 par->setCounter(i, 0);
1717 /* Updates all counters BEHIND the row. Changed paragraphs
1718 * with a dynamic left margin will be rebroken. */
1719 void LyXText::UpdateCounters(Row * row) const
1728 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1729 par = row->par->LastPhysicalPar()->Next();
1731 par = row->par->next;
1736 while (row->par != par)
1741 /* now check for the headline layouts. remember that they
1742 * have a dynamic left margin */
1744 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1745 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1748 /* Rebreak the paragraph */
1749 RemoveParagraph(row);
1750 AppendParagraph(row);
1752 /* think about the damned open footnotes! */
1753 while (par->Next() &&
1754 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1755 || par->Next()->IsDummy())){
1757 if (par->IsDummy()) {
1758 while (row->par != par)
1760 RemoveParagraph(row);
1761 AppendParagraph(row);
1766 par = par->LastPhysicalPar()->Next();
1772 /* insets an inset. */
1773 void LyXText::InsertInset(Inset *inset)
1775 SetUndo(Undo::INSERT,
1776 cursor.par->ParFromPos(cursor.pos)->previous,
1777 cursor.par->ParFromPos(cursor.pos)->next);
1778 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1779 cursor.par->InsertInset(cursor.pos, inset);
1780 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1781 * The character will not be inserted a
1786 // this is for the simple cut and paste mechanism
1787 static LyXParagraph * simple_cut_buffer = 0;
1788 static char simple_cut_buffer_textclass = 0;
1790 void DeleteSimpleCutBuffer()
1792 if (!simple_cut_buffer)
1794 LyXParagraph * tmppar;
1796 while (simple_cut_buffer) {
1797 tmppar = simple_cut_buffer;
1798 simple_cut_buffer = simple_cut_buffer->next;
1801 simple_cut_buffer = 0;
1805 void LyXText::copyEnvironmentType()
1807 copylayouttype = cursor.par->GetLayout();
1811 void LyXText::pasteEnvironmentType()
1813 SetLayout(copylayouttype);
1817 void LyXText::CutSelection(bool doclear)
1819 // This doesn't make sense, if there is no selection
1823 // OK, we have a selection. This is always between sel_start_cursor
1824 // and sel_end cursor
1825 LyXParagraph * tmppar;
1827 // Check whether there are half footnotes in the selection
1828 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1829 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1830 tmppar = sel_start_cursor.par;
1831 while (tmppar != sel_end_cursor.par){
1832 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1833 WriteAlert(_("Impossible operation"),
1834 _("Don't know what to do with half floats."),
1838 tmppar = tmppar->Next();
1842 /* table stuff -- begin */
1843 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1844 if ( sel_start_cursor.par != sel_end_cursor.par) {
1845 WriteAlert(_("Impossible operation"),
1846 _("Don't know what to do with half tables."),
1850 sel_start_cursor.par->table->Reinit();
1852 /* table stuff -- end */
1854 // make sure that the depth behind the selection are restored, too
1855 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1856 LyXParagraph * undoendpar = endpar;
1858 if (endpar && endpar->GetDepth()) {
1859 while (endpar && endpar->GetDepth()) {
1860 endpar = endpar->LastPhysicalPar()->Next();
1861 undoendpar = endpar;
1863 } else if (endpar) {
1864 endpar = endpar->Next(); // because of parindents etc.
1867 SetUndo(Undo::DELETE,
1869 .par->ParFromPos(sel_start_cursor.pos)->previous,
1872 // clear the simple_cut_buffer
1873 DeleteSimpleCutBuffer();
1875 // set the textclass
1876 simple_cut_buffer_textclass = parameters->textclass;
1878 #ifdef WITH_WARNINGS
1879 #warning Asger: Make cut more intelligent here.
1882 White paper for "intelligent" cutting:
1884 Example: "This is our text."
1885 Using " our " as selection, cutting will give "This istext.".
1886 Using "our" as selection, cutting will give "This is text.".
1887 Using " our" as selection, cutting will give "This is text.".
1888 Using "our " as selection, cutting will give "This is text.".
1890 All those four selections will (however) paste identically:
1891 Pasting with the cursor right after the "is" will give the
1892 original text with all four selections.
1894 The rationale is to be intelligent such that words are copied,
1895 cut and pasted in a functional manner.
1897 This is not implemented yet. (Asger)
1899 The changes below sees to do a lot of what you want. However
1900 I have not verified that all cases work as they should:
1902 - cut in multiple row
1904 - cut across footnotes and paragraph
1905 My simplistic tests show that the idea are basically sound but
1906 there are some items to fix up...we only need to find them
1909 As do redo Asger's example above (with | beeing the cursor in the
1910 result after cutting.):
1912 Example: "This is our text."
1913 Using " our " as selection, cutting will give "This is|text.".
1914 Using "our" as selection, cutting will give "This is | text.".
1915 Using " our" as selection, cutting will give "This is| text.".
1916 Using "our " as selection, cutting will give "This is |text.".
1921 #ifndef FIX_DOUBLE_SPACE
1922 bool space_wrapped =
1923 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1924 if (sel_end_cursor.pos > 0
1925 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1926 // please break before a space at the end
1927 sel_end_cursor.pos--;
1928 space_wrapped = true;
1930 // cut behind a space if there is one
1931 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1932 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1933 && (sel_start_cursor.par != sel_end_cursor.par
1934 || sel_start_cursor.pos < sel_end_cursor.pos))
1935 sel_start_cursor.pos++;
1937 // there are two cases: cut only within one paragraph or
1938 // more than one paragraph
1940 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1941 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1942 // only within one paragraph
1943 simple_cut_buffer = new LyXParagraph;
1944 LyXParagraph::size_type i =
1945 sel_start_cursor.pos;
1946 for (; i < sel_end_cursor.pos; ++i) {
1947 /* table stuff -- begin */
1948 if (sel_start_cursor.par->table
1949 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1950 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1951 sel_start_cursor.pos++;
1953 /* table stuff -- end */
1954 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1955 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1957 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1959 #ifndef FIX_DOUBLE_SPACE
1960 // check for double spaces
1961 if (sel_start_cursor.pos &&
1962 sel_start_cursor.par->Last() > sel_start_cursor.pos
1963 && sel_start_cursor.par
1964 ->IsLineSeparator(sel_start_cursor.pos - 1)
1965 && sel_start_cursor.par
1966 ->IsLineSeparator(sel_start_cursor.pos)) {
1967 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1970 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1973 endpar = sel_end_cursor.par->Next();
1975 // cut more than one paragraph
1978 ->BreakParagraphConservative(sel_end_cursor.pos);
1979 #ifndef FIX_DOUBLE_SPACE
1980 // insert a space at the end if there was one
1983 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1985 sel_end_cursor.par = sel_end_cursor.par->Next();
1986 sel_end_cursor.pos = 0;
1988 cursor = sel_end_cursor;
1990 #ifndef FIX_DOUBLE_SPACE
1991 // please break behind a space, if there is one.
1992 // The space should be copied too
1993 if (sel_start_cursor.par
1994 ->IsLineSeparator(sel_start_cursor.pos))
1995 sel_start_cursor.pos++;
1997 sel_start_cursor.par
1998 ->BreakParagraphConservative(sel_start_cursor.pos);
1999 #ifndef FIX_DOUBLE_SPACE
2000 if (!sel_start_cursor.pos
2001 || sel_start_cursor.par
2002 ->IsLineSeparator(sel_start_cursor.pos - 1)
2003 || sel_start_cursor.par
2004 ->IsNewline(sel_start_cursor.pos - 1)) {
2005 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2008 // store the endparagraph for redoing later
2009 endpar = sel_end_cursor.par->Next(); /* needed because
2014 // store the selection
2015 simple_cut_buffer = sel_start_cursor.par
2016 ->ParFromPos(sel_start_cursor.pos)->next;
2017 simple_cut_buffer->previous = 0;
2018 sel_end_cursor.par->previous->next = 0;
2020 // cut the selection
2021 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2022 = sel_end_cursor.par;
2024 sel_end_cursor.par->previous
2025 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2027 // care about footnotes
2028 if (simple_cut_buffer->footnoteflag) {
2029 LyXParagraph * tmppar = simple_cut_buffer;
2031 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2032 tmppar = tmppar->next;
2036 // the cut selection should begin with standard layout
2037 simple_cut_buffer->Clear();
2039 // paste the paragraphs again, if possible
2041 sel_start_cursor.par->Next()->ClearParagraph();
2042 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2044 !sel_start_cursor.par->Next()->Last())
2045 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2047 #ifndef FIX_DOUBLE_SPACE
2048 // maybe a forgotten blank
2049 if (sel_start_cursor.pos
2050 && sel_start_cursor.par
2051 ->IsLineSeparator(sel_start_cursor.pos)
2052 && sel_start_cursor.par
2053 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2054 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2059 // sometimes necessary
2061 sel_start_cursor.par->ClearParagraph();
2063 RedoParagraphs(sel_start_cursor, endpar);
2066 cursor = sel_start_cursor;
2067 SetCursor(cursor.par, cursor.pos);
2068 sel_cursor = cursor;
2069 UpdateCounters(cursor.row);
2073 void LyXText::CopySelection()
2075 // this doesnt make sense, if there is no selection
2079 // ok we have a selection. This is always between sel_start_cursor
2080 // and sel_end cursor
2081 LyXParagraph * tmppar;
2083 /* check wether there are half footnotes in the selection */
2084 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2085 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2086 tmppar = sel_start_cursor.par;
2087 while (tmppar != sel_end_cursor.par) {
2088 if (tmppar->footnoteflag !=
2089 sel_end_cursor.par->footnoteflag) {
2090 WriteAlert(_("Impossible operation"),
2091 _("Don't know what to do"
2092 " with half floats."),
2096 tmppar = tmppar->Next();
2100 /* table stuff -- begin */
2101 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2102 if ( sel_start_cursor.par != sel_end_cursor.par){
2103 WriteAlert(_("Impossible operation"),
2104 _("Don't know what to do with half tables."),
2109 /* table stuff -- end */
2111 // delete the simple_cut_buffer
2112 DeleteSimpleCutBuffer();
2114 // set the textclass
2115 simple_cut_buffer_textclass = parameters->textclass;
2117 #ifdef FIX_DOUBLE_SPACE
2118 // copy behind a space if there is one
2119 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2120 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2121 && (sel_start_cursor.par != sel_end_cursor.par
2122 || sel_start_cursor.pos < sel_end_cursor.pos))
2123 sel_start_cursor.pos++;
2125 // there are two cases: copy only within one paragraph
2126 // or more than one paragraph
2127 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2128 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2129 // only within one paragraph
2130 simple_cut_buffer = new LyXParagraph;
2131 LyXParagraph::size_type i = 0;
2132 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2133 sel_start_cursor.par->CopyIntoMinibuffer(i);
2134 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2137 // copy more than one paragraph
2138 // clone the paragraphs within the selection
2140 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2141 simple_cut_buffer = tmppar->Clone();
2142 LyXParagraph *tmppar2 = simple_cut_buffer;
2144 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2146 tmppar = tmppar->next;
2147 tmppar2->next = tmppar->Clone();
2148 tmppar2->next->previous = tmppar2;
2149 tmppar2 = tmppar2->next;
2153 // care about footnotes
2154 if (simple_cut_buffer->footnoteflag) {
2155 tmppar = simple_cut_buffer;
2157 tmppar->footnoteflag =
2158 LyXParagraph::NO_FOOTNOTE;
2159 tmppar = tmppar->next;
2163 // the simple_cut_buffer paragraph is too big
2164 LyXParagraph::size_type tmpi2 =
2165 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2166 for (; tmpi2; --tmpi2)
2167 simple_cut_buffer->Erase(0);
2169 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2171 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2172 while (tmppar2->size() > tmpi2) {
2173 tmppar2->Erase(tmppar2->text.size() - 1);
2179 void LyXText::PasteSelection()
2181 // this does not make sense, if there is nothing to paste
2182 if (!simple_cut_buffer)
2185 LyXParagraph * tmppar;
2186 LyXParagraph * endpar;
2188 LyXCursor tmpcursor;
2190 // be carefull with footnotes in footnotes
2191 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2193 // check whether the cut_buffer includes a footnote
2194 tmppar = simple_cut_buffer;
2196 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2197 tmppar = tmppar->next;
2200 WriteAlert(_("Impossible operation"),
2201 _("Can't paste float into float!"),
2207 /* table stuff -- begin */
2208 if (cursor.par->table) {
2209 if (simple_cut_buffer->next) {
2210 WriteAlert(_("Impossible operation"),
2211 _("Table cell cannot include more than one paragraph!"),
2216 /* table stuff -- end */
2218 SetUndo(Undo::INSERT,
2219 cursor.par->ParFromPos(cursor.pos)->previous,
2220 cursor.par->ParFromPos(cursor.pos)->next);
2224 // There are two cases: cutbuffer only one paragraph or many
2225 if (!simple_cut_buffer->next) {
2226 // only within a paragraph
2228 #ifndef FIX_DOUBLE_SPACE
2229 // please break behind a space, if there is one
2230 while (tmpcursor.par->Last() > tmpcursor.pos
2231 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2234 tmppar = simple_cut_buffer->Clone();
2235 /* table stuff -- begin */
2236 bool table_too_small = false;
2237 if (tmpcursor.par->table) {
2238 while (simple_cut_buffer->text.size()
2239 && !table_too_small) {
2240 if (simple_cut_buffer->IsNewline(0)){
2241 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2243 simple_cut_buffer->Erase(0);
2244 if (tmpcursor.pos < tmpcursor.par->Last())
2247 table_too_small = true;
2249 #ifdef FIX_DOUBLE_SPACE
2250 // This is an attempt to fix the
2251 // "never insert a space at the
2252 // beginning of a paragraph" problem.
2253 if (tmpcursor.pos == 0
2254 && simple_cut_buffer->IsLineSeparator(0)) {
2255 simple_cut_buffer->Erase(0);
2257 simple_cut_buffer->CutIntoMinibuffer(0);
2258 simple_cut_buffer->Erase(0);
2259 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2263 simple_cut_buffer->CutIntoMinibuffer(0);
2264 simple_cut_buffer->Erase(0);
2265 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2271 /* table stuff -- end */
2272 // Some provisions should be done here for checking
2273 // if we are inserting at the beginning of a
2274 // paragraph. If there are a space at the beginning
2275 // of the text to insert and we are inserting at
2276 // the beginning of the paragraph the space should
2278 while (simple_cut_buffer->text.size()) {
2279 #ifdef FIX_DOUBLE_SPACE
2280 // This is an attempt to fix the
2281 // "never insert a space at the
2282 // beginning of a paragraph" problem.
2283 if (tmpcursor.pos == 0
2284 && simple_cut_buffer->IsLineSeparator(0)) {
2285 simple_cut_buffer->Erase(0);
2287 simple_cut_buffer->CutIntoMinibuffer(0);
2288 simple_cut_buffer->Erase(0);
2289 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2293 simple_cut_buffer->CutIntoMinibuffer(0);
2294 simple_cut_buffer->Erase(0);
2295 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2300 delete simple_cut_buffer;
2301 simple_cut_buffer = tmppar;
2302 endpar = tmpcursor.par->Next();
2306 // make a copy of the simple cut_buffer
2307 tmppar = simple_cut_buffer;
2308 LyXParagraph * simple_cut_clone = tmppar->Clone();
2309 LyXParagraph * tmppar2 = simple_cut_clone;
2310 if (cursor.par->footnoteflag){
2311 tmppar->footnoteflag = cursor.par->footnoteflag;
2312 tmppar->footnotekind = cursor.par->footnotekind;
2314 while (tmppar->next) {
2315 tmppar = tmppar->next;
2316 tmppar2->next = tmppar->Clone();
2317 tmppar2->next->previous = tmppar2;
2318 tmppar2 = tmppar2->next;
2319 if (cursor.par->footnoteflag){
2320 tmppar->footnoteflag = cursor.par->footnoteflag;
2321 tmppar->footnotekind = cursor.par->footnotekind;
2325 // make sure there is no class difference
2326 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2327 parameters->textclass,
2330 // make the simple_cut_buffer exactly the same layout than
2331 // the cursor paragraph
2332 simple_cut_buffer->MakeSameLayout(cursor.par);
2334 // find the end of the buffer
2335 LyXParagraph * lastbuffer = simple_cut_buffer;
2336 while (lastbuffer->Next())
2337 lastbuffer = lastbuffer->Next();
2339 #ifndef FIX_DOUBLE_SPACE
2340 // Please break behind a space, if there is one. The space
2341 // should be copied too.
2342 if (cursor.par->Last() > cursor.pos
2343 && cursor.par->IsLineSeparator(cursor.pos))
2346 bool paste_the_end = false;
2348 // open the paragraph for inserting the simple_cut_buffer
2350 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2351 cursor.par->BreakParagraphConservative(cursor.pos);
2352 paste_the_end = true;
2355 #ifndef FIX_DOUBLE_SPACE
2356 // be careful with double spaces
2357 if ((!cursor.par->Last()
2358 || cursor.par->IsLineSeparator(cursor.pos - 1)
2359 || cursor.par->IsNewline(cursor.pos - 1))
2360 && simple_cut_buffer->text.size()
2361 && simple_cut_buffer->IsLineSeparator(0))
2362 simple_cut_buffer->Erase(0);
2364 // set the end for redoing later
2365 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2368 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2369 cursor.par->ParFromPos(cursor.pos)->next;
2370 cursor.par->ParFromPos(cursor.pos)->next->previous =
2371 lastbuffer->ParFromPos(lastbuffer->Last());
2373 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2374 simple_cut_buffer->previous =
2375 cursor.par->ParFromPos(cursor.pos);
2377 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2378 lastbuffer = cursor.par;
2380 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2382 // store the new cursor position
2383 tmpcursor.par = lastbuffer;
2384 tmpcursor.pos = lastbuffer->Last();
2386 // maybe some pasting
2387 if (lastbuffer->Next() && paste_the_end) {
2388 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2389 #ifndef FIX_DOUBLE_SPACE
2390 // be careful with double spaces
2391 if ((!lastbuffer->Last()
2392 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2393 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2394 && lastbuffer->Next()->Last()
2395 && lastbuffer->Next()->IsLineSeparator(0))
2396 lastbuffer->Next()->Erase(0);
2398 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2400 } else if (!lastbuffer->Next()->Last()) {
2401 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2402 #ifndef FIX_DOUBLE_SPACE
2403 // be careful witth double spaces
2404 if ((!lastbuffer->Last()
2405 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2406 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2407 && lastbuffer->Next()->Last()
2408 && lastbuffer->Next()->IsLineSeparator(0))
2409 lastbuffer->Next()->Erase(0);
2411 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2413 } else if (!lastbuffer->Last()) {
2414 lastbuffer->MakeSameLayout(lastbuffer->next);
2415 #ifndef FIX_DOUBLE_SPACE
2416 // be careful witth double spaces
2417 if ((!lastbuffer->Last()
2418 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2419 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2420 && lastbuffer->Next()->Last()
2421 && lastbuffer->Next()->IsLineSeparator(0))
2422 lastbuffer->Next()->Erase(0);
2424 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2427 lastbuffer->Next()->ClearParagraph();
2430 // restore the simple cut buffer
2431 simple_cut_buffer = simple_cut_clone;
2434 RedoParagraphs(cursor, endpar);
2436 SetCursor(cursor.par, cursor.pos);
2439 sel_cursor = cursor;
2440 SetCursor(tmpcursor.par, tmpcursor.pos);
2442 UpdateCounters(cursor.row);
2446 // returns a pointer to the very first LyXParagraph
2447 LyXParagraph * LyXText::FirstParagraph() const
2449 return params->paragraph;
2453 // returns true if the specified string is at the specified position
2454 bool LyXText::IsStringInText(LyXParagraph * par,
2455 LyXParagraph::size_type pos,
2456 char const * str) const
2460 while (pos + i < par->Last() && str[i] &&
2461 str[i] == par->GetChar(pos + i)) {
2471 // sets the selection over the number of characters of string, no check!!
2472 void LyXText::SetSelectionOverString(char const * string)
2474 sel_cursor = cursor;
2475 for (int i = 0; string[i]; ++i)
2481 // simple replacing. The font of the first selected character is used
2482 void LyXText::ReplaceSelectionWithString(char const * str)
2487 if (!selection) { // create a dummy selection
2488 sel_end_cursor = cursor;
2489 sel_start_cursor = cursor;
2492 // Get font setting before we cut
2493 LyXParagraph::size_type pos = sel_end_cursor.pos;
2494 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2496 // Insert the new string
2497 for (int i = 0; str[i]; ++i) {
2498 sel_end_cursor.par->InsertChar(pos, str[i]);
2499 sel_end_cursor.par->SetFont(pos, font);
2503 // Cut the selection
2510 // if the string can be found: return true and set the cursor to
2512 bool LyXText::SearchForward(char const * str) const
2514 LyXParagraph * par = cursor.par;
2515 LyXParagraph::size_type pos = cursor.pos;
2516 while (par && !IsStringInText(par, pos, str)) {
2517 if (pos < par->Last() - 1)
2525 SetCursor(par, pos);
2533 bool LyXText::SearchBackward(char const * string) const
2535 LyXParagraph * par = cursor.par;
2536 int pos = cursor.pos;
2542 // We skip empty paragraphs (Asger)
2544 par = par->Previous();
2546 pos = par->Last() - 1;
2547 } while (par && pos < 0);
2549 } while (par && !IsStringInText(par, pos, string));
2552 SetCursor(par, pos);
2559 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2561 char * str = new char[text.size() + 1];
2562 copy(text.begin(), text.end(), str);
2563 str[text.size()] = '\0';
2569 // needed to insert the selection
2570 void LyXText::InsertStringA(char const * s)
2573 LyXParagraph * par = cursor.par;
2574 LyXParagraph::size_type pos = cursor.pos;
2575 LyXParagraph::size_type a = 0;
2577 LyXParagraph * endpar = cursor.par->Next();
2582 textclasslist.Style(parameters->textclass,
2583 cursor.par->GetLayout()).isEnvironment();
2584 // only to be sure, should not be neccessary
2587 // insert the string, don't insert doublespace
2588 string::size_type i = 0;
2589 while (i < str.length()) {
2590 if (str[i] != '\n') {
2592 && i + 1 < str.length() && str[i + 1] != ' '
2593 && pos && par->GetChar(pos - 1)!= ' ') {
2594 par->InsertChar(pos,' ');
2596 } else if (par->table) {
2597 if (str[i] == '\t') {
2598 while((pos < par->size()) &&
2599 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2601 if (pos < par->size())
2603 else // no more fields to fill skip the rest
2605 } else if ((str[i] != 13) &&
2606 ((str[i] & 127) >= ' ')) {
2607 par->InsertChar(pos, str[i]);
2610 } else if (str[i] == ' ') {
2611 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2613 } else if (str[i] == '\t') {
2614 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2615 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2618 } else if (str[i]!= 13 &&
2619 // Ignore unprintables
2620 (str[i] & 127) >= ' ') {
2621 par->InsertChar(pos, str[i]);
2626 if (i + 1 >= str.length()) {
2630 while((pos < par->size()) &&
2631 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2634 cell = NumberOfCell(par, pos);
2635 while((pos < par->size()) &&
2636 !(par->table->IsFirstCell(cell))) {
2637 while((pos < par->size()) &&
2638 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2641 cell = NumberOfCell(par, pos);
2643 if (pos >= par->size())
2644 // no more fields to fill skip the rest
2647 if (!par->text.size()) {
2648 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2651 par->BreakParagraph(pos, flag);
2659 RedoParagraphs(cursor, endpar);
2660 SetCursor(cursor.par, cursor.pos);
2661 sel_cursor = cursor;
2662 SetCursor(par, pos);
2667 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2669 char * str = new char[text.size() + 1];
2670 copy(text.begin(), text.end(), str);
2671 str[text.size()] = '\0';
2677 /* turns double-CR to single CR, others where converted into one blank and 13s
2678 * that are ignored .Double spaces are also converted into one. Spaces at
2679 * the beginning of a paragraph are forbidden. tabs are converted into one
2680 * space. then InsertStringA is called */
2681 void LyXText::InsertStringB(char const * s)
2684 LyXParagraph * par = cursor.par;
2685 string::size_type i = 1;
2686 while (i < str.length()) {
2687 if (str[i] == '\t' && !par->table)
2689 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2691 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2692 if (str[i + 1] != '\n') {
2693 if (str[i - 1] != ' ')
2698 while (i + 1 < str.length()
2699 && (str[i + 1] == ' '
2700 || str[i + 1] == '\t'
2701 || str[i + 1] == '\n'
2702 || str[i + 1] == 13)) {
2709 InsertStringA(str.c_str());
2713 bool LyXText::GotoNextError() const
2715 LyXCursor res = cursor;
2717 if (res.pos < res.par->Last() - 1) {
2721 res.par = res.par->Next();
2726 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2727 && res.par->GetInset(res.pos)->AutoDelete()));
2730 SetCursor(res.par, res.pos);
2737 bool LyXText::GotoNextNote() const
2739 LyXCursor res = cursor;
2741 if (res.pos < res.par->Last() - 1) {
2744 res.par = res.par->Next();
2749 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2750 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2753 SetCursor(res.par, res.pos);
2760 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2764 if (!par || class1 == class2)
2766 par = par->FirstPhysicalPar();
2768 string name = textclasslist.NameOfLayout(class1, par->layout);
2770 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2771 textclasslist.NumberOfLayout(class2, name);
2774 } else { // layout not found
2775 // use default layout "Standard" (0)
2780 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2782 string s = "Layout had to be changed from\n"
2783 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2784 + "\nbecause of class conversion from\n"
2785 + textclasslist.NameOfClass(class1) + " to "
2786 + textclasslist.NameOfClass(class2);
2787 InsetError * new_inset = new InsetError(s);
2788 par->InsertChar(0, LyXParagraph::META_INSET);
2789 par->InsertInset(0, new_inset);
2798 void LyXText::CheckParagraph(LyXParagraph * par,
2799 LyXParagraph::size_type pos)
2802 LyXCursor tmpcursor;
2804 /* table stuff -- begin*/
2807 CheckParagraphInTable(par, pos);
2810 /* table stuff -- end*/
2813 LyXParagraph::size_type z;
2814 Row * row = GetRow(par, pos, y);
2816 // is there a break one row above
2817 if (row->previous && row->previous->par == row->par) {
2818 z = NextBreakPoint(row->previous, paperwidth);
2819 if ( z >= row->pos) {
2820 // set the dimensions of the row above
2821 y -= row->previous->height;
2823 refresh_row = row->previous;
2824 status = LyXText::NEED_MORE_REFRESH;
2826 BreakAgain(row->previous);
2828 // set the cursor again. Otherwise
2829 // dangling pointers are possible
2830 SetCursor(cursor.par, cursor.pos);
2831 sel_cursor = cursor;
2836 int tmpheight = row->height;
2837 LyXParagraph::size_type tmplast = RowLast(row);
2842 if (row->height == tmpheight && RowLast(row) == tmplast)
2843 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2845 status = LyXText::NEED_MORE_REFRESH;
2847 // check the special right address boxes
2848 if (textclasslist.Style(parameters->textclass,
2849 par->GetLayout()).margintype
2850 == MARGIN_RIGHT_ADDRESS_BOX) {
2851 tmpcursor.par = par;
2852 tmpcursor.row = row;
2855 tmpcursor.x_fix = 0;
2856 tmpcursor.pos = pos;
2857 RedoDrawingOfParagraph(tmpcursor);
2862 // set the cursor again. Otherwise dangling pointers are possible
2863 // also set the selection
2867 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2868 sel_cursor = cursor;
2869 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2870 sel_start_cursor = cursor;
2871 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2872 sel_end_cursor = cursor;
2873 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2874 last_sel_cursor = cursor;
2877 SetCursorIntern(cursor.par, cursor.pos);
2881 // returns 0 if inset wasn't found
2882 int LyXText::UpdateInset(Inset * inset)
2884 // first check the current paragraph
2885 int pos = cursor.par->GetPositionOfInset(inset);
2887 CheckParagraph(cursor.par, pos);
2891 // check every paragraph
2893 LyXParagraph * par = FirstParagraph();
2895 // make sure the paragraph is open
2896 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2897 pos = par->GetPositionOfInset(inset);
2899 CheckParagraph(par, pos);
2910 void LyXText::SetCursor(LyXParagraph * par,
2911 LyXParagraph::size_type pos, bool setfont) const
2913 LyXCursor old_cursor = cursor;
2914 SetCursorIntern(par, pos, setfont);
2915 DeleteEmptyParagraphMechanism(old_cursor);
2919 void LyXText::SetCursorIntern(LyXParagraph * par,
2920 LyXParagraph::size_type pos, bool setfont) const
2924 LyXParagraph * tmppar;
2925 LyXParagraph::size_type vpos,cursor_vpos;
2927 // correct the cursor position if impossible
2928 if (pos > par->Last()){
2929 tmppar = par->ParFromPos(pos);
2930 pos = par->PositionInParFromPos(pos);
2933 if (par->IsDummy() && par->previous &&
2934 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2935 while (par->previous &&
2936 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2937 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2938 par = par->previous ;
2939 if (par->IsDummy() &&
2940 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2941 pos += par->text.size() + 1;
2943 if (par->previous) {
2944 par = par->previous;
2946 pos += par->text.size() + 1;
2954 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2955 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2956 && !cursor.par->IsSeparator(cursor.pos))
2957 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2959 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2960 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2962 current_font = cursor.par->GetFontSettings(cursor.pos);
2963 real_current_font = GetFont(cursor.par, cursor.pos);
2964 if (pos == 0 && par->size() == 0
2965 && current_view->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2966 current_font.setDirection(LyXFont::RTL_DIR);
2967 real_current_font.setDirection(LyXFont::RTL_DIR);
2971 /* get the cursor y position in text */
2972 row = GetRow(par, pos, y);
2973 /* y is now the beginning of the cursor row */
2975 /* y is now the cursor baseline */
2978 /* now get the cursors x position */
2980 float fill_separator, fill_hfill, fill_label_hfill;
2981 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2983 LyXParagraph::size_type last = RowLast(row);
2984 if (row->pos > last)
2986 else if (pos <= last ) {
2987 LyXDirection letter_direction =
2988 row->par->getLetterDirection(pos);
2989 LyXDirection font_direction =
2990 real_current_font.getFontDirection();
2991 if (letter_direction == font_direction || pos == 0)
2992 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
2993 ? log2vis(pos) : log2vis(pos)+1;
2995 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
2996 ? log2vis(pos-1)+1 : log2vis(pos-1);
2998 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
2999 ? log2vis(last)+1 : log2vis(last);
3001 /* table stuff -- begin*/
3002 if (row->par->table) {
3003 int cell = NumberOfCell(row->par, row->pos);
3005 x += row->par->table->GetBeginningOfTextInCell(cell);
3006 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3007 pos = vis2log(vpos);
3008 if (row->par->IsNewline(pos)) {
3009 x = x_old + row->par->table->WidthOfColumn(cell);
3012 x += row->par->table->GetBeginningOfTextInCell(cell);
3014 x += SingleWidth(row->par, pos);
3018 /* table stuff -- end*/
3019 LyXParagraph::size_type main_body =
3020 BeginningOfMainBody(row->par);
3021 if (main_body > 0 &&
3022 (main_body-1 > last ||
3023 !row->par->IsLineSeparator(main_body-1)))
3026 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3027 pos = vis2log(vpos);
3028 if (main_body > 0 && pos == main_body-1) {
3029 x += fill_label_hfill +
3030 GetFont(row->par, -2).stringWidth(
3031 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3032 if (row->par->IsLineSeparator(main_body-1))
3033 x -= SingleWidth(row->par, main_body-1);
3036 x += SingleWidth(row->par, pos);
3037 if (HfillExpansion(row, pos)) {
3038 if (pos >= main_body)
3041 x += fill_label_hfill;
3043 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3051 cursor.x_fix = cursor.x;
3056 void LyXText::SetCursorFromCoordinates(int x, long y) const
3058 LyXCursor old_cursor = cursor;
3060 /* get the row first */
3062 Row * row = GetRowNearY(y);
3064 cursor.par = row->par;
3066 int column = GetColumnNearX(row, x);
3067 cursor.pos = row->pos + column;
3069 cursor.y = y + row->baseline;
3074 (cursor.pos == cursor.par->Last()
3075 || cursor.par->IsSeparator(cursor.pos)
3076 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3077 && !cursor.par->IsSeparator(cursor.pos))
3078 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3080 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3081 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3083 current_font = cursor.par->GetFontSettings(cursor.pos);
3084 real_current_font = GetFont(cursor.par, cursor.pos);
3086 DeleteEmptyParagraphMechanism(old_cursor);
3090 void LyXText::CursorLeft() const
3093 if (cursor.par->table) {
3094 int cell = NumberOfCell(cursor.par, cursor.pos);
3095 if (cursor.par->table->IsContRow(cell) &&
3096 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3103 void LyXText::CursorLeftIntern() const
3105 if (cursor.pos > 0) {
3106 SetCursor(cursor.par, cursor.pos - 1);
3108 else if (cursor.par->Previous()) {
3109 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3114 void LyXText::CursorRight() const
3116 CursorRightIntern();
3117 if (cursor.par->table) {
3118 int cell = NumberOfCell(cursor.par, cursor.pos);
3119 if (cursor.par->table->IsContRow(cell) &&
3120 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3127 void LyXText::CursorRightIntern() const
3129 if (cursor.pos < cursor.par->Last()) {
3130 SetCursor(cursor.par, cursor.pos + 1);
3132 else if (cursor.par->Next()) {
3133 SetCursor(cursor.par->Next(), 0);
3138 void LyXText::CursorUp() const
3140 SetCursorFromCoordinates(cursor.x_fix,
3141 cursor.y - cursor.row->baseline - 1);
3142 if (cursor.par->table) {
3143 int cell = NumberOfCell(cursor.par, cursor.pos);
3144 if (cursor.par->table->IsContRow(cell) &&
3145 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3152 void LyXText::CursorDown() const
3154 if (cursor.par->table &&
3155 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3158 SetCursorFromCoordinates(cursor.x_fix,
3159 cursor.y - cursor.row->baseline
3160 + cursor.row->height + 1);
3161 if (cursor.par->table) {
3162 int cell = NumberOfCell(cursor.par, cursor.pos);
3163 int cell_above = cursor.par->table->GetCellAbove(cell);
3164 while(cursor.par->table &&
3165 cursor.par->table->IsContRow(cell) &&
3166 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3167 SetCursorFromCoordinates(cursor.x_fix,
3168 cursor.y - cursor.row->baseline
3169 + cursor.row->height + 1);
3170 if (cursor.par->table) {
3171 cell = NumberOfCell(cursor.par, cursor.pos);
3172 cell_above = cursor.par->table->GetCellAbove(cell);
3179 void LyXText::CursorUpParagraph() const
3181 if (cursor.pos > 0) {
3182 SetCursor(cursor.par, 0);
3184 else if (cursor.par->Previous()) {
3185 SetCursor(cursor.par->Previous(), 0);
3190 void LyXText::CursorDownParagraph() const
3192 if (cursor.par->Next()) {
3193 SetCursor(cursor.par->Next(), 0);
3195 SetCursor(cursor.par, cursor.par->Last());
3201 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3203 bool deleted = false;
3205 // this is the delete-empty-paragraph-mechanism.
3206 if (selection) return;
3208 #ifdef FIX_DOUBLE_SPACE
3209 /* Ok I'll put some comments here about what is missing.
3210 I have fixed BackSpace (and thus Delete) to not delete
3211 double-spaces automagically. I have also changed Cut,
3212 Copy and Paste to hopefully do some sensible things.
3213 There are still some small problems that can lead to
3214 double spaces stored in the document file or space at
3215 the beginning of paragraphs. This happens if you have
3216 the cursor betwenn to spaces and then save. Or if you
3217 cut and paste and the selection have a space at the
3218 beginning and then save right after the paste. I am
3219 sure none of these are very hard to fix, but I will
3220 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3221 that I can get some feedback. (Lgb)
3224 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3225 // delete the LineSeparator.
3228 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3229 // delete the LineSeparator.
3232 // If the pos around the old_cursor were spaces, delete one of them.
3233 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3234 && old_cursor.pos > 0
3235 && old_cursor.pos < old_cursor.par->Last()
3236 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3237 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3238 old_cursor.par->Erase(old_cursor.pos - 1);
3239 RedoParagraphs(old_cursor, old_cursor.par->Next());
3240 // or RedoDrawingOfParagraph(old_cursor);
3242 if (old_cursor.par == cursor.par &&
3243 cursor.pos > old_cursor.pos)
3244 SetCursor(cursor.par, cursor.pos - 1);
3246 SetCursor(cursor.par, cursor.pos);
3251 // Paragraph should not be deleted if empty
3252 if ((textclasslist.Style(parameters->textclass,
3253 old_cursor.par->GetLayout())).keepempty)
3256 LyXCursor tmpcursor;
3258 if (old_cursor.par != cursor.par) {
3259 if ( (old_cursor.par->Last() == 0
3260 || (old_cursor.par->Last() == 1
3261 && (old_cursor.par->IsLineSeparator(0))))
3262 && old_cursor.par->FirstPhysicalPar()
3263 == old_cursor.par->LastPhysicalPar()) {
3265 // ok, we will delete anything
3267 // make sure that you do not delete any environments
3268 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3269 !(old_cursor.row->previous
3270 && 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 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3275 ((old_cursor.row->previous
3276 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3278 (old_cursor.row->next
3279 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3281 status = LyXText::NEED_MORE_REFRESH;
3284 if (old_cursor.row->previous) {
3285 refresh_row = old_cursor.row->previous;
3286 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3288 cursor = old_cursor; // that undo can restore the right cursor position
3289 LyXParagraph * endpar = old_cursor.par->next;
3290 if (endpar && endpar->GetDepth()) {
3291 while (endpar && endpar->GetDepth()) {
3292 endpar = endpar->LastPhysicalPar()->Next();
3295 SetUndo(Undo::DELETE,
3296 old_cursor.par->previous,
3301 RemoveRow(old_cursor.row);
3302 if (params->paragraph == old_cursor.par) {
3303 params->paragraph = params->paragraph->next;
3306 delete old_cursor.par;
3308 /* Breakagain the next par. Needed
3309 * because of the parindent that
3310 * can occur or dissappear. The
3311 * next row can change its height,
3312 * if there is another layout before */
3313 if (refresh_row->next) {
3314 BreakAgain(refresh_row->next);
3315 UpdateCounters(refresh_row);
3317 SetHeightOfRow(refresh_row);
3319 refresh_row = old_cursor.row->next;
3320 refresh_y = old_cursor.y - old_cursor.row->baseline;
3323 cursor = old_cursor; // that undo can restore the right cursor position
3324 LyXParagraph *endpar = old_cursor.par->next;
3325 if (endpar && endpar->GetDepth()) {
3326 while (endpar && endpar->GetDepth()) {
3327 endpar = endpar->LastPhysicalPar()->Next();
3330 SetUndo(Undo::DELETE,
3331 old_cursor.par->previous,
3336 RemoveRow(old_cursor.row);
3338 if (params->paragraph == old_cursor.par) {
3339 params->paragraph = params->paragraph->next;
3341 delete old_cursor.par;
3343 /* Breakagain the next par. Needed
3344 because of the parindent that can
3345 occur or dissappear.
3346 The next row can change its height,
3347 if there is another layout before
3350 BreakAgain(refresh_row);
3351 UpdateCounters(refresh_row->previous);
3356 SetCursor(cursor.par, cursor.pos);
3358 /* if (cursor.y > old_cursor.y)
3359 cursor.y -= old_cursor.row->height; */
3361 if (sel_cursor.par == old_cursor.par
3362 && sel_cursor.pos == sel_cursor.pos) {
3363 // correct selection
3364 sel_cursor = cursor;
3369 if (old_cursor.par->ClearParagraph()){
3370 RedoParagraphs(old_cursor, old_cursor.par->Next());
3372 SetCursor(cursor.par, cursor.pos);
3373 sel_cursor = cursor;
3380 LyXParagraph * LyXText::GetParFromID(int id)
3382 LyXParagraph * result = FirstParagraph();
3383 while (result && result->id() != id)
3384 result = result->next;
3390 bool LyXText::TextUndo()
3392 // returns false if no undo possible
3393 Undo * undo = params->undostack.pop();
3398 .push(CreateUndo(undo->kind,
3399 GetParFromID(undo->number_of_before_par),
3400 GetParFromID(undo->number_of_behind_par)));
3402 return TextHandleUndo(undo);
3406 bool LyXText::TextRedo()
3408 // returns false if no redo possible
3409 Undo * undo = params->redostack.pop();
3414 .push(CreateUndo(undo->kind,
3415 GetParFromID(undo->number_of_before_par),
3416 GetParFromID(undo->number_of_behind_par)));
3418 return TextHandleUndo(undo);
3422 bool LyXText::TextHandleUndo(Undo * undo)
3424 // returns false if no undo possible
3425 bool result = false;
3427 LyXParagraph * before =
3428 GetParFromID(undo->number_of_before_par);
3429 LyXParagraph * behind =
3430 GetParFromID(undo->number_of_behind_par);
3431 LyXParagraph * tmppar;
3432 LyXParagraph * tmppar2;
3433 LyXParagraph * tmppar3;
3434 LyXParagraph * tmppar4;
3435 LyXParagraph * endpar;
3436 LyXParagraph * tmppar5;
3438 // if there's no before take the beginning
3439 // of the document for redoing
3441 SetCursorIntern(FirstParagraph(), 0);
3443 // replace the paragraphs with the undo informations
3445 tmppar3 = undo->par;
3446 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3449 while (tmppar4->next)
3450 tmppar4 = tmppar4->next;
3451 } // get last undo par
3453 // now remove the old text if there is any
3454 if (before != behind || (!behind && !before)){
3456 tmppar5 = before->next;
3458 tmppar5 = params->paragraph;
3460 while (tmppar5 && tmppar5 != behind){
3462 tmppar5 = tmppar5->next;
3463 // a memory optimization for edit: Only layout information
3464 // is stored in the undo. So restore the text informations.
3465 if (undo->kind == Undo::EDIT){
3466 tmppar2->text = tmppar->text;
3467 tmppar->text.clear();
3468 tmppar2 = tmppar2->next;
3470 if ( currentrow && currentrow->par == tmppar )
3471 currentrow = currentrow -> previous;
3472 // Commenting out this might remove the error
3473 // reported by Purify, but it might also
3474 // introduce a memory leak. We need to
3480 // put the new stuff in the list if there is one
3483 before->next = tmppar3;
3485 params->paragraph = tmppar3;
3486 tmppar3->previous = before;
3490 params->paragraph = behind;
3493 tmppar4->next = behind;
3495 behind->previous = tmppar4;
3499 // Set the cursor for redoing
3501 SetCursorIntern(before->FirstSelfrowPar(), 0);
3502 // check wether before points to a closed float and open it if necessary
3503 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3504 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3506 while (tmppar4->previous &&
3507 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3508 tmppar4 = tmppar4->previous;
3509 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3510 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3511 tmppar4 = tmppar4->next;
3516 // open a cosed footnote at the end if necessary
3517 if (behind && behind->previous &&
3518 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3519 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3520 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3521 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3522 behind = behind->next;
3526 // calculate the endpar for redoing the paragraphs.
3528 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3529 endpar = behind->LastPhysicalPar()->Next();
3531 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3536 tmppar = GetParFromID(undo->number_of_cursor_par);
3537 RedoParagraphs(cursor, endpar);
3539 SetCursorIntern(tmppar, undo->cursor_pos);
3540 UpdateCounters(cursor.row);
3550 void LyXText::FinishUndo()
3552 // makes sure the next operation will be stored
3553 undo_finished = True;
3557 void LyXText::FreezeUndo()
3559 // this is dangerous and for internal use only
3564 void LyXText::UnFreezeUndo()
3566 // this is dangerous and for internal use only
3567 undo_frozen = false;
3571 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3572 LyXParagraph const * behind) const
3575 params->undostack.push(CreateUndo(kind, before, behind));
3576 params->redostack.clear();
3580 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3581 LyXParagraph const * behind)
3583 params->redostack.push(CreateUndo(kind, before, behind));
3587 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3588 LyXParagraph const * behind) const
3590 int before_number = -1;
3591 int behind_number = -1;
3593 before_number = before->id();
3595 behind_number = behind->id();
3596 // Undo::EDIT and Undo::FINISH are
3597 // always finished. (no overlapping there)
3598 // overlapping only with insert and delete inside one paragraph:
3599 // Nobody wants all removed character
3600 // appear one by one when undoing.
3601 // EDIT is special since only layout information, not the
3602 // contents of a paragaph are stored.
3603 if (!undo_finished && kind != Undo::EDIT &&
3604 kind != Undo::FINISH){
3605 // check wether storing is needed
3606 if (!params->undostack.empty() &&
3607 params->undostack.top()->kind == kind &&
3608 params->undostack.top()->number_of_before_par == before_number &&
3609 params->undostack.top()->number_of_behind_par == behind_number ){
3614 // create a new Undo
3615 LyXParagraph * undopar;
3616 LyXParagraph * tmppar;
3617 LyXParagraph * tmppar2;
3619 LyXParagraph * start = 0;
3620 LyXParagraph * end = 0;
3623 start = before->next;
3625 start = FirstParagraph();
3627 end = behind->previous;
3629 end = FirstParagraph();
3635 && start != end->next
3636 && (before != behind || (!before && !behind))) {
3638 tmppar2 = tmppar->Clone();
3639 tmppar2->id(tmppar->id());
3641 // a memory optimization: Just store the layout information
3643 if (kind == Undo::EDIT){
3644 tmppar2->text.clear();
3649 while (tmppar != end && tmppar->next) {
3650 tmppar = tmppar->next;
3651 tmppar2->next = tmppar->Clone();
3652 tmppar2->next->id(tmppar->id());
3653 // a memory optimization: Just store the layout
3654 // information when only edit
3655 if (kind == Undo::EDIT){
3656 tmppar2->next->text.clear();
3658 tmppar2->next->previous = tmppar2;
3659 tmppar2 = tmppar2->next;
3663 undopar = 0; // nothing to replace (undo of delete maybe)
3665 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3666 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3668 Undo * undo = new Undo(kind,
3669 before_number, behind_number,
3670 cursor_par, cursor_pos,
3673 undo_finished = false;
3678 void LyXText::SetCursorParUndo()
3680 SetUndo(Undo::FINISH,
3681 cursor.par->ParFromPos(cursor.pos)->previous,
3682 cursor.par->ParFromPos(cursor.pos)->next);
3686 void LyXText::RemoveTableRow(LyXCursor * cur) const
3692 // move to the previous row
3693 int cell_act = NumberOfCell(cur->par, cur->pos);
3696 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3699 !cur->par->table->IsFirstCell(cell_act)) {
3701 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3706 // now we have to pay attention if the actual table is the
3707 // main row of TableContRows and if yes to delete all of them
3712 // delete up to the next row
3713 while (cur->pos < cur->par->Last() &&
3715 || !cur->par->table->IsFirstCell(cell_act))) {
3716 while (cur->pos < cur->par->Last() &&
3717 !cur->par->IsNewline(cur->pos))
3718 cur->par->Erase(cur->pos);
3721 if (cur->pos < cur->par->Last())
3722 cur->par->Erase(cur->pos);
3724 if (cur->pos && cur->pos == cur->par->Last()) {
3726 cur->par->Erase(cur->pos); // no newline at very end!
3728 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3729 !cur->par->table->IsContRow(cell_org) &&
3730 cur->par->table->IsContRow(cell));
3731 cur->par->table->DeleteRow(cell_org);
3736 bool LyXText::IsEmptyTableCell() const
3738 LyXParagraph::size_type pos = cursor.pos - 1;
3739 while (pos >= 0 && pos < cursor.par->Last()
3740 && !cursor.par->IsNewline(pos))
3742 return cursor.par->IsNewline(pos + 1);
3746 void LyXText::toggleAppendix(){
3747 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3748 bool start = !par->start_of_appendix;
3750 // ensure that we have only one start_of_appendix in this document
3751 LyXParagraph * tmp = FirstParagraph();
3752 for (; tmp; tmp = tmp->next)
3753 tmp->start_of_appendix = 0;
3754 par->start_of_appendix = start;
3756 // we can set the refreshing parameters now
3757 status = LyXText::NEED_MORE_REFRESH;
3759 refresh_row = 0; // not needed for full update
3761 SetCursor(cursor.par, cursor.pos);