1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include FORMS_H_LOCATION
18 #pragma implementation "lyxtext.h"
22 #include "lyxparagraph.h"
23 #include "insets/inseterror.h"
24 #include "insets/insetbib.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
39 #define FIX_DOUBLE_SPACE 1
43 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
51 parameters = &p->params;
55 status = LyXText::UNCHANGED;
56 LyXParagraph * par = p->paragraph;
57 current_font = GetFont(par, 0);
62 InsertParagraph(par, lastrow);
66 // set cursor at the very top position
67 selection = true; /* these setting is necessary
68 because of the delete-empty-
69 paragraph mechanism in
71 SetCursor(firstrow->par, 0);
76 // no rebreak necessary
82 // Default layouttype for copy environment type
89 // Delete all rows, this does not touch the paragraphs!
90 Row * tmprow = firstrow;
92 tmprow = firstrow->next;
99 void LyXText::owner(BufferView * bv)
101 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 // If position is -1, we get the layout font of the paragraph.
111 // If position is -2, we get the font of the manual label of the paragraph.
112 LyXFont LyXText::GetFont(LyXParagraph * par,
113 LyXParagraph::size_type pos) const
115 LyXLayout const & layout =
116 textclasslist.Style(parameters->textclass, par->GetLayout());
118 char par_depth = par->GetDepth();
119 // We specialize the 95% common case:
120 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
123 if (layout.labeltype == LABEL_MANUAL
124 && pos < BeginningOfMainBody(par)) {
126 return par->GetFontSettings(pos).
127 realize(layout.reslabelfont);
129 return par->GetFontSettings(pos).
130 realize(layout.resfont);
133 // process layoutfont for pos == -1 and labelfont for pos < -1
135 return layout.resfont;
137 return layout.reslabelfont;
141 // The uncommon case need not be optimized as much
143 LyXFont layoutfont, tmpfont;
147 if (pos < BeginningOfMainBody(par)) {
149 layoutfont = layout.labelfont;
152 layoutfont = layout.font;
154 tmpfont = par->GetFontSettings(pos);
155 tmpfont.realize(layoutfont);
158 // process layoutfont for pos == -1 and labelfont for pos < -1
160 tmpfont = layout.font;
162 tmpfont = layout.labelfont;
165 // Resolve against environment font information
166 while (par && par_depth && !tmpfont.resolved()) {
167 par = par->DepthHook(par_depth - 1);
169 tmpfont.realize(textclasslist.
170 Style(parameters->textclass,
171 par->GetLayout()).font);
172 par_depth = par->GetDepth();
176 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
178 // Cosmetic improvement: If this is an open footnote, make the font
180 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
181 && par->footnotekind == LyXParagraph::FOOTNOTE) {
189 void LyXText::SetCharFont(LyXParagraph * par,
190 LyXParagraph::size_type pos,
194 // Let the insets convert their font
195 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
196 if (par->GetInset(pos))
197 font = par->GetInset(pos)->ConvertFont(font);
200 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
203 // Get concrete layout font to reduce against
206 if (pos < BeginningOfMainBody(par))
207 layoutfont = layout.labelfont;
209 layoutfont = layout.font;
211 // Realize against environment font information
212 if (par->GetDepth()){
213 LyXParagraph * tp = par;
214 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
215 tp = tp->DepthHook(tp->GetDepth()-1);
217 layoutfont.realize(textclasslist.
218 Style(parameters->textclass,
219 tp->GetLayout()).font);
223 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
225 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
226 && par->footnotekind == LyXParagraph::FOOTNOTE) {
227 layoutfont.decSize();
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 par->SetFont(pos, font);
237 /* inserts a new row behind the specified row, increments
238 * the touched counters */
239 void LyXText::InsertRow(Row * row, LyXParagraph * par,
240 LyXParagraph::size_type pos) const
242 Row * tmprow = new Row;
244 tmprow->previous = 0;
245 tmprow->next = firstrow;
248 tmprow->previous = row;
249 tmprow->next = row->next;
254 tmprow->next->previous = tmprow;
256 if (tmprow->previous)
257 tmprow->previous->next = tmprow;
265 ++number_of_rows; // one more row
269 // removes the row and reset the touched counters
270 void LyXText::RemoveRow(Row * row) const
272 /* this must not happen before the currentrow for clear reasons.
273 so the trick is just to set the current row onto the previous
276 GetRow(row->par, row->pos, unused_y);
277 currentrow = currentrow->previous;
279 currentrow_y -= currentrow->height;
284 row->next->previous = row->previous;
285 if (!row->previous) {
286 firstrow = row->next;
288 row->previous->next = row->next;
291 lastrow = row->previous;
293 height -= row->height; // the text becomes smaller
296 --number_of_rows; // one row less
300 // remove all following rows of the paragraph of the specified row.
301 void LyXText::RemoveParagraph(Row * row) const
303 LyXParagraph * tmppar = row->par;
307 while (row && row->par == tmppar) {
315 // insert the specified paragraph behind the specified row
316 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
318 InsertRow(row, par, 0); /* insert a new row, starting
321 SetCounter(par); // set the counters
323 // and now append the whole paragraph behind the new row
325 firstrow->height = 0;
326 AppendParagraph(firstrow);
328 row->next->height = 0;
329 AppendParagraph(row->next);
334 void LyXText::ToggleFootnote()
336 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
338 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
340 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
342 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
348 void LyXText::OpenStuff()
350 if (cursor.pos == 0 && cursor.par->bibkey){
351 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
353 else if (cursor.pos < cursor.par->Last()
354 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
355 && cursor.par->GetInset(cursor.pos)->Editable()) {
356 owner_->owner()->getMiniBuffer()
357 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
358 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
360 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
367 void LyXText::CloseFootnote()
369 LyXParagraph * tmppar;
370 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
372 // if the cursor is not in an open footnote, or
373 // there is no open footnote in this paragraph, just return.
374 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
377 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
378 owner_->owner()->getMiniBuffer()
379 ->Set(_("Nothing to do"));
383 // ok, move the cursor right before the footnote
384 // just a little faster than using CursorRight()
386 cursor.par->ParFromPos(cursor.pos) != par;
390 // now the cursor is at the beginning of the physical par
391 SetCursor(cursor.par,
393 cursor.par->ParFromPos(cursor.pos)->text.size());
395 /* we are in a footnote, so let us move at the beginning */
396 /* this is just faster than using just CursorLeft() */
399 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
400 // just a little bit faster than movin the cursor
401 tmppar = tmppar->Previous();
403 SetCursor(tmppar, tmppar->Last());
406 // the cursor must be exactly before the footnote
407 par = cursor.par->ParFromPos(cursor.pos);
409 status = LyXText::NEED_MORE_REFRESH;
410 refresh_row = cursor.row;
411 refresh_y = cursor.y - cursor.row->baseline;
414 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
415 Row * row = cursor.row;
417 tmppar->CloseFootnote(cursor.pos);
419 while (tmppar != endpar) {
420 RemoveRow(row->next);
422 tmppar = row->next->par;
427 AppendParagraph(cursor.row);
429 SetCursor(cursor.par, cursor.pos);
433 if (cursor.row->next)
434 SetHeightOfRow(cursor.row->next);
438 /* used in setlayout */
439 // Asger is not sure we want to do this...
440 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
442 LyXFont layoutfont, tmpfont;
444 LyXLayout const & layout =
445 textclasslist.Style(parameters->textclass, par->GetLayout());
447 for (LyXParagraph::size_type pos = 0;
448 pos < par->Last(); ++pos) {
449 if (pos < BeginningOfMainBody(par))
450 layoutfont = layout.labelfont;
452 layoutfont = layout.font;
454 tmpfont = par->GetFontSettings(pos);
455 tmpfont.reduce(layoutfont);
456 par->SetFont(pos, tmpfont);
461 // set layout over selection and make a total rebreak of those paragraphs
462 void LyXText::SetLayout(char layout)
466 // if there is no selection just set the layout
467 // of the current paragraph */
469 sel_start_cursor = cursor; // dummy selection
470 sel_end_cursor = cursor;
473 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
474 LyXParagraph * undoendpar = endpar;
476 if (endpar && endpar->GetDepth()) {
477 while (endpar && endpar->GetDepth()) {
478 endpar = endpar->LastPhysicalPar()->Next();
483 endpar = endpar->Next(); // because of parindents etc.
487 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
490 tmpcursor = cursor; /* store the current cursor */
492 /* ok we have a selection. This is always between sel_start_cursor
493 * and sel_end cursor */
494 cursor = sel_start_cursor;
496 LyXLayout const & lyxlayout =
497 textclasslist.Style(parameters->textclass, layout);
499 while (cursor.par != sel_end_cursor.par) {
500 if (cursor.par->footnoteflag ==
501 sel_start_cursor.par->footnoteflag) {
502 cursor.par->SetLayout(layout);
503 MakeFontEntriesLayoutSpecific(cursor.par);
504 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
505 fppar->added_space_top = lyxlayout.fill_top ?
506 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
507 fppar->added_space_bottom = lyxlayout.fill_bottom ?
508 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
509 if (lyxlayout.margintype == MARGIN_MANUAL)
510 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
511 if (lyxlayout.labeltype != LABEL_BIBLIO
513 delete fppar->bibkey;
517 cursor.par = cursor.par->Next();
519 if (cursor.par->footnoteflag ==
520 sel_start_cursor.par->footnoteflag) {
521 cursor.par->SetLayout(layout);
522 MakeFontEntriesLayoutSpecific(cursor.par);
523 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
524 fppar->added_space_top = lyxlayout.fill_top ?
525 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
526 fppar->added_space_bottom = lyxlayout.fill_bottom ?
527 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
528 if (lyxlayout.margintype == MARGIN_MANUAL)
529 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
530 if (lyxlayout.labeltype != LABEL_BIBLIO
532 delete fppar->bibkey;
537 RedoParagraphs(sel_start_cursor, endpar);
539 // we have to reset the selection, because the
540 // geometry could have changed */
541 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
543 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
544 UpdateCounters(cursor.row);
547 SetCursor(tmpcursor.par, tmpcursor.pos);
551 // increment depth over selection and
552 // make a total rebreak of those paragraphs
553 void LyXText::IncDepth()
555 // If there is no selection, just use the current paragraph
557 sel_start_cursor = cursor; // dummy selection
558 sel_end_cursor = cursor;
561 // We end at the next paragraph with depth 0
562 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
563 LyXParagraph * undoendpar = endpar;
565 if (endpar && endpar->GetDepth()) {
566 while (endpar && endpar->GetDepth()) {
567 endpar = endpar->LastPhysicalPar()->Next();
572 endpar = endpar->Next(); // because of parindents etc.
577 .par->ParFromPos(sel_start_cursor.pos)->previous,
580 LyXCursor tmpcursor = cursor; // store the current cursor
582 // ok we have a selection. This is always between sel_start_cursor
583 // and sel_end cursor
584 cursor = sel_start_cursor;
586 bool anything_changed = false;
589 // NOTE: you can't change the depth of a bibliography entry
590 if (cursor.par->footnoteflag ==
591 sel_start_cursor.par->footnoteflag
592 && textclasslist.Style(parameters->textclass,
593 cursor.par->GetLayout()
594 ).labeltype != LABEL_BIBLIO) {
595 LyXParagraph * prev =
596 cursor.par->FirstPhysicalPar()->Previous();
598 && (prev->GetDepth() - cursor.par->GetDepth() > 0
599 || (prev->GetDepth() == cursor.par->GetDepth()
600 && textclasslist.Style(parameters->textclass,
601 prev->GetLayout()).isEnvironment()))) {
602 cursor.par->FirstPhysicalPar()->depth++;
603 anything_changed = true;
606 if (cursor.par == sel_end_cursor.par)
608 cursor.par = cursor.par->Next();
611 // if nothing changed set all depth to 0
612 if (!anything_changed) {
613 cursor = sel_start_cursor;
614 while (cursor.par != sel_end_cursor.par) {
615 cursor.par->FirstPhysicalPar()->depth = 0;
616 cursor.par = cursor.par->Next();
618 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
619 cursor.par->FirstPhysicalPar()->depth = 0;
622 RedoParagraphs(sel_start_cursor, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
628 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
629 UpdateCounters(cursor.row);
632 SetCursor(tmpcursor.par, tmpcursor.pos);
636 // decrement depth over selection and
637 // make a total rebreak of those paragraphs
638 void LyXText::DecDepth()
640 // if there is no selection just set the layout
641 // of the current paragraph
643 sel_start_cursor = cursor; // dummy selection
644 sel_end_cursor = cursor;
647 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
648 LyXParagraph * undoendpar = endpar;
650 if (endpar && endpar->GetDepth()) {
651 while (endpar && endpar->GetDepth()) {
652 endpar = endpar->LastPhysicalPar()->Next();
657 endpar = endpar->Next(); // because of parindents etc.
662 .par->ParFromPos(sel_start_cursor.pos)->previous,
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
669 cursor = sel_start_cursor;
672 if (cursor.par->footnoteflag ==
673 sel_start_cursor.par->footnoteflag) {
674 if (cursor.par->FirstPhysicalPar()->depth)
675 cursor.par->FirstPhysicalPar()->depth--;
677 if (cursor.par == sel_end_cursor.par)
679 cursor.par = cursor.par->Next();
682 RedoParagraphs(sel_start_cursor, endpar);
684 // we have to reset the selection, because the
685 // geometry could have changed
686 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
688 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
689 UpdateCounters(cursor.row);
692 SetCursor(tmpcursor.par, tmpcursor.pos);
696 // set font over selection and make a total rebreak of those paragraphs
697 void LyXText::SetFont(LyXFont const & font, bool toggleall)
699 // if there is no selection just set the current_font
701 // Determine basis font
703 if (cursor.pos < BeginningOfMainBody(cursor.par))
704 layoutfont = GetFont(cursor.par, -2);
706 layoutfont = GetFont(cursor.par, -1);
707 // Update current font
708 real_current_font.update(font, toggleall);
710 // Reduce to implicit settings
711 current_font = real_current_font;
712 current_font.reduce(layoutfont);
713 // And resolve it completely
714 real_current_font.realize(layoutfont);
718 LyXCursor tmpcursor = cursor; // store the current cursor
720 // ok we have a selection. This is always between sel_start_cursor
721 // and sel_end cursor
724 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
725 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
726 cursor = sel_start_cursor;
727 while (cursor.par != sel_end_cursor.par ||
728 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
729 && cursor.pos < sel_end_cursor.pos))
731 if (cursor.pos < cursor.par->Last()
732 && cursor.par->footnoteflag
733 == sel_start_cursor.par->footnoteflag) {
734 // an open footnote should behave
736 LyXFont newfont = GetFont(cursor.par, cursor.pos);
737 newfont.update(font, toggleall);
738 SetCharFont(cursor.par, cursor.pos, newfont);
742 cursor.par = cursor.par->Next();
746 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
748 // we have to reset the selection, because the
749 // geometry could have changed
750 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
752 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
755 SetCursor(tmpcursor.par, tmpcursor.pos);
759 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
761 Row * tmprow = cur.row;
762 long y = cur.y - tmprow->baseline;
764 SetHeightOfRow(tmprow);
765 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
766 // find the first row of the paragraph
767 if (first_phys_par != tmprow->par)
768 while (tmprow->previous
769 && tmprow->previous->par != first_phys_par) {
770 tmprow = tmprow->previous;
772 SetHeightOfRow(tmprow);
774 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
775 tmprow = tmprow->previous;
777 SetHeightOfRow(tmprow);
780 // we can set the refreshing parameters now
781 status = LyXText::NEED_MORE_REFRESH;
783 refresh_row = tmprow;
784 SetCursor(cur.par, cur.pos);
788 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
790 Row * tmprow = cur.row;
792 long y = cur.y - tmprow->baseline;
793 SetHeightOfRow(tmprow);
794 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
795 // find the first row of the paragraph
796 if (first_phys_par != tmprow->par)
797 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
798 tmprow = tmprow->previous;
801 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
802 tmprow = tmprow->previous;
806 // we can set the refreshing parameters now
807 if (status == LyXText::UNCHANGED || y < refresh_y) {
809 refresh_row = tmprow;
811 status = LyXText::NEED_MORE_REFRESH;
812 SetCursor(cur.par, cur.pos);
816 /* deletes and inserts again all paragaphs between the cursor
817 * and the specified par
818 * This function is needed after SetLayout and SetFont etc. */
819 void LyXText::RedoParagraphs(LyXCursor const & cur,
820 LyXParagraph const * endpar) const
823 LyXParagraph * tmppar, * first_phys_par;
825 Row * tmprow = cur.row;
827 long y = cur.y - tmprow->baseline;
829 if (!tmprow->previous){
830 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
832 first_phys_par = tmprow->par->FirstPhysicalPar();
833 // find the first row of the paragraph
834 if (first_phys_par != tmprow->par)
835 while (tmprow->previous
836 && tmprow->previous->par != first_phys_par) {
837 tmprow = tmprow->previous;
840 while (tmprow->previous
841 && tmprow->previous->par == first_phys_par) {
842 tmprow = tmprow->previous;
847 // we can set the refreshing parameters now
848 status = LyXText::NEED_MORE_REFRESH;
850 refresh_row = tmprow->previous; /* the real refresh row will
851 be deleted, so I store
855 tmppar = tmprow->next->par;
858 while (tmppar != endpar) {
859 RemoveRow(tmprow->next);
861 tmppar = tmprow->next->par;
866 // remove the first one
867 tmprow2 = tmprow; /* this is because tmprow->previous
869 tmprow = tmprow->previous;
872 tmppar = first_phys_par;
876 InsertParagraph(tmppar, tmprow);
879 while (tmprow->next && tmprow->next->par == tmppar)
880 tmprow = tmprow->next;
881 tmppar = tmppar->Next();
883 } while (tmppar != endpar);
885 // this is because of layout changes
887 refresh_y -= refresh_row->height;
888 SetHeightOfRow(refresh_row);
890 refresh_row = firstrow;
892 SetHeightOfRow(refresh_row);
895 if (tmprow && tmprow->next)
896 SetHeightOfRow(tmprow->next);
900 int LyXText::FullRebreak()
902 if (need_break_row) {
903 BreakAgain(need_break_row);
911 /* important for the screen */
914 /* the cursor set functions have a special mechanism. When they
915 * realize, that you left an empty paragraph, they will delete it.
916 * They also delet the corresponding row */
918 // need the selection cursor:
919 void LyXText::SetSelection()
922 last_sel_cursor = sel_cursor;
923 sel_start_cursor = sel_cursor;
924 sel_end_cursor = sel_cursor;
929 // first the toggling area
930 if (cursor.y < last_sel_cursor.y ||
931 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
932 toggle_end_cursor = last_sel_cursor;
933 toggle_cursor = cursor;
936 toggle_end_cursor = cursor;
937 toggle_cursor = last_sel_cursor;
940 last_sel_cursor = cursor;
942 // and now the whole selection
944 if (sel_cursor.par == cursor.par)
945 if (sel_cursor.pos < cursor.pos) {
946 sel_end_cursor = cursor;
947 sel_start_cursor = sel_cursor;
949 sel_end_cursor = sel_cursor;
950 sel_start_cursor = cursor;
952 else if (sel_cursor.y < cursor.y ||
953 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
954 sel_end_cursor = cursor;
955 sel_start_cursor = sel_cursor;
958 sel_end_cursor = sel_cursor;
959 sel_start_cursor = cursor;
962 // a selection with no contents is not a selection
963 if (sel_start_cursor.x == sel_end_cursor.x &&
964 sel_start_cursor.y == sel_end_cursor.y)
969 void LyXText::ClearSelection() const
976 void LyXText::CursorHome() const
978 SetCursor(cursor.par, cursor.row->pos);
982 void LyXText::CursorEnd() const
984 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
985 SetCursor(cursor.par, RowLast(cursor.row) + 1);
987 if (cursor.par->Last() &&
988 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
989 || cursor.par->IsNewline(RowLast(cursor.row))))
990 SetCursor(cursor.par, RowLast(cursor.row));
992 SetCursor(cursor.par, RowLast(cursor.row) + 1);
994 if (cursor.par->table) {
995 int cell = NumberOfCell(cursor.par, cursor.pos);
996 if (cursor.par->table->RowHasContRow(cell) &&
997 cursor.par->table->CellHasContRow(cell)<0) {
998 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
999 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1001 if (cursor.par->Last() &&
1002 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1003 || cursor.par->IsNewline(RowLast(cursor.row))))
1004 SetCursor(cursor.par, RowLast(cursor.row));
1006 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1013 void LyXText::CursorTop() const
1015 while (cursor.par->Previous())
1016 cursor.par = cursor.par->Previous();
1017 SetCursor(cursor.par, 0);
1021 void LyXText::CursorBottom() const
1023 while (cursor.par->Next())
1024 cursor.par = cursor.par->Next();
1025 SetCursor(cursor.par, cursor.par->Last());
1029 /* returns a pointer to the row near the specified y-coordinate
1030 * (relative to the whole text). y is set to the real beginning
1032 Row * LyXText::GetRowNearY(long & y) const
1038 tmprow = currentrow;
1039 tmpy = currentrow_y;
1046 while (tmprow->next && tmpy + tmprow->height <= y) {
1047 tmpy += tmprow->height;
1048 tmprow = tmprow->next;
1051 while (tmprow->previous && tmpy > y) {
1052 tmprow = tmprow->previous;
1053 tmpy -= tmprow->height;
1056 currentrow = tmprow;
1057 currentrow_y = tmpy;
1059 y = tmpy; // return the real y
1064 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1066 // If the mask is completely neutral, tell user
1067 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1068 // Could only happen with user style
1069 owner_->owner()->getMiniBuffer()
1070 ->Set(_("No font change defined. Use Character under"
1071 " the Layout menu to define font change."));
1075 // Try implicit word selection
1076 LyXCursor resetCursor = cursor;
1077 int implicitSelection = SelectWordWhenUnderCursor();
1080 SetFont(font, toggleall);
1082 /* Implicit selections are cleared afterwards and cursor is set to the
1083 original position. */
1084 if (implicitSelection) {
1086 cursor = resetCursor;
1087 SetCursor( cursor.par, cursor.pos );
1088 sel_cursor = cursor;
1093 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1095 if (textclasslist.Style(parameters->textclass,
1096 par->GetLayout()).labeltype != LABEL_MANUAL)
1099 return par->BeginningOfMainBody();
1103 /* if there is a selection, reset every environment you can find
1104 * in the selection, otherwise just the environment you are in */
1105 void LyXText::MeltFootnoteEnvironment()
1107 LyXParagraph * tmppar, * firsttmppar;
1111 /* is is only allowed, if the cursor is IN an open footnote.
1112 * Otherwise it is too dangerous */
1113 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1116 SetUndo(Undo::FINISH,
1117 cursor.par->PreviousBeforeFootnote()->previous,
1118 cursor.par->NextAfterFootnote()->next);
1120 /* ok, move to the beginning of the footnote. */
1121 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1122 cursor.par = cursor.par->Previous();
1124 SetCursor(cursor.par, cursor.par->Last());
1125 /* this is just faster than using CursorLeft(); */
1127 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1128 tmppar = firsttmppar;
1129 /* tmppar is now the paragraph right before the footnote */
1131 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1134 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1135 tmppar = tmppar->next; /* I use next instead of Next(),
1136 * because there cannot be any
1137 * footnotes in a footnote
1139 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1141 /* remember the captions and empty paragraphs */
1142 if ((textclasslist.Style(parameters->textclass,
1143 tmppar->GetLayout())
1144 .labeltype == LABEL_SENSITIVE)
1146 tmppar->SetLayout(0);
1149 // now we will paste the ex-footnote, if the layouts allow it
1150 // first restore the layout of the paragraph right behind
1153 tmppar->next->MakeSameLayout(cursor.par);
1156 if ((!tmppar->GetLayout() && !tmppar->table)
1158 && (!tmppar->Next()->Last()
1159 || tmppar->Next()->HasSameLayout(tmppar)))) {
1160 if (tmppar->Next()->Last()
1161 && tmppar->Next()->IsLineSeparator(0))
1162 tmppar->Next()->Erase(0);
1163 tmppar->PasteParagraph();
1166 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1167 * by the pasting of the beginning */
1169 /* then the beginning */
1170 /* if there is no space between the text and the footnote, so we insert
1172 * (only if the previous par and the footnotepar are not empty!) */
1173 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1174 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1175 if (firsttmppar->text.size()
1176 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1177 && first_footnote_par_is_not_empty) {
1178 firsttmppar->next->InsertChar(0, ' ');
1180 firsttmppar->PasteParagraph();
1183 /* now redo the paragaphs */
1184 RedoParagraphs(cursor, tmppar);
1186 SetCursor(cursor.par, cursor.pos);
1188 /* sometimes it can happen, that there is a counter change */
1189 Row * row = cursor.row;
1190 while (row->next && row->par != tmppar && row->next->par != tmppar)
1192 UpdateCounters(row);
1199 /* the DTP switches for paragraphs. LyX will store them in the
1200 * first physicla paragraph. When a paragraph is broken, the top settings
1201 * rest, the bottom settings are given to the new one. So I can make shure,
1202 * they do not duplicate themself and you cannnot make dirty things with
1205 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1206 bool pagebreak_top, bool pagebreak_bottom,
1207 VSpace const & space_top,
1208 VSpace const & space_bottom,
1210 string labelwidthstring,
1213 LyXCursor tmpcursor = cursor;
1215 sel_start_cursor = cursor;
1216 sel_end_cursor = cursor;
1219 // make sure that the depth behind the selection are restored, too
1220 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1221 LyXParagraph * undoendpar = endpar;
1223 if (endpar && endpar->GetDepth()) {
1224 while (endpar && endpar->GetDepth()) {
1225 endpar = endpar->LastPhysicalPar()->Next();
1226 undoendpar = endpar;
1230 endpar = endpar->Next(); // because of parindents etc.
1235 .par->ParFromPos(sel_start_cursor.pos)->previous,
1239 LyXParagraph * tmppar = sel_end_cursor.par;
1240 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1241 SetCursor(tmppar->FirstPhysicalPar(), 0);
1242 status = LyXText::NEED_MORE_REFRESH;
1243 refresh_row = cursor.row;
1244 refresh_y = cursor.y - cursor.row->baseline;
1245 if (cursor.par->footnoteflag ==
1246 sel_start_cursor.par->footnoteflag) {
1247 cursor.par->line_top = line_top;
1248 cursor.par->line_bottom = line_bottom;
1249 cursor.par->pagebreak_top = pagebreak_top;
1250 cursor.par->pagebreak_bottom = pagebreak_bottom;
1251 cursor.par->added_space_top = space_top;
1252 cursor.par->added_space_bottom = space_bottom;
1253 // does the layout allow the new alignment?
1254 if (align == LYX_ALIGN_LAYOUT)
1255 align = textclasslist
1256 .Style(parameters->textclass,
1257 cursor.par->GetLayout()).align;
1258 if (align & textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).alignpossible) {
1261 if (align == textclasslist
1262 .Style(parameters->textclass,
1263 cursor.par->GetLayout()).align)
1264 cursor.par->align = LYX_ALIGN_LAYOUT;
1266 cursor.par->align = align;
1268 cursor.par->SetLabelWidthString(labelwidthstring);
1269 cursor.par->noindent = noindent;
1272 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1275 RedoParagraphs(sel_start_cursor, endpar);
1278 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1279 sel_cursor = cursor;
1280 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1282 SetCursor(tmpcursor.par, tmpcursor.pos);
1286 void LyXText::SetParagraphExtraOpt(int type,
1288 char const * widthp,
1289 int alignment, bool hfill,
1290 bool start_minipage)
1292 LyXCursor tmpcursor = cursor;
1293 LyXParagraph * tmppar;
1295 sel_start_cursor = cursor;
1296 sel_end_cursor = cursor;
1299 // make sure that the depth behind the selection are restored, too
1300 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1301 LyXParagraph * undoendpar = endpar;
1303 if (endpar && endpar->GetDepth()) {
1304 while (endpar && endpar->GetDepth()) {
1305 endpar = endpar->LastPhysicalPar()->Next();
1306 undoendpar = endpar;
1310 endpar = endpar->Next(); // because of parindents etc.
1315 .par->ParFromPos(sel_start_cursor.pos)->previous,
1318 tmppar = sel_end_cursor.par;
1319 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1320 SetCursor(tmppar->FirstPhysicalPar(), 0);
1321 status = LyXText::NEED_MORE_REFRESH;
1322 refresh_row = cursor.row;
1323 refresh_y = cursor.y - cursor.row->baseline;
1324 if (cursor.par->footnoteflag ==
1325 sel_start_cursor.par->footnoteflag) {
1326 if (type == LyXParagraph::PEXTRA_NONE) {
1327 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1328 cursor.par->UnsetPExtraType();
1329 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1332 cursor.par->SetPExtraType(type, width, widthp);
1333 cursor.par->pextra_hfill = hfill;
1334 cursor.par->pextra_start_minipage = start_minipage;
1335 cursor.par->pextra_alignment = alignment;
1338 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1340 RedoParagraphs(sel_start_cursor, endpar);
1342 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1343 sel_cursor = cursor;
1344 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1346 SetCursor(tmpcursor.par, tmpcursor.pos);
1350 static char const * alphaCounter(int n) {
1351 static char result[2];
1356 result[0] = 'A' + n;
1364 // set the counter of a paragraph. This includes the labels
1365 void LyXText::SetCounter(LyXParagraph * par) const
1367 // this is only relevant for the beginning of paragraph
1368 par = par->FirstPhysicalPar();
1370 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1373 LyXTextClass const & textclass =
1374 textclasslist.TextClass(parameters->textclass);
1376 /* copy the prev-counters to this one, unless this is the start of a
1377 footnote or of a bibliography or the very first paragraph */
1379 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1380 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1381 && par->footnotekind == LyXParagraph::FOOTNOTE)
1382 && !(textclasslist.Style(parameters->textclass,
1383 par->Previous()->GetLayout()
1384 ).labeltype != LABEL_BIBLIO
1385 && layout.labeltype == LABEL_BIBLIO)) {
1386 for (int i = 0; i < 10; ++i) {
1387 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1389 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1390 if (!par->appendix && par->start_of_appendix){
1391 par->appendix = true;
1392 for (int i = 0; i < 10; ++i) {
1393 par->setCounter(i, 0);
1396 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1397 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1400 for (int i = 0; i < 10; ++i) {
1401 par->setCounter(i, 0);
1403 par->appendix = par->start_of_appendix;
1408 // if this is an open marginnote and this is the first
1409 // entry in the marginnote and the enclosing
1410 // environment is an enum/item then correct for the
1411 // LaTeX behaviour (ARRae)
1412 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1413 && par->footnotekind == LyXParagraph::MARGIN
1415 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1416 && (par->PreviousBeforeFootnote()
1417 && textclasslist.Style(parameters->textclass,
1418 par->PreviousBeforeFootnote()->GetLayout()
1419 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1420 // Any itemize or enumerate environment in a marginnote
1421 // that is embedded in an itemize or enumerate
1422 // paragraph is seen by LaTeX as being at a deeper
1423 // level within that enclosing itemization/enumeration
1424 // even if there is a "standard" layout at the start of
1430 /* Maybe we have to increment the enumeration depth.
1431 * BUT, enumeration in a footnote is considered in isolation from its
1432 * surrounding paragraph so don't increment if this is the
1433 * first line of the footnote
1434 * AND, bibliographies can't have their depth changed ie. they
1435 * are always of depth 0
1438 && par->Previous()->GetDepth() < par->GetDepth()
1439 && textclasslist.Style(parameters->textclass,
1440 par->Previous()->GetLayout()
1441 ).labeltype == LABEL_COUNTER_ENUMI
1442 && par->enumdepth < 3
1443 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1444 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1445 && par->footnotekind == LyXParagraph::FOOTNOTE)
1446 && layout.labeltype != LABEL_BIBLIO) {
1450 /* Maybe we have to decrement the enumeration depth, see note above */
1452 && par->Previous()->GetDepth() > par->GetDepth()
1453 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1454 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1455 && par->footnotekind == LyXParagraph::FOOTNOTE)
1456 && layout.labeltype != LABEL_BIBLIO) {
1457 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1458 par->setCounter(6 + par->enumdepth,
1459 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1460 /* reset the counters.
1461 * A depth change is like a breaking layout
1463 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1464 par->setCounter(i, 0);
1467 if (!par->labelstring.empty()) {
1468 par->labelstring.clear();
1471 if (layout.margintype == MARGIN_MANUAL) {
1472 if (par->labelwidthstring.empty()) {
1473 par->SetLabelWidthString(layout.labelstring());
1477 par->SetLabelWidthString(string());
1480 /* is it a layout that has an automatic label ? */
1481 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1483 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1484 if (i >= 0 && i<= parameters->secnumdepth) {
1485 par->incCounter(i); // increment the counter
1487 char * s = new char[50];
1489 // Is there a label? Useful for Chapter layout
1490 if (!par->appendix){
1491 if (!layout.labelstring().empty())
1492 par->labelstring = layout.labelstring();
1494 par->labelstring.clear();
1496 if (!layout.labelstring_appendix().empty())
1497 par->labelstring = layout.labelstring_appendix();
1499 par->labelstring.clear();
1502 if (!par->appendix){
1503 switch (2 * LABEL_FIRST_COUNTER -
1504 textclass.maxcounter() + i) {
1505 case LABEL_COUNTER_CHAPTER:
1507 par->getCounter(i));
1509 case LABEL_COUNTER_SECTION:
1511 par->getCounter(i - 1),
1512 par->getCounter(i));
1514 case LABEL_COUNTER_SUBSECTION:
1515 sprintf(s, "%d.%d.%d",
1516 par->getCounter(i-2),
1517 par->getCounter(i-1),
1518 par->getCounter(i));
1520 case LABEL_COUNTER_SUBSUBSECTION:
1521 sprintf(s, "%d.%d.%d.%d",
1522 par->getCounter(i-3),
1523 par->getCounter(i-2),
1524 par->getCounter(i-1),
1525 par->getCounter(i));
1527 case LABEL_COUNTER_PARAGRAPH:
1528 sprintf(s, "%d.%d.%d.%d.%d",
1529 par->getCounter(i-4),
1530 par->getCounter(i-3),
1531 par->getCounter(i-2),
1532 par->getCounter(i-1),
1533 par->getCounter(i));
1535 case LABEL_COUNTER_SUBPARAGRAPH:
1536 sprintf(s, "%d.%d.%d.%d.%d.%d",
1537 par->getCounter(i-5),
1538 par->getCounter(i-4),
1539 par->getCounter(i-3),
1540 par->getCounter(i-2),
1541 par->getCounter(i-1),
1542 par->getCounter(i));
1545 sprintf(s, "%d.", par->getCounter(i));
1549 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1550 case LABEL_COUNTER_CHAPTER:
1552 alphaCounter(par->getCounter(i)));
1554 case LABEL_COUNTER_SECTION:
1556 alphaCounter(par->getCounter(i - 1)),
1557 par->getCounter(i));
1559 case LABEL_COUNTER_SUBSECTION:
1560 sprintf(s, "%s.%d.%d",
1561 alphaCounter(par->getCounter(i-2)),
1562 par->getCounter(i-1),
1563 par->getCounter(i));
1565 case LABEL_COUNTER_SUBSUBSECTION:
1566 sprintf(s, "%s.%d.%d.%d",
1567 alphaCounter(par->getCounter(i-3)),
1568 par->getCounter(i-2),
1569 par->getCounter(i-1),
1570 par->getCounter(i));
1572 case LABEL_COUNTER_PARAGRAPH:
1573 sprintf(s, "%s.%d.%d.%d.%d",
1574 alphaCounter(par->getCounter(i-4)),
1575 par->getCounter(i-3),
1576 par->getCounter(i-2),
1577 par->getCounter(i-1),
1578 par->getCounter(i));
1580 case LABEL_COUNTER_SUBPARAGRAPH:
1581 sprintf(s, "%s.%d.%d.%d.%d.%d",
1582 alphaCounter(par->getCounter(i-5)),
1583 par->getCounter(i-4),
1584 par->getCounter(i-3),
1585 par->getCounter(i-2),
1586 par->getCounter(i-1),
1587 par->getCounter(i));
1590 sprintf(s, "%c.", par->getCounter(i));
1595 par->labelstring += s;
1598 for (i++; i < 10; ++i) {
1599 // reset the following counters
1600 par->setCounter(i, 0);
1602 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1603 for (i++; i < 10; ++i) {
1604 // reset the following counters
1605 par->setCounter(i, 0);
1607 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1608 par->incCounter(i + par->enumdepth);
1609 char * s = new char[25];
1610 int number = par->getCounter(i + par->enumdepth);
1612 static const char *roman[20] = {
1613 "i", "ii", "iii", "iv", "v",
1614 "vi", "vii", "viii", "ix", "x",
1615 "xi", "xii", "xiii", "xiv", "xv",
1616 "xvi", "xvii", "xviii", "xix", "xx"
1618 static const char hebrew[22] = {
1619 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1620 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1621 '÷', 'ø', 'ù', 'ú'
1624 switch (par->enumdepth) {
1626 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1627 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1629 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1632 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1633 sprintf(s, "%s.", roman[(number-1) % 20]);
1635 sprintf(s, ".%s", roman[(number-1) % 20]);
1638 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1639 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1641 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1644 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1645 sprintf(s, "%d.", number);
1647 sprintf(s, ".%d", number);
1650 par->labelstring = s;
1653 for (i += par->enumdepth + 1; i < 10; ++i)
1654 par->setCounter(i, 0); /* reset the following counters */
1657 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1658 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1660 int number = par->getCounter(i);
1662 par->bibkey = new InsetBibKey();
1663 par->bibkey->setCounter(number);
1664 par->labelstring = layout.labelstring();
1666 // In biblio should't be following counters but...
1668 string s = layout.labelstring();
1670 // the caption hack:
1672 if (layout.labeltype == LABEL_SENSITIVE) {
1673 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1674 && (par->footnotekind == LyXParagraph::FIG
1675 || par->footnotekind == LyXParagraph::WIDE_FIG))
1676 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1680 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1681 && (par->footnotekind == LyXParagraph::TAB
1682 || par->footnotekind == LyXParagraph::WIDE_TAB))
1683 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1687 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1688 && par->footnotekind == LyXParagraph::ALGORITHM)
1689 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1692 s = ":Ãúéøåâìà ";
1694 /* par->SetLayout(0);
1695 s = layout->labelstring; */
1696 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1699 s = " :úåòîùî øñç";
1703 par->labelstring = s;
1705 /* reset the enumeration counter. They are always resetted
1706 * when there is any other layout between */
1707 for (int i = 6 + par->enumdepth; i < 10; ++i)
1708 par->setCounter(i, 0);
1713 /* Updates all counters BEHIND the row. Changed paragraphs
1714 * with a dynamic left margin will be rebroken. */
1715 void LyXText::UpdateCounters(Row * row) const
1724 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1725 par = row->par->LastPhysicalPar()->Next();
1727 par = row->par->next;
1732 while (row->par != par)
1737 /* now check for the headline layouts. remember that they
1738 * have a dynamic left margin */
1740 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1741 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1744 /* Rebreak the paragraph */
1745 RemoveParagraph(row);
1746 AppendParagraph(row);
1748 /* think about the damned open footnotes! */
1749 while (par->Next() &&
1750 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1751 || par->Next()->IsDummy())){
1753 if (par->IsDummy()) {
1754 while (row->par != par)
1756 RemoveParagraph(row);
1757 AppendParagraph(row);
1762 par = par->LastPhysicalPar()->Next();
1768 /* insets an inset. */
1769 void LyXText::InsertInset(Inset *inset)
1771 SetUndo(Undo::INSERT,
1772 cursor.par->ParFromPos(cursor.pos)->previous,
1773 cursor.par->ParFromPos(cursor.pos)->next);
1774 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1775 cursor.par->InsertInset(cursor.pos, inset);
1776 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1777 * The character will not be inserted a
1782 // this is for the simple cut and paste mechanism
1783 static LyXParagraph * simple_cut_buffer = 0;
1784 static char simple_cut_buffer_textclass = 0;
1786 void DeleteSimpleCutBuffer()
1788 if (!simple_cut_buffer)
1790 LyXParagraph * tmppar;
1792 while (simple_cut_buffer) {
1793 tmppar = simple_cut_buffer;
1794 simple_cut_buffer = simple_cut_buffer->next;
1797 simple_cut_buffer = 0;
1801 void LyXText::copyEnvironmentType()
1803 copylayouttype = cursor.par->GetLayout();
1807 void LyXText::pasteEnvironmentType()
1809 SetLayout(copylayouttype);
1813 void LyXText::CutSelection(bool doclear)
1815 // This doesn't make sense, if there is no selection
1819 // OK, we have a selection. This is always between sel_start_cursor
1820 // and sel_end cursor
1821 LyXParagraph * tmppar;
1823 // Check whether there are half footnotes in the selection
1824 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1825 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1826 tmppar = sel_start_cursor.par;
1827 while (tmppar != sel_end_cursor.par){
1828 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1829 WriteAlert(_("Impossible operation"),
1830 _("Don't know what to do with half floats."),
1834 tmppar = tmppar->Next();
1838 /* table stuff -- begin */
1839 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1840 if ( sel_start_cursor.par != sel_end_cursor.par) {
1841 WriteAlert(_("Impossible operation"),
1842 _("Don't know what to do with half tables."),
1846 sel_start_cursor.par->table->Reinit();
1848 /* table stuff -- end */
1850 // make sure that the depth behind the selection are restored, too
1851 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1852 LyXParagraph * undoendpar = endpar;
1854 if (endpar && endpar->GetDepth()) {
1855 while (endpar && endpar->GetDepth()) {
1856 endpar = endpar->LastPhysicalPar()->Next();
1857 undoendpar = endpar;
1859 } else if (endpar) {
1860 endpar = endpar->Next(); // because of parindents etc.
1863 SetUndo(Undo::DELETE,
1865 .par->ParFromPos(sel_start_cursor.pos)->previous,
1868 // clear the simple_cut_buffer
1869 DeleteSimpleCutBuffer();
1871 // set the textclass
1872 simple_cut_buffer_textclass = parameters->textclass;
1874 #ifdef WITH_WARNINGS
1875 #warning Asger: Make cut more intelligent here.
1878 White paper for "intelligent" cutting:
1880 Example: "This is our text."
1881 Using " our " as selection, cutting will give "This istext.".
1882 Using "our" as selection, cutting will give "This is text.".
1883 Using " our" as selection, cutting will give "This is text.".
1884 Using "our " as selection, cutting will give "This is text.".
1886 All those four selections will (however) paste identically:
1887 Pasting with the cursor right after the "is" will give the
1888 original text with all four selections.
1890 The rationale is to be intelligent such that words are copied,
1891 cut and pasted in a functional manner.
1893 This is not implemented yet. (Asger)
1895 The changes below sees to do a lot of what you want. However
1896 I have not verified that all cases work as they should:
1898 - cut in multiple row
1900 - cut across footnotes and paragraph
1901 My simplistic tests show that the idea are basically sound but
1902 there are some items to fix up...we only need to find them
1905 As do redo Asger's example above (with | beeing the cursor in the
1906 result after cutting.):
1908 Example: "This is our text."
1909 Using " our " as selection, cutting will give "This is|text.".
1910 Using "our" as selection, cutting will give "This is | text.".
1911 Using " our" as selection, cutting will give "This is| text.".
1912 Using "our " as selection, cutting will give "This is |text.".
1917 #ifndef FIX_DOUBLE_SPACE
1918 bool space_wrapped =
1919 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1920 if (sel_end_cursor.pos > 0
1921 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1922 // please break before a space at the end
1923 sel_end_cursor.pos--;
1924 space_wrapped = true;
1926 // cut behind a space if there is one
1927 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1928 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1929 && (sel_start_cursor.par != sel_end_cursor.par
1930 || sel_start_cursor.pos < sel_end_cursor.pos))
1931 sel_start_cursor.pos++;
1933 // there are two cases: cut only within one paragraph or
1934 // more than one paragraph
1936 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1937 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1938 // only within one paragraph
1939 simple_cut_buffer = new LyXParagraph;
1940 LyXParagraph::size_type i =
1941 sel_start_cursor.pos;
1942 for (; i < sel_end_cursor.pos; ++i) {
1943 /* table stuff -- begin */
1944 if (sel_start_cursor.par->table
1945 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1946 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1947 sel_start_cursor.pos++;
1949 /* table stuff -- end */
1950 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1951 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1953 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1955 #ifndef FIX_DOUBLE_SPACE
1956 // check for double spaces
1957 if (sel_start_cursor.pos &&
1958 sel_start_cursor.par->Last() > sel_start_cursor.pos
1959 && sel_start_cursor.par
1960 ->IsLineSeparator(sel_start_cursor.pos - 1)
1961 && sel_start_cursor.par
1962 ->IsLineSeparator(sel_start_cursor.pos)) {
1963 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1966 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1969 endpar = sel_end_cursor.par->Next();
1971 // cut more than one paragraph
1974 ->BreakParagraphConservative(sel_end_cursor.pos);
1975 #ifndef FIX_DOUBLE_SPACE
1976 // insert a space at the end if there was one
1979 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1981 sel_end_cursor.par = sel_end_cursor.par->Next();
1982 sel_end_cursor.pos = 0;
1984 cursor = sel_end_cursor;
1986 #ifndef FIX_DOUBLE_SPACE
1987 // please break behind a space, if there is one.
1988 // The space should be copied too
1989 if (sel_start_cursor.par
1990 ->IsLineSeparator(sel_start_cursor.pos))
1991 sel_start_cursor.pos++;
1993 sel_start_cursor.par
1994 ->BreakParagraphConservative(sel_start_cursor.pos);
1995 #ifndef FIX_DOUBLE_SPACE
1996 if (!sel_start_cursor.pos
1997 || sel_start_cursor.par
1998 ->IsLineSeparator(sel_start_cursor.pos - 1)
1999 || sel_start_cursor.par
2000 ->IsNewline(sel_start_cursor.pos - 1)) {
2001 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2004 // store the endparagraph for redoing later
2005 endpar = sel_end_cursor.par->Next(); /* needed because
2010 // store the selection
2011 simple_cut_buffer = sel_start_cursor.par
2012 ->ParFromPos(sel_start_cursor.pos)->next;
2013 simple_cut_buffer->previous = 0;
2014 sel_end_cursor.par->previous->next = 0;
2016 // cut the selection
2017 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2018 = sel_end_cursor.par;
2020 sel_end_cursor.par->previous
2021 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2023 // care about footnotes
2024 if (simple_cut_buffer->footnoteflag) {
2025 LyXParagraph * tmppar = simple_cut_buffer;
2027 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2028 tmppar = tmppar->next;
2032 // the cut selection should begin with standard layout
2033 simple_cut_buffer->Clear();
2035 // paste the paragraphs again, if possible
2037 sel_start_cursor.par->Next()->ClearParagraph();
2038 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2040 !sel_start_cursor.par->Next()->Last())
2041 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2043 #ifndef FIX_DOUBLE_SPACE
2044 // maybe a forgotten blank
2045 if (sel_start_cursor.pos
2046 && sel_start_cursor.par
2047 ->IsLineSeparator(sel_start_cursor.pos)
2048 && sel_start_cursor.par
2049 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2050 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2055 // sometimes necessary
2057 sel_start_cursor.par->ClearParagraph();
2059 RedoParagraphs(sel_start_cursor, endpar);
2062 cursor = sel_start_cursor;
2063 SetCursor(cursor.par, cursor.pos);
2064 sel_cursor = cursor;
2065 UpdateCounters(cursor.row);
2069 void LyXText::CopySelection()
2071 // this doesnt make sense, if there is no selection
2075 // ok we have a selection. This is always between sel_start_cursor
2076 // and sel_end cursor
2077 LyXParagraph * tmppar;
2079 /* check wether there are half footnotes in the selection */
2080 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2081 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2082 tmppar = sel_start_cursor.par;
2083 while (tmppar != sel_end_cursor.par) {
2084 if (tmppar->footnoteflag !=
2085 sel_end_cursor.par->footnoteflag) {
2086 WriteAlert(_("Impossible operation"),
2087 _("Don't know what to do"
2088 " with half floats."),
2092 tmppar = tmppar->Next();
2096 /* table stuff -- begin */
2097 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2098 if ( sel_start_cursor.par != sel_end_cursor.par){
2099 WriteAlert(_("Impossible operation"),
2100 _("Don't know what to do with half tables."),
2105 /* table stuff -- end */
2107 // delete the simple_cut_buffer
2108 DeleteSimpleCutBuffer();
2110 // set the textclass
2111 simple_cut_buffer_textclass = parameters->textclass;
2113 #ifdef FIX_DOUBLE_SPACE
2114 // copy behind a space if there is one
2115 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2116 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2117 && (sel_start_cursor.par != sel_end_cursor.par
2118 || sel_start_cursor.pos < sel_end_cursor.pos))
2119 sel_start_cursor.pos++;
2121 // there are two cases: copy only within one paragraph
2122 // or more than one paragraph
2123 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2124 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2125 // only within one paragraph
2126 simple_cut_buffer = new LyXParagraph;
2127 LyXParagraph::size_type i = 0;
2128 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2129 sel_start_cursor.par->CopyIntoMinibuffer(i);
2130 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2133 // copy more than one paragraph
2134 // clone the paragraphs within the selection
2136 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2137 simple_cut_buffer = tmppar->Clone();
2138 LyXParagraph *tmppar2 = simple_cut_buffer;
2140 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2142 tmppar = tmppar->next;
2143 tmppar2->next = tmppar->Clone();
2144 tmppar2->next->previous = tmppar2;
2145 tmppar2 = tmppar2->next;
2149 // care about footnotes
2150 if (simple_cut_buffer->footnoteflag) {
2151 tmppar = simple_cut_buffer;
2153 tmppar->footnoteflag =
2154 LyXParagraph::NO_FOOTNOTE;
2155 tmppar = tmppar->next;
2159 // the simple_cut_buffer paragraph is too big
2160 LyXParagraph::size_type tmpi2 =
2161 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2162 for (; tmpi2; --tmpi2)
2163 simple_cut_buffer->Erase(0);
2165 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2167 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2168 while (tmppar2->size() > tmpi2) {
2169 tmppar2->Erase(tmppar2->text.size() - 1);
2175 void LyXText::PasteSelection()
2177 // this does not make sense, if there is nothing to paste
2178 if (!simple_cut_buffer)
2181 LyXParagraph * tmppar;
2182 LyXParagraph * endpar;
2184 LyXCursor tmpcursor;
2186 // be carefull with footnotes in footnotes
2187 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2189 // check whether the cut_buffer includes a footnote
2190 tmppar = simple_cut_buffer;
2192 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2193 tmppar = tmppar->next;
2196 WriteAlert(_("Impossible operation"),
2197 _("Can't paste float into float!"),
2203 /* table stuff -- begin */
2204 if (cursor.par->table) {
2205 if (simple_cut_buffer->next) {
2206 WriteAlert(_("Impossible operation"),
2207 _("Table cell cannot include more than one paragraph!"),
2212 /* table stuff -- end */
2214 SetUndo(Undo::INSERT,
2215 cursor.par->ParFromPos(cursor.pos)->previous,
2216 cursor.par->ParFromPos(cursor.pos)->next);
2220 // There are two cases: cutbuffer only one paragraph or many
2221 if (!simple_cut_buffer->next) {
2222 // only within a paragraph
2224 #ifndef FIX_DOUBLE_SPACE
2225 // please break behind a space, if there is one
2226 while (tmpcursor.par->Last() > tmpcursor.pos
2227 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2230 tmppar = simple_cut_buffer->Clone();
2231 /* table stuff -- begin */
2232 bool table_too_small = false;
2233 if (tmpcursor.par->table) {
2234 while (simple_cut_buffer->text.size()
2235 && !table_too_small) {
2236 if (simple_cut_buffer->IsNewline(0)){
2237 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2239 simple_cut_buffer->Erase(0);
2240 if (tmpcursor.pos < tmpcursor.par->Last())
2243 table_too_small = true;
2245 #ifdef FIX_DOUBLE_SPACE
2246 // This is an attempt to fix the
2247 // "never insert a space at the
2248 // beginning of a paragraph" problem.
2249 if (tmpcursor.pos == 0
2250 && simple_cut_buffer->IsLineSeparator(0)) {
2251 simple_cut_buffer->Erase(0);
2253 simple_cut_buffer->CutIntoMinibuffer(0);
2254 simple_cut_buffer->Erase(0);
2255 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2259 simple_cut_buffer->CutIntoMinibuffer(0);
2260 simple_cut_buffer->Erase(0);
2261 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2267 /* table stuff -- end */
2268 // Some provisions should be done here for checking
2269 // if we are inserting at the beginning of a
2270 // paragraph. If there are a space at the beginning
2271 // of the text to insert and we are inserting at
2272 // the beginning of the paragraph the space should
2274 while (simple_cut_buffer->text.size()) {
2275 #ifdef FIX_DOUBLE_SPACE
2276 // This is an attempt to fix the
2277 // "never insert a space at the
2278 // beginning of a paragraph" problem.
2279 if (tmpcursor.pos == 0
2280 && simple_cut_buffer->IsLineSeparator(0)) {
2281 simple_cut_buffer->Erase(0);
2283 simple_cut_buffer->CutIntoMinibuffer(0);
2284 simple_cut_buffer->Erase(0);
2285 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2289 simple_cut_buffer->CutIntoMinibuffer(0);
2290 simple_cut_buffer->Erase(0);
2291 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2296 delete simple_cut_buffer;
2297 simple_cut_buffer = tmppar;
2298 endpar = tmpcursor.par->Next();
2302 // make a copy of the simple cut_buffer
2303 tmppar = simple_cut_buffer;
2304 LyXParagraph * simple_cut_clone = tmppar->Clone();
2305 LyXParagraph * tmppar2 = simple_cut_clone;
2306 if (cursor.par->footnoteflag){
2307 tmppar->footnoteflag = cursor.par->footnoteflag;
2308 tmppar->footnotekind = cursor.par->footnotekind;
2310 while (tmppar->next) {
2311 tmppar = tmppar->next;
2312 tmppar2->next = tmppar->Clone();
2313 tmppar2->next->previous = tmppar2;
2314 tmppar2 = tmppar2->next;
2315 if (cursor.par->footnoteflag){
2316 tmppar->footnoteflag = cursor.par->footnoteflag;
2317 tmppar->footnotekind = cursor.par->footnotekind;
2321 // make sure there is no class difference
2322 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2323 parameters->textclass,
2326 // make the simple_cut_buffer exactly the same layout than
2327 // the cursor paragraph
2328 simple_cut_buffer->MakeSameLayout(cursor.par);
2330 // find the end of the buffer
2331 LyXParagraph * lastbuffer = simple_cut_buffer;
2332 while (lastbuffer->Next())
2333 lastbuffer = lastbuffer->Next();
2335 #ifndef FIX_DOUBLE_SPACE
2336 // Please break behind a space, if there is one. The space
2337 // should be copied too.
2338 if (cursor.par->Last() > cursor.pos
2339 && cursor.par->IsLineSeparator(cursor.pos))
2342 bool paste_the_end = false;
2344 // open the paragraph for inserting the simple_cut_buffer
2346 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2347 cursor.par->BreakParagraphConservative(cursor.pos);
2348 paste_the_end = true;
2351 #ifndef FIX_DOUBLE_SPACE
2352 // be careful with double spaces
2353 if ((!cursor.par->Last()
2354 || cursor.par->IsLineSeparator(cursor.pos - 1)
2355 || cursor.par->IsNewline(cursor.pos - 1))
2356 && simple_cut_buffer->text.size()
2357 && simple_cut_buffer->IsLineSeparator(0))
2358 simple_cut_buffer->Erase(0);
2360 // set the end for redoing later
2361 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2364 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2365 cursor.par->ParFromPos(cursor.pos)->next;
2366 cursor.par->ParFromPos(cursor.pos)->next->previous =
2367 lastbuffer->ParFromPos(lastbuffer->Last());
2369 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2370 simple_cut_buffer->previous =
2371 cursor.par->ParFromPos(cursor.pos);
2373 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2374 lastbuffer = cursor.par;
2376 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2378 // store the new cursor position
2379 tmpcursor.par = lastbuffer;
2380 tmpcursor.pos = lastbuffer->Last();
2382 // maybe some pasting
2383 if (lastbuffer->Next() && paste_the_end) {
2384 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2385 #ifndef FIX_DOUBLE_SPACE
2386 // be careful with double spaces
2387 if ((!lastbuffer->Last()
2388 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2389 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2390 && lastbuffer->Next()->Last()
2391 && lastbuffer->Next()->IsLineSeparator(0))
2392 lastbuffer->Next()->Erase(0);
2394 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2396 } else if (!lastbuffer->Next()->Last()) {
2397 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2398 #ifndef FIX_DOUBLE_SPACE
2399 // be careful witth double spaces
2400 if ((!lastbuffer->Last()
2401 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2402 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2403 && lastbuffer->Next()->Last()
2404 && lastbuffer->Next()->IsLineSeparator(0))
2405 lastbuffer->Next()->Erase(0);
2407 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2409 } else if (!lastbuffer->Last()) {
2410 lastbuffer->MakeSameLayout(lastbuffer->next);
2411 #ifndef FIX_DOUBLE_SPACE
2412 // be careful witth double spaces
2413 if ((!lastbuffer->Last()
2414 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2415 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2416 && lastbuffer->Next()->Last()
2417 && lastbuffer->Next()->IsLineSeparator(0))
2418 lastbuffer->Next()->Erase(0);
2420 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2423 lastbuffer->Next()->ClearParagraph();
2426 // restore the simple cut buffer
2427 simple_cut_buffer = simple_cut_clone;
2430 RedoParagraphs(cursor, endpar);
2432 SetCursor(cursor.par, cursor.pos);
2435 sel_cursor = cursor;
2436 SetCursor(tmpcursor.par, tmpcursor.pos);
2438 UpdateCounters(cursor.row);
2442 // returns a pointer to the very first LyXParagraph
2443 LyXParagraph * LyXText::FirstParagraph() const
2445 return params->paragraph;
2449 // returns true if the specified string is at the specified position
2450 bool LyXText::IsStringInText(LyXParagraph * par,
2451 LyXParagraph::size_type pos,
2452 char const * str) const
2456 while (pos + i < par->Last() && str[i] &&
2457 str[i] == par->GetChar(pos + i)) {
2467 // sets the selection over the number of characters of string, no check!!
2468 void LyXText::SetSelectionOverString(char const * string)
2470 sel_cursor = cursor;
2471 for (int i = 0; string[i]; ++i)
2477 // simple replacing. The font of the first selected character is used
2478 void LyXText::ReplaceSelectionWithString(char const * str)
2483 if (!selection) { // create a dummy selection
2484 sel_end_cursor = cursor;
2485 sel_start_cursor = cursor;
2488 // Get font setting before we cut
2489 LyXParagraph::size_type pos = sel_end_cursor.pos;
2490 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2492 // Insert the new string
2493 for (int i = 0; str[i]; ++i) {
2494 sel_end_cursor.par->InsertChar(pos, str[i]);
2495 sel_end_cursor.par->SetFont(pos, font);
2499 // Cut the selection
2506 // if the string can be found: return true and set the cursor to
2508 bool LyXText::SearchForward(char const * str) const
2510 LyXParagraph * par = cursor.par;
2511 LyXParagraph::size_type pos = cursor.pos;
2512 while (par && !IsStringInText(par, pos, str)) {
2513 if (pos < par->Last() - 1)
2521 SetCursor(par, pos);
2529 bool LyXText::SearchBackward(char const * string) const
2531 LyXParagraph * par = cursor.par;
2532 int pos = cursor.pos;
2538 // We skip empty paragraphs (Asger)
2540 par = par->Previous();
2542 pos = par->Last() - 1;
2543 } while (par && pos < 0);
2545 } while (par && !IsStringInText(par, pos, string));
2548 SetCursor(par, pos);
2555 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2557 char * str = new char[text.size() + 1];
2558 copy(text.begin(), text.end(), str);
2559 str[text.size()] = '\0';
2565 // needed to insert the selection
2566 void LyXText::InsertStringA(char const * s)
2569 LyXParagraph * par = cursor.par;
2570 LyXParagraph::size_type pos = cursor.pos;
2571 LyXParagraph::size_type a = 0;
2573 LyXParagraph * endpar = cursor.par->Next();
2578 textclasslist.Style(parameters->textclass,
2579 cursor.par->GetLayout()).isEnvironment();
2580 // only to be sure, should not be neccessary
2583 // insert the string, don't insert doublespace
2584 string::size_type i = 0;
2585 while (i < str.length()) {
2586 if (str[i] != '\n') {
2588 && i + 1 < str.length() && str[i + 1] != ' '
2589 && pos && par->GetChar(pos - 1)!= ' ') {
2590 par->InsertChar(pos,' ');
2592 } else if (par->table) {
2593 if (str[i] == '\t') {
2594 while((pos < par->size()) &&
2595 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2597 if (pos < par->size())
2599 else // no more fields to fill skip the rest
2601 } else if ((str[i] != 13) &&
2602 ((str[i] & 127) >= ' ')) {
2603 par->InsertChar(pos, str[i]);
2606 } else if (str[i] == ' ') {
2607 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2609 } else if (str[i] == '\t') {
2610 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2611 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2614 } else if (str[i]!= 13 &&
2615 // Ignore unprintables
2616 (str[i] & 127) >= ' ') {
2617 par->InsertChar(pos, str[i]);
2622 if (i + 1 >= str.length()) {
2626 while((pos < par->size()) &&
2627 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2630 cell = NumberOfCell(par, pos);
2631 while((pos < par->size()) &&
2632 !(par->table->IsFirstCell(cell))) {
2633 while((pos < par->size()) &&
2634 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2637 cell = NumberOfCell(par, pos);
2639 if (pos >= par->size())
2640 // no more fields to fill skip the rest
2643 if (!par->text.size()) {
2644 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2647 par->BreakParagraph(pos, flag);
2655 RedoParagraphs(cursor, endpar);
2656 SetCursor(cursor.par, cursor.pos);
2657 sel_cursor = cursor;
2658 SetCursor(par, pos);
2663 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2665 char * str = new char[text.size() + 1];
2666 copy(text.begin(), text.end(), str);
2667 str[text.size()] = '\0';
2673 /* turns double-CR to single CR, others where converted into one blank and 13s
2674 * that are ignored .Double spaces are also converted into one. Spaces at
2675 * the beginning of a paragraph are forbidden. tabs are converted into one
2676 * space. then InsertStringA is called */
2677 void LyXText::InsertStringB(char const * s)
2680 LyXParagraph * par = cursor.par;
2681 string::size_type i = 1;
2682 while (i < str.length()) {
2683 if (str[i] == '\t' && !par->table)
2685 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2687 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2688 if (str[i + 1] != '\n') {
2689 if (str[i - 1] != ' ')
2694 while (i + 1 < str.length()
2695 && (str[i + 1] == ' '
2696 || str[i + 1] == '\t'
2697 || str[i + 1] == '\n'
2698 || str[i + 1] == 13)) {
2705 InsertStringA(str.c_str());
2709 bool LyXText::GotoNextError() const
2711 LyXCursor res = cursor;
2713 if (res.pos < res.par->Last() - 1) {
2717 res.par = res.par->Next();
2722 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2723 && res.par->GetInset(res.pos)->AutoDelete()));
2726 SetCursor(res.par, res.pos);
2733 bool LyXText::GotoNextNote() const
2735 LyXCursor res = cursor;
2737 if (res.pos < res.par->Last() - 1) {
2740 res.par = res.par->Next();
2745 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2746 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2749 SetCursor(res.par, res.pos);
2756 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2760 if (!par || class1 == class2)
2762 par = par->FirstPhysicalPar();
2764 string name = textclasslist.NameOfLayout(class1, par->layout);
2766 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2767 textclasslist.NumberOfLayout(class2, name);
2770 } else { // layout not found
2771 // use default layout "Standard" (0)
2776 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2778 string s = "Layout had to be changed from\n"
2779 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2780 + "\nbecause of class conversion from\n"
2781 + textclasslist.NameOfClass(class1) + " to "
2782 + textclasslist.NameOfClass(class2);
2783 InsetError * new_inset = new InsetError(s);
2784 par->InsertChar(0, LyXParagraph::META_INSET);
2785 par->InsertInset(0, new_inset);
2794 void LyXText::CheckParagraph(LyXParagraph * par,
2795 LyXParagraph::size_type pos)
2798 LyXCursor tmpcursor;
2800 /* table stuff -- begin*/
2803 CheckParagraphInTable(par, pos);
2806 /* table stuff -- end*/
2809 LyXParagraph::size_type z;
2810 Row * row = GetRow(par, pos, y);
2812 // is there a break one row above
2813 if (row->previous && row->previous->par == row->par) {
2814 z = NextBreakPoint(row->previous, paperwidth);
2815 if ( z >= row->pos) {
2816 // set the dimensions of the row above
2817 y -= row->previous->height;
2819 refresh_row = row->previous;
2820 status = LyXText::NEED_MORE_REFRESH;
2822 BreakAgain(row->previous);
2824 // set the cursor again. Otherwise
2825 // dangling pointers are possible
2826 SetCursor(cursor.par, cursor.pos);
2827 sel_cursor = cursor;
2832 int tmpheight = row->height;
2833 LyXParagraph::size_type tmplast = RowLast(row);
2838 if (row->height == tmpheight && RowLast(row) == tmplast)
2839 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2841 status = LyXText::NEED_MORE_REFRESH;
2843 // check the special right address boxes
2844 if (textclasslist.Style(parameters->textclass,
2845 par->GetLayout()).margintype
2846 == MARGIN_RIGHT_ADDRESS_BOX) {
2847 tmpcursor.par = par;
2848 tmpcursor.row = row;
2851 tmpcursor.x_fix = 0;
2852 tmpcursor.pos = pos;
2853 RedoDrawingOfParagraph(tmpcursor);
2858 // set the cursor again. Otherwise dangling pointers are possible
2859 // also set the selection
2863 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2864 sel_cursor = cursor;
2865 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2866 sel_start_cursor = cursor;
2867 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2868 sel_end_cursor = cursor;
2869 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2870 last_sel_cursor = cursor;
2873 SetCursorIntern(cursor.par, cursor.pos);
2877 // returns 0 if inset wasn't found
2878 int LyXText::UpdateInset(Inset * inset)
2880 // first check the current paragraph
2881 int pos = cursor.par->GetPositionOfInset(inset);
2883 CheckParagraph(cursor.par, pos);
2887 // check every paragraph
2889 LyXParagraph * par = FirstParagraph();
2891 // make sure the paragraph is open
2892 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2893 pos = par->GetPositionOfInset(inset);
2895 CheckParagraph(par, pos);
2906 void LyXText::SetCursor(LyXParagraph * par,
2907 LyXParagraph::size_type pos, bool setfont) const
2909 LyXCursor old_cursor = cursor;
2910 SetCursorIntern(par, pos, setfont);
2911 DeleteEmptyParagraphMechanism(old_cursor);
2915 void LyXText::SetCursorIntern(LyXParagraph * par,
2916 LyXParagraph::size_type pos, bool setfont) const
2920 LyXParagraph * tmppar;
2921 LyXParagraph::size_type vpos,cursor_vpos;
2923 // correct the cursor position if impossible
2924 if (pos > par->Last()){
2925 tmppar = par->ParFromPos(pos);
2926 pos = par->PositionInParFromPos(pos);
2929 if (par->IsDummy() && par->previous &&
2930 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2931 while (par->previous &&
2932 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2933 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2934 par = par->previous ;
2935 if (par->IsDummy() &&
2936 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2937 pos += par->text.size() + 1;
2939 if (par->previous) {
2940 par = par->previous;
2942 pos += par->text.size() + 1;
2950 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2951 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2952 && !cursor.par->IsSeparator(cursor.pos))
2953 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2955 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2956 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2958 current_font = cursor.par->GetFontSettings(cursor.pos);
2959 real_current_font = GetFont(cursor.par, cursor.pos);
2960 if (pos == 0 && par->size() == 0
2961 && owner_->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2962 current_font.setDirection(LyXFont::RTL_DIR);
2963 real_current_font.setDirection(LyXFont::RTL_DIR);
2967 /* get the cursor y position in text */
2968 row = GetRow(par, pos, y);
2969 /* y is now the beginning of the cursor row */
2971 /* y is now the cursor baseline */
2974 /* now get the cursors x position */
2976 float fill_separator, fill_hfill, fill_label_hfill;
2977 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2979 LyXParagraph::size_type last = RowLast(row);
2980 if (row->pos > last)
2982 else if (pos <= last ) {
2983 LyXDirection letter_direction =
2984 row->par->getLetterDirection(pos);
2985 LyXDirection font_direction =
2986 real_current_font.getFontDirection();
2987 if (letter_direction == font_direction || pos == 0)
2988 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
2989 ? log2vis(pos) : log2vis(pos)+1;
2991 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
2992 ? log2vis(pos-1)+1 : log2vis(pos-1);
2994 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
2995 ? log2vis(last)+1 : log2vis(last);
2997 /* table stuff -- begin*/
2998 if (row->par->table) {
2999 int cell = NumberOfCell(row->par, row->pos);
3001 x += row->par->table->GetBeginningOfTextInCell(cell);
3002 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3003 pos = vis2log(vpos);
3004 if (row->par->IsNewline(pos)) {
3005 x = x_old + row->par->table->WidthOfColumn(cell);
3008 x += row->par->table->GetBeginningOfTextInCell(cell);
3010 x += SingleWidth(row->par, pos);
3014 /* table stuff -- end*/
3015 LyXParagraph::size_type main_body =
3016 BeginningOfMainBody(row->par);
3017 if (main_body > 0 &&
3018 (main_body-1 > last ||
3019 !row->par->IsLineSeparator(main_body-1)))
3022 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3023 pos = vis2log(vpos);
3024 if (main_body > 0 && pos == main_body-1) {
3025 x += fill_label_hfill +
3026 GetFont(row->par, -2).stringWidth(
3027 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3028 if (row->par->IsLineSeparator(main_body-1))
3029 x -= SingleWidth(row->par, main_body-1);
3032 x += SingleWidth(row->par, pos);
3033 if (HfillExpansion(row, pos)) {
3034 if (pos >= main_body)
3037 x += fill_label_hfill;
3039 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3047 cursor.x_fix = cursor.x;
3052 void LyXText::SetCursorFromCoordinates(int x, long y) const
3054 LyXCursor old_cursor = cursor;
3056 /* get the row first */
3058 Row * row = GetRowNearY(y);
3060 cursor.par = row->par;
3062 int column = GetColumnNearX(row, x);
3063 cursor.pos = row->pos + column;
3065 cursor.y = y + row->baseline;
3070 (cursor.pos == cursor.par->Last()
3071 || cursor.par->IsSeparator(cursor.pos)
3072 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3073 && !cursor.par->IsSeparator(cursor.pos))
3074 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3076 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3077 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3079 current_font = cursor.par->GetFontSettings(cursor.pos);
3080 real_current_font = GetFont(cursor.par, cursor.pos);
3082 DeleteEmptyParagraphMechanism(old_cursor);
3086 void LyXText::CursorLeft() const
3089 if (cursor.par->table) {
3090 int cell = NumberOfCell(cursor.par, cursor.pos);
3091 if (cursor.par->table->IsContRow(cell) &&
3092 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3099 void LyXText::CursorLeftIntern() const
3101 if (cursor.pos > 0) {
3102 SetCursor(cursor.par, cursor.pos - 1);
3104 else if (cursor.par->Previous()) {
3105 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3110 void LyXText::CursorRight() const
3112 CursorRightIntern();
3113 if (cursor.par->table) {
3114 int cell = NumberOfCell(cursor.par, cursor.pos);
3115 if (cursor.par->table->IsContRow(cell) &&
3116 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3123 void LyXText::CursorRightIntern() const
3125 if (cursor.pos < cursor.par->Last()) {
3126 SetCursor(cursor.par, cursor.pos + 1);
3128 else if (cursor.par->Next()) {
3129 SetCursor(cursor.par->Next(), 0);
3134 void LyXText::CursorUp() const
3136 SetCursorFromCoordinates(cursor.x_fix,
3137 cursor.y - cursor.row->baseline - 1);
3138 if (cursor.par->table) {
3139 int cell = NumberOfCell(cursor.par, cursor.pos);
3140 if (cursor.par->table->IsContRow(cell) &&
3141 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3148 void LyXText::CursorDown() const
3150 if (cursor.par->table &&
3151 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3154 SetCursorFromCoordinates(cursor.x_fix,
3155 cursor.y - cursor.row->baseline
3156 + cursor.row->height + 1);
3157 if (cursor.par->table) {
3158 int cell = NumberOfCell(cursor.par, cursor.pos);
3159 int cell_above = cursor.par->table->GetCellAbove(cell);
3160 while(cursor.par->table &&
3161 cursor.par->table->IsContRow(cell) &&
3162 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3163 SetCursorFromCoordinates(cursor.x_fix,
3164 cursor.y - cursor.row->baseline
3165 + cursor.row->height + 1);
3166 if (cursor.par->table) {
3167 cell = NumberOfCell(cursor.par, cursor.pos);
3168 cell_above = cursor.par->table->GetCellAbove(cell);
3175 void LyXText::CursorUpParagraph() const
3177 if (cursor.pos > 0) {
3178 SetCursor(cursor.par, 0);
3180 else if (cursor.par->Previous()) {
3181 SetCursor(cursor.par->Previous(), 0);
3186 void LyXText::CursorDownParagraph() const
3188 if (cursor.par->Next()) {
3189 SetCursor(cursor.par->Next(), 0);
3191 SetCursor(cursor.par, cursor.par->Last());
3197 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3199 bool deleted = false;
3201 // this is the delete-empty-paragraph-mechanism.
3202 if (selection) return;
3204 #ifdef FIX_DOUBLE_SPACE
3205 /* Ok I'll put some comments here about what is missing.
3206 I have fixed BackSpace (and thus Delete) to not delete
3207 double-spaces automagically. I have also changed Cut,
3208 Copy and Paste to hopefully do some sensible things.
3209 There are still some small problems that can lead to
3210 double spaces stored in the document file or space at
3211 the beginning of paragraphs. This happens if you have
3212 the cursor betwenn to spaces and then save. Or if you
3213 cut and paste and the selection have a space at the
3214 beginning and then save right after the paste. I am
3215 sure none of these are very hard to fix, but I will
3216 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3217 that I can get some feedback. (Lgb)
3220 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3221 // delete the LineSeparator.
3224 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3225 // delete the LineSeparator.
3228 // If the pos around the old_cursor were spaces, delete one of them.
3229 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3230 && old_cursor.pos > 0
3231 && old_cursor.pos < old_cursor.par->Last()
3232 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3233 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3234 old_cursor.par->Erase(old_cursor.pos - 1);
3235 RedoParagraphs(old_cursor, old_cursor.par->Next());
3236 // or RedoDrawingOfParagraph(old_cursor);
3238 if (old_cursor.par == cursor.par &&
3239 cursor.pos > old_cursor.pos)
3240 SetCursor(cursor.par, cursor.pos - 1);
3242 SetCursor(cursor.par, cursor.pos);
3247 // Paragraph should not be deleted if empty
3248 if ((textclasslist.Style(parameters->textclass,
3249 old_cursor.par->GetLayout())).keepempty)
3252 LyXCursor tmpcursor;
3254 if (old_cursor.par != cursor.par) {
3255 if ( (old_cursor.par->Last() == 0
3256 || (old_cursor.par->Last() == 1
3257 && (old_cursor.par->IsLineSeparator(0))))
3258 && old_cursor.par->FirstPhysicalPar()
3259 == old_cursor.par->LastPhysicalPar()) {
3261 // ok, we will delete anything
3263 // make sure that you do not delete any environments
3264 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3265 !(old_cursor.row->previous
3266 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3267 && !(old_cursor.row->next
3268 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3270 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3271 ((old_cursor.row->previous
3272 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3274 (old_cursor.row->next
3275 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3277 status = LyXText::NEED_MORE_REFRESH;
3280 if (old_cursor.row->previous) {
3281 refresh_row = old_cursor.row->previous;
3282 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3284 cursor = old_cursor; // that undo can restore the right cursor position
3285 LyXParagraph * endpar = old_cursor.par->next;
3286 if (endpar && endpar->GetDepth()) {
3287 while (endpar && endpar->GetDepth()) {
3288 endpar = endpar->LastPhysicalPar()->Next();
3291 SetUndo(Undo::DELETE,
3292 old_cursor.par->previous,
3297 RemoveRow(old_cursor.row);
3298 if (params->paragraph == old_cursor.par) {
3299 params->paragraph = params->paragraph->next;
3302 delete old_cursor.par;
3304 /* Breakagain the next par. Needed
3305 * because of the parindent that
3306 * can occur or dissappear. The
3307 * next row can change its height,
3308 * if there is another layout before */
3309 if (refresh_row->next) {
3310 BreakAgain(refresh_row->next);
3311 UpdateCounters(refresh_row);
3313 SetHeightOfRow(refresh_row);
3315 refresh_row = old_cursor.row->next;
3316 refresh_y = old_cursor.y - old_cursor.row->baseline;
3319 cursor = old_cursor; // that undo can restore the right cursor position
3320 LyXParagraph *endpar = old_cursor.par->next;
3321 if (endpar && endpar->GetDepth()) {
3322 while (endpar && endpar->GetDepth()) {
3323 endpar = endpar->LastPhysicalPar()->Next();
3326 SetUndo(Undo::DELETE,
3327 old_cursor.par->previous,
3332 RemoveRow(old_cursor.row);
3334 if (params->paragraph == old_cursor.par) {
3335 params->paragraph = params->paragraph->next;
3337 delete old_cursor.par;
3339 /* Breakagain the next par. Needed
3340 because of the parindent that can
3341 occur or dissappear.
3342 The next row can change its height,
3343 if there is another layout before
3346 BreakAgain(refresh_row);
3347 UpdateCounters(refresh_row->previous);
3352 SetCursor(cursor.par, cursor.pos);
3354 /* if (cursor.y > old_cursor.y)
3355 cursor.y -= old_cursor.row->height; */
3357 if (sel_cursor.par == old_cursor.par
3358 && sel_cursor.pos == sel_cursor.pos) {
3359 // correct selection
3360 sel_cursor = cursor;
3365 if (old_cursor.par->ClearParagraph()){
3366 RedoParagraphs(old_cursor, old_cursor.par->Next());
3368 SetCursor(cursor.par, cursor.pos);
3369 sel_cursor = cursor;
3376 LyXParagraph * LyXText::GetParFromID(int id)
3378 LyXParagraph * result = FirstParagraph();
3379 while (result && result->id() != id)
3380 result = result->next;
3386 bool LyXText::TextUndo()
3388 // returns false if no undo possible
3389 Undo * undo = params->undostack.pop();
3394 .push(CreateUndo(undo->kind,
3395 GetParFromID(undo->number_of_before_par),
3396 GetParFromID(undo->number_of_behind_par)));
3398 return TextHandleUndo(undo);
3402 bool LyXText::TextRedo()
3404 // returns false if no redo possible
3405 Undo * undo = params->redostack.pop();
3410 .push(CreateUndo(undo->kind,
3411 GetParFromID(undo->number_of_before_par),
3412 GetParFromID(undo->number_of_behind_par)));
3414 return TextHandleUndo(undo);
3418 bool LyXText::TextHandleUndo(Undo * undo)
3420 // returns false if no undo possible
3421 bool result = false;
3423 LyXParagraph * before =
3424 GetParFromID(undo->number_of_before_par);
3425 LyXParagraph * behind =
3426 GetParFromID(undo->number_of_behind_par);
3427 LyXParagraph * tmppar;
3428 LyXParagraph * tmppar2;
3429 LyXParagraph * tmppar3;
3430 LyXParagraph * tmppar4;
3431 LyXParagraph * endpar;
3432 LyXParagraph * tmppar5;
3434 // if there's no before take the beginning
3435 // of the document for redoing
3437 SetCursorIntern(FirstParagraph(), 0);
3439 // replace the paragraphs with the undo informations
3441 tmppar3 = undo->par;
3442 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3445 while (tmppar4->next)
3446 tmppar4 = tmppar4->next;
3447 } // get last undo par
3449 // now remove the old text if there is any
3450 if (before != behind || (!behind && !before)){
3452 tmppar5 = before->next;
3454 tmppar5 = params->paragraph;
3456 while (tmppar5 && tmppar5 != behind){
3458 tmppar5 = tmppar5->next;
3459 // a memory optimization for edit: Only layout information
3460 // is stored in the undo. So restore the text informations.
3461 if (undo->kind == Undo::EDIT){
3462 tmppar2->text = tmppar->text;
3463 tmppar->text.clear();
3464 tmppar2 = tmppar2->next;
3466 if ( currentrow && currentrow->par == tmppar )
3467 currentrow = currentrow -> previous;
3468 // Commenting out this might remove the error
3469 // reported by Purify, but it might also
3470 // introduce a memory leak. We need to
3476 // put the new stuff in the list if there is one
3479 before->next = tmppar3;
3481 params->paragraph = tmppar3;
3482 tmppar3->previous = before;
3486 params->paragraph = behind;
3489 tmppar4->next = behind;
3491 behind->previous = tmppar4;
3495 // Set the cursor for redoing
3497 SetCursorIntern(before->FirstSelfrowPar(), 0);
3498 // check wether before points to a closed float and open it if necessary
3499 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3500 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3502 while (tmppar4->previous &&
3503 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3504 tmppar4 = tmppar4->previous;
3505 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3506 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3507 tmppar4 = tmppar4->next;
3512 // open a cosed footnote at the end if necessary
3513 if (behind && behind->previous &&
3514 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3515 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3516 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3517 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3518 behind = behind->next;
3522 // calculate the endpar for redoing the paragraphs.
3524 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3525 endpar = behind->LastPhysicalPar()->Next();
3527 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3532 tmppar = GetParFromID(undo->number_of_cursor_par);
3533 RedoParagraphs(cursor, endpar);
3535 SetCursorIntern(tmppar, undo->cursor_pos);
3536 UpdateCounters(cursor.row);
3546 void LyXText::FinishUndo()
3548 // makes sure the next operation will be stored
3549 undo_finished = True;
3553 void LyXText::FreezeUndo()
3555 // this is dangerous and for internal use only
3560 void LyXText::UnFreezeUndo()
3562 // this is dangerous and for internal use only
3563 undo_frozen = false;
3567 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3568 LyXParagraph const * behind) const
3571 params->undostack.push(CreateUndo(kind, before, behind));
3572 params->redostack.clear();
3576 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3577 LyXParagraph const * behind)
3579 params->redostack.push(CreateUndo(kind, before, behind));
3583 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3584 LyXParagraph const * behind) const
3586 int before_number = -1;
3587 int behind_number = -1;
3589 before_number = before->id();
3591 behind_number = behind->id();
3592 // Undo::EDIT and Undo::FINISH are
3593 // always finished. (no overlapping there)
3594 // overlapping only with insert and delete inside one paragraph:
3595 // Nobody wants all removed character
3596 // appear one by one when undoing.
3597 // EDIT is special since only layout information, not the
3598 // contents of a paragaph are stored.
3599 if (!undo_finished && kind != Undo::EDIT &&
3600 kind != Undo::FINISH){
3601 // check wether storing is needed
3602 if (!params->undostack.empty() &&
3603 params->undostack.top()->kind == kind &&
3604 params->undostack.top()->number_of_before_par == before_number &&
3605 params->undostack.top()->number_of_behind_par == behind_number ){
3610 // create a new Undo
3611 LyXParagraph * undopar;
3612 LyXParagraph * tmppar;
3613 LyXParagraph * tmppar2;
3615 LyXParagraph * start = 0;
3616 LyXParagraph * end = 0;
3619 start = before->next;
3621 start = FirstParagraph();
3623 end = behind->previous;
3625 end = FirstParagraph();
3631 && start != end->next
3632 && (before != behind || (!before && !behind))) {
3634 tmppar2 = tmppar->Clone();
3635 tmppar2->id(tmppar->id());
3637 // a memory optimization: Just store the layout information
3639 if (kind == Undo::EDIT){
3640 tmppar2->text.clear();
3645 while (tmppar != end && tmppar->next) {
3646 tmppar = tmppar->next;
3647 tmppar2->next = tmppar->Clone();
3648 tmppar2->next->id(tmppar->id());
3649 // a memory optimization: Just store the layout
3650 // information when only edit
3651 if (kind == Undo::EDIT){
3652 tmppar2->next->text.clear();
3654 tmppar2->next->previous = tmppar2;
3655 tmppar2 = tmppar2->next;
3659 undopar = 0; // nothing to replace (undo of delete maybe)
3661 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3662 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3664 Undo * undo = new Undo(kind,
3665 before_number, behind_number,
3666 cursor_par, cursor_pos,
3669 undo_finished = false;
3674 void LyXText::SetCursorParUndo()
3676 SetUndo(Undo::FINISH,
3677 cursor.par->ParFromPos(cursor.pos)->previous,
3678 cursor.par->ParFromPos(cursor.pos)->next);
3682 void LyXText::RemoveTableRow(LyXCursor * cur) const
3688 // move to the previous row
3689 int cell_act = NumberOfCell(cur->par, cur->pos);
3692 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3695 !cur->par->table->IsFirstCell(cell_act)) {
3697 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3702 // now we have to pay attention if the actual table is the
3703 // main row of TableContRows and if yes to delete all of them
3708 // delete up to the next row
3709 while (cur->pos < cur->par->Last() &&
3711 || !cur->par->table->IsFirstCell(cell_act))) {
3712 while (cur->pos < cur->par->Last() &&
3713 !cur->par->IsNewline(cur->pos))
3714 cur->par->Erase(cur->pos);
3717 if (cur->pos < cur->par->Last())
3718 cur->par->Erase(cur->pos);
3720 if (cur->pos && cur->pos == cur->par->Last()) {
3722 cur->par->Erase(cur->pos); // no newline at very end!
3724 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3725 !cur->par->table->IsContRow(cell_org) &&
3726 cur->par->table->IsContRow(cell));
3727 cur->par->table->DeleteRow(cell_org);
3732 bool LyXText::IsEmptyTableCell() const
3734 LyXParagraph::size_type pos = cursor.pos - 1;
3735 while (pos >= 0 && pos < cursor.par->Last()
3736 && !cursor.par->IsNewline(pos))
3738 return cursor.par->IsNewline(pos + 1);
3742 void LyXText::toggleAppendix(){
3743 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3744 bool start = !par->start_of_appendix;
3746 // ensure that we have only one start_of_appendix in this document
3747 LyXParagraph * tmp = FirstParagraph();
3748 for (; tmp; tmp = tmp->next)
3749 tmp->start_of_appendix = 0;
3750 par->start_of_appendix = start;
3752 // we can set the refreshing parameters now
3753 status = LyXText::NEED_MORE_REFRESH;
3755 refresh_row = 0; // not needed for full update
3757 SetCursor(cursor.par, cursor.pos);