1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
40 #define FIX_DOUBLE_SPACE 1
45 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
53 parameters = &p->params;
57 status = LyXText::UNCHANGED;
58 LyXParagraph * par = p->paragraph;
59 current_font = GetFont(par, 0);
64 InsertParagraph(par, lastrow);
68 // set cursor at the very top position
69 selection = true; /* these setting is necessary
70 because of the delete-empty-
71 paragraph mechanism in
73 SetCursor(firstrow->par, 0);
78 // no rebreak necessary
84 // Default layouttype for copy environment type
91 // Delete all rows, this does not touch the paragraphs!
92 Row * tmprow = firstrow;
94 tmprow = firstrow->next;
101 void LyXText::owner(BufferView * bv)
103 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
107 // Gets the fully instantiated font at a given position in a paragraph
108 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
109 // The difference is that this one is used for displaying, and thus we
110 // are allowed to make cosmetic improvements. For instance make footnotes
112 // If position is -1, we get the layout font of the paragraph.
113 // If position is -2, we get the font of the manual label of the paragraph.
114 LyXFont LyXText::GetFont(LyXParagraph * par,
115 LyXParagraph::size_type pos) const
117 LyXLayout const & layout =
118 textclasslist.Style(parameters->textclass, par->GetLayout());
120 char par_depth = par->GetDepth();
121 // We specialize the 95% common case:
122 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
125 if (layout.labeltype == LABEL_MANUAL
126 && pos < BeginningOfMainBody(par)) {
128 return par->GetFontSettings(pos).
129 realize(layout.reslabelfont);
131 return par->GetFontSettings(pos).
132 realize(layout.resfont);
135 // process layoutfont for pos == -1 and labelfont for pos < -1
137 return layout.resfont;
139 return layout.reslabelfont;
143 // The uncommon case need not be optimized as much
145 LyXFont layoutfont, tmpfont;
149 if (pos < BeginningOfMainBody(par)) {
151 layoutfont = layout.labelfont;
154 layoutfont = layout.font;
156 tmpfont = par->GetFontSettings(pos);
157 tmpfont.realize(layoutfont);
160 // process layoutfont for pos == -1 and labelfont for pos < -1
162 tmpfont = layout.font;
164 tmpfont = layout.labelfont;
167 // Resolve against environment font information
168 while (par && par_depth && !tmpfont.resolved()) {
169 par = par->DepthHook(par_depth - 1);
171 tmpfont.realize(textclasslist.
172 Style(parameters->textclass,
173 par->GetLayout()).font);
174 par_depth = par->GetDepth();
178 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
180 // Cosmetic improvement: If this is an open footnote, make the font
182 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
183 && par->footnotekind == LyXParagraph::FOOTNOTE) {
191 void LyXText::SetCharFont(LyXParagraph * par,
192 LyXParagraph::size_type pos,
196 // Let the insets convert their font
197 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
198 if (par->GetInset(pos))
199 font = par->GetInset(pos)->ConvertFont(font);
202 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
205 // Get concrete layout font to reduce against
208 if (pos < BeginningOfMainBody(par))
209 layoutfont = layout.labelfont;
211 layoutfont = layout.font;
213 // Realize against environment font information
214 if (par->GetDepth()){
215 LyXParagraph * tp = par;
216 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
217 tp = tp->DepthHook(tp->GetDepth()-1);
219 layoutfont.realize(textclasslist.
220 Style(parameters->textclass,
221 tp->GetLayout()).font);
225 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
227 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
228 && par->footnotekind == LyXParagraph::FOOTNOTE) {
229 layoutfont.decSize();
232 // Now, reduce font against full layout font
233 font.reduce(layoutfont);
235 par->SetFont(pos, font);
239 /* inserts a new row behind the specified row, increments
240 * the touched counters */
241 void LyXText::InsertRow(Row * row, LyXParagraph * par,
242 LyXParagraph::size_type pos) const
244 Row * tmprow = new Row;
246 tmprow->previous = 0;
247 tmprow->next = firstrow;
250 tmprow->previous = row;
251 tmprow->next = row->next;
256 tmprow->next->previous = tmprow;
258 if (tmprow->previous)
259 tmprow->previous->next = tmprow;
267 ++number_of_rows; // one more row
271 // removes the row and reset the touched counters
272 void LyXText::RemoveRow(Row * row) const
274 /* this must not happen before the currentrow for clear reasons.
275 so the trick is just to set the current row onto the previous
278 GetRow(row->par, row->pos, unused_y);
279 currentrow = currentrow->previous;
281 currentrow_y -= currentrow->height;
286 row->next->previous = row->previous;
287 if (!row->previous) {
288 firstrow = row->next;
290 row->previous->next = row->next;
293 lastrow = row->previous;
295 height -= row->height; // the text becomes smaller
298 --number_of_rows; // one row less
302 // remove all following rows of the paragraph of the specified row.
303 void LyXText::RemoveParagraph(Row * row) const
305 LyXParagraph * tmppar = row->par;
309 while (row && row->par == tmppar) {
317 // insert the specified paragraph behind the specified row
318 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
320 InsertRow(row, par, 0); /* insert a new row, starting
323 SetCounter(par); // set the counters
325 // and now append the whole paragraph behind the new row
327 firstrow->height = 0;
328 AppendParagraph(firstrow);
330 row->next->height = 0;
331 AppendParagraph(row->next);
336 void LyXText::ToggleFootnote()
338 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
340 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
342 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
344 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
350 void LyXText::OpenStuff()
352 if (cursor.pos == 0 && cursor.par->bibkey){
353 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
355 else if (cursor.pos < cursor.par->Last()
356 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
357 && cursor.par->GetInset(cursor.pos)->Editable()) {
358 owner_->owner()->getMiniBuffer()
359 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
360 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
362 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
369 void LyXText::CloseFootnote()
371 LyXParagraph * tmppar;
372 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
374 // if the cursor is not in an open footnote, or
375 // there is no open footnote in this paragraph, just return.
376 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
379 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
380 owner_->owner()->getMiniBuffer()
381 ->Set(_("Nothing to do"));
385 // ok, move the cursor right before the footnote
386 // just a little faster than using CursorRight()
388 cursor.par->ParFromPos(cursor.pos) != par;
392 // now the cursor is at the beginning of the physical par
393 SetCursor(cursor.par,
395 cursor.par->ParFromPos(cursor.pos)->size());
397 /* we are in a footnote, so let us move at the beginning */
398 /* this is just faster than using just CursorLeft() */
401 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
402 // just a little bit faster than movin the cursor
403 tmppar = tmppar->Previous();
405 SetCursor(tmppar, tmppar->Last());
408 // the cursor must be exactly before the footnote
409 par = cursor.par->ParFromPos(cursor.pos);
411 status = LyXText::NEED_MORE_REFRESH;
412 refresh_row = cursor.row;
413 refresh_y = cursor.y - cursor.row->baseline;
416 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
417 Row * row = cursor.row;
419 tmppar->CloseFootnote(cursor.pos);
421 while (tmppar != endpar) {
422 RemoveRow(row->next);
424 tmppar = row->next->par;
429 AppendParagraph(cursor.row);
431 SetCursor(cursor.par, cursor.pos);
435 if (cursor.row->next)
436 SetHeightOfRow(cursor.row->next);
440 /* used in setlayout */
441 // Asger is not sure we want to do this...
442 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
445 LyXLayout const & layout =
446 textclasslist.Style(parameters->textclass, par->GetLayout());
448 LyXFont layoutfont, tmpfont;
449 for (LyXParagraph::size_type pos = 0;
450 pos < par->Last(); ++pos) {
451 if (pos < BeginningOfMainBody(par))
452 layoutfont = layout.labelfont;
454 layoutfont = layout.font;
456 tmpfont = par->GetFontSettings(pos);
457 tmpfont.reduce(layoutfont);
458 par->SetFont(pos, tmpfont);
463 // set layout over selection and make a total rebreak of those paragraphs
464 void LyXText::SetLayout(LyXTextClass::size_type layout)
468 // if there is no selection just set the layout
469 // of the current paragraph */
471 sel_start_cursor = cursor; // dummy selection
472 sel_end_cursor = cursor;
475 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
476 LyXParagraph * undoendpar = endpar;
478 if (endpar && endpar->GetDepth()) {
479 while (endpar && endpar->GetDepth()) {
480 endpar = endpar->LastPhysicalPar()->Next();
485 endpar = endpar->Next(); // because of parindents etc.
489 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
492 tmpcursor = cursor; /* store the current cursor */
494 /* ok we have a selection. This is always between sel_start_cursor
495 * and sel_end cursor */
496 cursor = sel_start_cursor;
498 LyXLayout const & lyxlayout =
499 textclasslist.Style(parameters->textclass, layout);
501 while (cursor.par != sel_end_cursor.par) {
502 if (cursor.par->footnoteflag ==
503 sel_start_cursor.par->footnoteflag) {
504 cursor.par->SetLayout(layout);
505 MakeFontEntriesLayoutSpecific(cursor.par);
506 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
507 fppar->added_space_top = lyxlayout.fill_top ?
508 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
509 fppar->added_space_bottom = lyxlayout.fill_bottom ?
510 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
511 if (lyxlayout.margintype == MARGIN_MANUAL)
512 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
513 if (lyxlayout.labeltype != LABEL_BIBLIO
515 delete fppar->bibkey;
519 cursor.par = cursor.par->Next();
521 if (cursor.par->footnoteflag ==
522 sel_start_cursor.par->footnoteflag) {
523 cursor.par->SetLayout(layout);
524 MakeFontEntriesLayoutSpecific(cursor.par);
525 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
526 fppar->added_space_top = lyxlayout.fill_top ?
527 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
528 fppar->added_space_bottom = lyxlayout.fill_bottom ?
529 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
530 if (lyxlayout.margintype == MARGIN_MANUAL)
531 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
532 if (lyxlayout.labeltype != LABEL_BIBLIO
534 delete fppar->bibkey;
539 RedoParagraphs(sel_start_cursor, endpar);
541 // we have to reset the selection, because the
542 // geometry could have changed */
543 SetCursor(sel_start_cursor.par, sel_start_cursor.pos, false);
545 SetCursor(sel_end_cursor.par, sel_end_cursor.pos, false);
546 UpdateCounters(cursor.row);
549 SetCursor(tmpcursor.par, tmpcursor.pos, true);
553 // increment depth over selection and
554 // make a total rebreak of those paragraphs
555 void LyXText::IncDepth()
557 // If there is no selection, just use the current paragraph
559 sel_start_cursor = cursor; // dummy selection
560 sel_end_cursor = cursor;
563 // We end at the next paragraph with depth 0
564 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
565 LyXParagraph * undoendpar = endpar;
567 if (endpar && endpar->GetDepth()) {
568 while (endpar && endpar->GetDepth()) {
569 endpar = endpar->LastPhysicalPar()->Next();
574 endpar = endpar->Next(); // because of parindents etc.
579 .par->ParFromPos(sel_start_cursor.pos)->previous,
582 LyXCursor tmpcursor = cursor; // store the current cursor
584 // ok we have a selection. This is always between sel_start_cursor
585 // and sel_end cursor
586 cursor = sel_start_cursor;
588 bool anything_changed = false;
591 // NOTE: you can't change the depth of a bibliography entry
592 if (cursor.par->footnoteflag ==
593 sel_start_cursor.par->footnoteflag
594 && textclasslist.Style(parameters->textclass,
595 cursor.par->GetLayout()
596 ).labeltype != LABEL_BIBLIO) {
597 LyXParagraph * prev =
598 cursor.par->FirstPhysicalPar()->Previous();
600 && (prev->GetDepth() - cursor.par->GetDepth() > 0
601 || (prev->GetDepth() == cursor.par->GetDepth()
602 && textclasslist.Style(parameters->textclass,
603 prev->GetLayout()).isEnvironment()))) {
604 cursor.par->FirstPhysicalPar()->depth++;
605 anything_changed = true;
608 if (cursor.par == sel_end_cursor.par)
610 cursor.par = cursor.par->Next();
613 // if nothing changed set all depth to 0
614 if (!anything_changed) {
615 cursor = sel_start_cursor;
616 while (cursor.par != sel_end_cursor.par) {
617 cursor.par->FirstPhysicalPar()->depth = 0;
618 cursor.par = cursor.par->Next();
620 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
621 cursor.par->FirstPhysicalPar()->depth = 0;
624 RedoParagraphs(sel_start_cursor, endpar);
626 // we have to reset the selection, because the
627 // geometry could have changed
628 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
630 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
631 UpdateCounters(cursor.row);
634 SetCursor(tmpcursor.par, tmpcursor.pos);
638 // decrement depth over selection and
639 // make a total rebreak of those paragraphs
640 void LyXText::DecDepth()
642 // if there is no selection just set the layout
643 // of the current paragraph
645 sel_start_cursor = cursor; // dummy selection
646 sel_end_cursor = cursor;
649 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
650 LyXParagraph * undoendpar = endpar;
652 if (endpar && endpar->GetDepth()) {
653 while (endpar && endpar->GetDepth()) {
654 endpar = endpar->LastPhysicalPar()->Next();
659 endpar = endpar->Next(); // because of parindents etc.
664 .par->ParFromPos(sel_start_cursor.pos)->previous,
667 LyXCursor tmpcursor = cursor; // store the current cursor
669 // ok we have a selection. This is always between sel_start_cursor
670 // and sel_end cursor
671 cursor = sel_start_cursor;
674 if (cursor.par->footnoteflag ==
675 sel_start_cursor.par->footnoteflag) {
676 if (cursor.par->FirstPhysicalPar()->depth)
677 cursor.par->FirstPhysicalPar()->depth--;
679 if (cursor.par == sel_end_cursor.par)
681 cursor.par = cursor.par->Next();
684 RedoParagraphs(sel_start_cursor, endpar);
686 // we have to reset the selection, because the
687 // geometry could have changed
688 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
690 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
691 UpdateCounters(cursor.row);
694 SetCursor(tmpcursor.par, tmpcursor.pos);
698 // set font over selection and make a total rebreak of those paragraphs
699 void LyXText::SetFont(LyXFont const & font, bool toggleall)
701 // if there is no selection just set the current_font
703 // Determine basis font
705 if (cursor.pos < BeginningOfMainBody(cursor.par))
706 layoutfont = GetFont(cursor.par, -2);
708 layoutfont = GetFont(cursor.par, -1);
709 // Update current font
710 real_current_font.update(font, parameters->language_info, toggleall);
712 // Reduce to implicit settings
713 current_font = real_current_font;
714 current_font.reduce(layoutfont);
715 // And resolve it completely
716 real_current_font.realize(layoutfont);
720 LyXCursor tmpcursor = cursor; // store the current cursor
722 // ok we have a selection. This is always between sel_start_cursor
723 // and sel_end cursor
726 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
727 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
728 cursor = sel_start_cursor;
729 while (cursor.par != sel_end_cursor.par ||
730 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
731 && cursor.pos < sel_end_cursor.pos))
733 if (cursor.pos < cursor.par->Last()
734 && cursor.par->footnoteflag
735 == sel_start_cursor.par->footnoteflag) {
736 // an open footnote should behave
738 LyXFont newfont = GetFont(cursor.par, cursor.pos);
739 newfont.update(font, parameters->language_info, toggleall);
740 SetCharFont(cursor.par, cursor.pos, newfont);
744 cursor.par = cursor.par->Next();
748 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
750 // we have to reset the selection, because the
751 // geometry could have changed
752 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
754 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
757 SetCursor(tmpcursor.par, tmpcursor.pos);
761 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
763 Row * tmprow = cur.row;
764 long y = cur.y - tmprow->baseline;
766 SetHeightOfRow(tmprow);
767 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
768 // find the first row of the paragraph
769 if (first_phys_par != tmprow->par)
770 while (tmprow->previous
771 && tmprow->previous->par != first_phys_par) {
772 tmprow = tmprow->previous;
774 SetHeightOfRow(tmprow);
776 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
777 tmprow = tmprow->previous;
779 SetHeightOfRow(tmprow);
782 // we can set the refreshing parameters now
783 status = LyXText::NEED_MORE_REFRESH;
785 refresh_row = tmprow;
786 SetCursor(cur.par, cur.pos);
790 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
792 Row * tmprow = cur.row;
794 long y = cur.y - tmprow->baseline;
795 SetHeightOfRow(tmprow);
796 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
797 // find the first row of the paragraph
798 if (first_phys_par != tmprow->par)
799 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
800 tmprow = tmprow->previous;
803 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
804 tmprow = tmprow->previous;
808 // we can set the refreshing parameters now
809 if (status == LyXText::UNCHANGED || y < refresh_y) {
811 refresh_row = tmprow;
813 status = LyXText::NEED_MORE_REFRESH;
814 SetCursor(cur.par, cur.pos);
818 /* deletes and inserts again all paragaphs between the cursor
819 * and the specified par
820 * This function is needed after SetLayout and SetFont etc. */
821 void LyXText::RedoParagraphs(LyXCursor const & cur,
822 LyXParagraph const * endpar) const
825 LyXParagraph * tmppar, * first_phys_par;
827 Row * tmprow = cur.row;
829 long y = cur.y - tmprow->baseline;
831 if (!tmprow->previous){
832 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
834 first_phys_par = tmprow->par->FirstPhysicalPar();
835 // find the first row of the paragraph
836 if (first_phys_par != tmprow->par)
837 while (tmprow->previous &&
838 (tmprow->previous->par != first_phys_par)) {
839 tmprow = tmprow->previous;
842 while (tmprow->previous
843 && tmprow->previous->par == first_phys_par) {
844 tmprow = tmprow->previous;
849 // we can set the refreshing parameters now
850 status = LyXText::NEED_MORE_REFRESH;
852 refresh_row = tmprow->previous; /* the real refresh row will
853 be deleted, so I store
857 tmppar = tmprow->next->par;
860 while (tmppar != endpar) {
861 RemoveRow(tmprow->next);
863 tmppar = tmprow->next->par;
868 // remove the first one
869 tmprow2 = tmprow; /* this is because tmprow->previous
871 tmprow = tmprow->previous;
874 tmppar = first_phys_par;
878 InsertParagraph(tmppar, tmprow);
881 while (tmprow->next && tmprow->next->par == tmppar)
882 tmprow = tmprow->next;
883 tmppar = tmppar->Next();
885 } while (tmppar != endpar);
887 // this is because of layout changes
889 refresh_y -= refresh_row->height;
890 SetHeightOfRow(refresh_row);
892 refresh_row = firstrow;
894 SetHeightOfRow(refresh_row);
897 if (tmprow && tmprow->next)
898 SetHeightOfRow(tmprow->next);
902 int LyXText::FullRebreak()
904 if (need_break_row) {
905 BreakAgain(need_break_row);
913 /* important for the screen */
916 /* the cursor set functions have a special mechanism. When they
917 * realize, that you left an empty paragraph, they will delete it.
918 * They also delet the corresponding row */
920 // need the selection cursor:
921 void LyXText::SetSelection()
924 last_sel_cursor = sel_cursor;
925 sel_start_cursor = sel_cursor;
926 sel_end_cursor = sel_cursor;
931 // first the toggling area
932 if (cursor.y < last_sel_cursor.y ||
933 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
934 toggle_end_cursor = last_sel_cursor;
935 toggle_cursor = cursor;
938 toggle_end_cursor = cursor;
939 toggle_cursor = last_sel_cursor;
942 last_sel_cursor = cursor;
944 // and now the whole selection
946 if (sel_cursor.par == cursor.par)
947 if (sel_cursor.pos < cursor.pos) {
948 sel_end_cursor = cursor;
949 sel_start_cursor = sel_cursor;
951 sel_end_cursor = sel_cursor;
952 sel_start_cursor = cursor;
954 else if (sel_cursor.y < cursor.y ||
955 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
956 sel_end_cursor = cursor;
957 sel_start_cursor = sel_cursor;
960 sel_end_cursor = sel_cursor;
961 sel_start_cursor = cursor;
964 // a selection with no contents is not a selection
965 if (sel_start_cursor.x == sel_end_cursor.x &&
966 sel_start_cursor.y == sel_end_cursor.y)
971 void LyXText::ClearSelection() const
978 void LyXText::CursorHome() const
980 SetCursor(cursor.par, cursor.row->pos);
984 void LyXText::CursorEnd() const
986 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
987 SetCursor(cursor.par, RowLast(cursor.row) + 1);
989 if (cursor.par->Last() &&
990 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
991 || cursor.par->IsNewline(RowLast(cursor.row))))
992 SetCursor(cursor.par, RowLast(cursor.row));
994 SetCursor(cursor.par, RowLast(cursor.row) + 1);
996 if (cursor.par->table) {
997 int cell = NumberOfCell(cursor.par, cursor.pos);
998 if (cursor.par->table->RowHasContRow(cell) &&
999 cursor.par->table->CellHasContRow(cell)<0) {
1000 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1001 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1003 if (cursor.par->Last() &&
1004 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1005 || cursor.par->IsNewline(RowLast(cursor.row))))
1006 SetCursor(cursor.par, RowLast(cursor.row));
1008 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1015 void LyXText::CursorTop() const
1017 while (cursor.par->Previous())
1018 cursor.par = cursor.par->Previous();
1019 SetCursor(cursor.par, 0);
1023 void LyXText::CursorBottom() const
1025 while (cursor.par->Next())
1026 cursor.par = cursor.par->Next();
1027 SetCursor(cursor.par, cursor.par->Last());
1031 /* returns a pointer to the row near the specified y-coordinate
1032 * (relative to the whole text). y is set to the real beginning
1034 Row * LyXText::GetRowNearY(long & y) const
1040 tmprow = currentrow;
1041 tmpy = currentrow_y;
1048 while (tmprow->next && tmpy + tmprow->height <= y) {
1049 tmpy += tmprow->height;
1050 tmprow = tmprow->next;
1053 while (tmprow->previous && tmpy > y) {
1054 tmprow = tmprow->previous;
1055 tmpy -= tmprow->height;
1058 currentrow = tmprow;
1059 currentrow_y = tmpy;
1061 y = tmpy; // return the real y
1066 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1068 // If the mask is completely neutral, tell user
1069 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1070 // Could only happen with user style
1071 owner_->owner()->getMiniBuffer()
1072 ->Set(_("No font change defined. Use Character under"
1073 " the Layout menu to define font change."));
1077 // Try implicit word selection
1078 LyXCursor resetCursor = cursor;
1079 int implicitSelection = SelectWordWhenUnderCursor();
1082 SetFont(font, toggleall);
1084 /* Implicit selections are cleared afterwards and cursor is set to the
1085 original position. */
1086 if (implicitSelection) {
1088 cursor = resetCursor;
1089 SetCursor( cursor.par, cursor.pos );
1090 sel_cursor = cursor;
1095 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1097 if (textclasslist.Style(parameters->textclass,
1098 par->GetLayout()).labeltype != LABEL_MANUAL)
1101 return par->BeginningOfMainBody();
1105 /* if there is a selection, reset every environment you can find
1106 * in the selection, otherwise just the environment you are in */
1107 void LyXText::MeltFootnoteEnvironment()
1109 LyXParagraph * tmppar, * firsttmppar;
1113 /* is is only allowed, if the cursor is IN an open footnote.
1114 * Otherwise it is too dangerous */
1115 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1118 SetUndo(Undo::FINISH,
1119 cursor.par->PreviousBeforeFootnote()->previous,
1120 cursor.par->NextAfterFootnote()->next);
1122 /* ok, move to the beginning of the footnote. */
1123 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1124 cursor.par = cursor.par->Previous();
1126 SetCursor(cursor.par, cursor.par->Last());
1127 /* this is just faster than using CursorLeft(); */
1129 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1130 tmppar = firsttmppar;
1131 /* tmppar is now the paragraph right before the footnote */
1133 bool first_footnote_par_is_not_empty = tmppar->next->size();
1136 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1137 tmppar = tmppar->next; /* I use next instead of Next(),
1138 * because there cannot be any
1139 * footnotes in a footnote
1141 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1143 /* remember the captions and empty paragraphs */
1144 if ((textclasslist.Style(parameters->textclass,
1145 tmppar->GetLayout())
1146 .labeltype == LABEL_SENSITIVE)
1148 tmppar->SetLayout(0);
1151 // now we will paste the ex-footnote, if the layouts allow it
1152 // first restore the layout of the paragraph right behind
1155 tmppar->next->MakeSameLayout(cursor.par);
1158 if ((!tmppar->GetLayout() && !tmppar->table)
1160 && (!tmppar->Next()->Last()
1161 || tmppar->Next()->HasSameLayout(tmppar)))) {
1162 if (tmppar->Next()->Last()
1163 && tmppar->Next()->IsLineSeparator(0))
1164 tmppar->Next()->Erase(0);
1165 tmppar->PasteParagraph();
1168 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1169 * by the pasting of the beginning */
1171 /* then the beginning */
1172 /* if there is no space between the text and the footnote, so we insert
1174 * (only if the previous par and the footnotepar are not empty!) */
1175 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1176 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1177 if (firsttmppar->size()
1178 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1179 && first_footnote_par_is_not_empty) {
1180 firsttmppar->next->InsertChar(0, ' ');
1182 firsttmppar->PasteParagraph();
1185 /* now redo the paragaphs */
1186 RedoParagraphs(cursor, tmppar);
1188 SetCursor(cursor.par, cursor.pos);
1190 /* sometimes it can happen, that there is a counter change */
1191 Row * row = cursor.row;
1192 while (row->next && row->par != tmppar && row->next->par != tmppar)
1194 UpdateCounters(row);
1201 /* the DTP switches for paragraphs. LyX will store them in the
1202 * first physicla paragraph. When a paragraph is broken, the top settings
1203 * rest, the bottom settings are given to the new one. So I can make shure,
1204 * they do not duplicate themself and you cannnot make dirty things with
1207 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1208 bool pagebreak_top, bool pagebreak_bottom,
1209 VSpace const & space_top,
1210 VSpace const & space_bottom,
1212 string labelwidthstring,
1215 LyXCursor tmpcursor = cursor;
1217 sel_start_cursor = cursor;
1218 sel_end_cursor = cursor;
1221 // make sure that the depth behind the selection are restored, too
1222 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1223 LyXParagraph * undoendpar = endpar;
1225 if (endpar && endpar->GetDepth()) {
1226 while (endpar && endpar->GetDepth()) {
1227 endpar = endpar->LastPhysicalPar()->Next();
1228 undoendpar = endpar;
1232 endpar = endpar->Next(); // because of parindents etc.
1237 .par->ParFromPos(sel_start_cursor.pos)->previous,
1241 LyXParagraph * tmppar = sel_end_cursor.par;
1242 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1243 SetCursor(tmppar->FirstPhysicalPar(), 0);
1244 status = LyXText::NEED_MORE_REFRESH;
1245 refresh_row = cursor.row;
1246 refresh_y = cursor.y - cursor.row->baseline;
1247 if (cursor.par->footnoteflag ==
1248 sel_start_cursor.par->footnoteflag) {
1249 cursor.par->line_top = line_top;
1250 cursor.par->line_bottom = line_bottom;
1251 cursor.par->pagebreak_top = pagebreak_top;
1252 cursor.par->pagebreak_bottom = pagebreak_bottom;
1253 cursor.par->added_space_top = space_top;
1254 cursor.par->added_space_bottom = space_bottom;
1255 // does the layout allow the new alignment?
1256 if (align == LYX_ALIGN_LAYOUT)
1257 align = textclasslist
1258 .Style(parameters->textclass,
1259 cursor.par->GetLayout()).align;
1260 if (align & textclasslist
1261 .Style(parameters->textclass,
1262 cursor.par->GetLayout()).alignpossible) {
1263 if (align == textclasslist
1264 .Style(parameters->textclass,
1265 cursor.par->GetLayout()).align)
1266 cursor.par->align = LYX_ALIGN_LAYOUT;
1268 cursor.par->align = align;
1270 cursor.par->SetLabelWidthString(labelwidthstring);
1271 cursor.par->noindent = noindent;
1274 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1277 RedoParagraphs(sel_start_cursor, endpar);
1280 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1281 sel_cursor = cursor;
1282 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1284 SetCursor(tmpcursor.par, tmpcursor.pos);
1288 void LyXText::SetParagraphExtraOpt(int type,
1290 char const * widthp,
1291 int alignment, bool hfill,
1292 bool start_minipage)
1294 LyXCursor tmpcursor = cursor;
1295 LyXParagraph * tmppar;
1297 sel_start_cursor = cursor;
1298 sel_end_cursor = cursor;
1301 // make sure that the depth behind the selection are restored, too
1302 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1303 LyXParagraph * undoendpar = endpar;
1305 if (endpar && endpar->GetDepth()) {
1306 while (endpar && endpar->GetDepth()) {
1307 endpar = endpar->LastPhysicalPar()->Next();
1308 undoendpar = endpar;
1312 endpar = endpar->Next(); // because of parindents etc.
1317 .par->ParFromPos(sel_start_cursor.pos)->previous,
1320 tmppar = sel_end_cursor.par;
1321 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1322 SetCursor(tmppar->FirstPhysicalPar(), 0);
1323 status = LyXText::NEED_MORE_REFRESH;
1324 refresh_row = cursor.row;
1325 refresh_y = cursor.y - cursor.row->baseline;
1326 if (cursor.par->footnoteflag ==
1327 sel_start_cursor.par->footnoteflag) {
1328 if (type == LyXParagraph::PEXTRA_NONE) {
1329 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1330 cursor.par->UnsetPExtraType();
1331 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1334 cursor.par->SetPExtraType(type, width, widthp);
1335 cursor.par->pextra_hfill = hfill;
1336 cursor.par->pextra_start_minipage = start_minipage;
1337 cursor.par->pextra_alignment = alignment;
1340 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1342 RedoParagraphs(sel_start_cursor, endpar);
1344 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1345 sel_cursor = cursor;
1346 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1348 SetCursor(tmpcursor.par, tmpcursor.pos);
1352 char loweralphaCounter(int n)
1354 if (n < 1 || n > 26)
1360 char alphaCounter(int n)
1362 if (n < 1 || n > 26)
1368 char hebrewCounter(int n)
1370 static const char hebrew[22] = {
1371 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1372 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1373 '÷', 'ø', 'ù', 'ú'
1375 if (n < 1 || n > 22)
1381 static char const * romanCounter(int n)
1383 static char const * roman[20] = {
1384 "i", "ii", "iii", "iv", "v",
1385 "vi", "vii", "viii", "ix", "x",
1386 "xi", "xii", "xiii", "xiv", "xv",
1387 "xvi", "xvii", "xviii", "xix", "xx"
1389 if (n < 1 || n > 20)
1395 // set the counter of a paragraph. This includes the labels
1396 void LyXText::SetCounter(LyXParagraph * par) const
1398 // this is only relevant for the beginning of paragraph
1399 par = par->FirstPhysicalPar();
1401 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1404 LyXTextClass const & textclass =
1405 textclasslist.TextClass(parameters->textclass);
1407 /* copy the prev-counters to this one, unless this is the start of a
1408 footnote or of a bibliography or the very first paragraph */
1410 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1411 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1412 && par->footnotekind == LyXParagraph::FOOTNOTE)
1413 && !(textclasslist.Style(parameters->textclass,
1414 par->Previous()->GetLayout()
1415 ).labeltype != LABEL_BIBLIO
1416 && layout.labeltype == LABEL_BIBLIO)) {
1417 for (int i = 0; i < 10; ++i) {
1418 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1420 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1421 if (!par->appendix && par->start_of_appendix){
1422 par->appendix = true;
1423 for (int i = 0; i < 10; ++i) {
1424 par->setCounter(i, 0);
1427 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1428 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1431 for (int i = 0; i < 10; ++i) {
1432 par->setCounter(i, 0);
1434 par->appendix = par->start_of_appendix;
1439 // if this is an open marginnote and this is the first
1440 // entry in the marginnote and the enclosing
1441 // environment is an enum/item then correct for the
1442 // LaTeX behaviour (ARRae)
1443 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1444 && par->footnotekind == LyXParagraph::MARGIN
1446 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1447 && (par->PreviousBeforeFootnote()
1448 && textclasslist.Style(parameters->textclass,
1449 par->PreviousBeforeFootnote()->GetLayout()
1450 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1451 // Any itemize or enumerate environment in a marginnote
1452 // that is embedded in an itemize or enumerate
1453 // paragraph is seen by LaTeX as being at a deeper
1454 // level within that enclosing itemization/enumeration
1455 // even if there is a "standard" layout at the start of
1461 /* Maybe we have to increment the enumeration depth.
1462 * BUT, enumeration in a footnote is considered in isolation from its
1463 * surrounding paragraph so don't increment if this is the
1464 * first line of the footnote
1465 * AND, bibliographies can't have their depth changed ie. they
1466 * are always of depth 0
1469 && par->Previous()->GetDepth() < par->GetDepth()
1470 && textclasslist.Style(parameters->textclass,
1471 par->Previous()->GetLayout()
1472 ).labeltype == LABEL_COUNTER_ENUMI
1473 && par->enumdepth < 3
1474 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1475 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1476 && par->footnotekind == LyXParagraph::FOOTNOTE)
1477 && layout.labeltype != LABEL_BIBLIO) {
1481 /* Maybe we have to decrement the enumeration depth, see note above */
1483 && par->Previous()->GetDepth() > par->GetDepth()
1484 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1485 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1486 && par->footnotekind == LyXParagraph::FOOTNOTE)
1487 && layout.labeltype != LABEL_BIBLIO) {
1488 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1489 par->setCounter(6 + par->enumdepth,
1490 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1491 /* reset the counters.
1492 * A depth change is like a breaking layout
1494 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1495 par->setCounter(i, 0);
1498 if (!par->labelstring.empty()) {
1499 par->labelstring.clear();
1502 if (layout.margintype == MARGIN_MANUAL) {
1503 if (par->labelwidthstring.empty()) {
1504 par->SetLabelWidthString(layout.labelstring());
1507 par->SetLabelWidthString(string());
1510 /* is it a layout that has an automatic label ? */
1511 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1513 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1514 if (i >= 0 && i<= parameters->secnumdepth) {
1515 par->incCounter(i); // increment the counter
1517 // Is there a label? Useful for Chapter layout
1518 if (!par->appendix){
1519 if (!layout.labelstring().empty())
1520 par->labelstring = layout.labelstring();
1522 par->labelstring.clear();
1524 if (!layout.labelstring_appendix().empty())
1525 par->labelstring = layout.labelstring_appendix();
1527 par->labelstring.clear();
1535 if (!par->appendix) {
1536 switch (2 * LABEL_FIRST_COUNTER -
1537 textclass.maxcounter() + i) {
1538 case LABEL_COUNTER_CHAPTER:
1539 s << par->getCounter(i);
1541 case LABEL_COUNTER_SECTION:
1542 s << par->getCounter(i - 1) << '.'
1543 << par->getCounter(i);
1545 case LABEL_COUNTER_SUBSECTION:
1546 s << par->getCounter(i - 2) << '.'
1547 << par->getCounter(i - 1) << '.'
1548 << par->getCounter(i);
1550 case LABEL_COUNTER_SUBSUBSECTION:
1551 s << par->getCounter(i - 3) << '.'
1552 << par->getCounter(i - 2) << '.'
1553 << par->getCounter(i - 1) << '.'
1554 << par->getCounter(i);
1557 case LABEL_COUNTER_PARAGRAPH:
1558 s << par->getCounter(i - 4) << '.'
1559 << par->getCounter(i - 3) << '.'
1560 << par->getCounter(i - 2) << '.'
1561 << par->getCounter(i - 1) << '.'
1562 << par->getCounter(i);
1564 case LABEL_COUNTER_SUBPARAGRAPH:
1565 s << par->getCounter(i - 5) << '.'
1566 << par->getCounter(i - 4) << '.'
1567 << par->getCounter(i - 3) << '.'
1568 << par->getCounter(i - 2) << '.'
1569 << par->getCounter(i - 1) << '.'
1570 << par->getCounter(i);
1574 s << par->getCounter(i) << '.';
1577 } else { // appendix
1578 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1579 case LABEL_COUNTER_CHAPTER:
1580 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1581 s << alphaCounter(par->getCounter(i));
1583 s << hebrewCounter(par->getCounter(i));
1585 case LABEL_COUNTER_SECTION:
1586 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1587 s << alphaCounter(par->getCounter(i - 1));
1589 s << hebrewCounter(par->getCounter(i - 1));
1592 << par->getCounter(i);
1595 case LABEL_COUNTER_SUBSECTION:
1596 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1597 s << alphaCounter(par->getCounter(i - 2));
1599 s << hebrewCounter(par->getCounter(i - 2));
1602 << par->getCounter(i-1) << '.'
1603 << par->getCounter(i);
1606 case LABEL_COUNTER_SUBSUBSECTION:
1607 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1608 s << alphaCounter(par->getCounter(i-3));
1610 s << hebrewCounter(par->getCounter(i-3));
1613 << par->getCounter(i-2) << '.'
1614 << par->getCounter(i-1) << '.'
1615 << par->getCounter(i);
1618 case LABEL_COUNTER_PARAGRAPH:
1619 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1620 s << alphaCounter(par->getCounter(i-4));
1622 s << hebrewCounter(par->getCounter(i-4));
1625 << par->getCounter(i-3) << '.'
1626 << par->getCounter(i-2) << '.'
1627 << par->getCounter(i-1) << '.'
1628 << par->getCounter(i);
1631 case LABEL_COUNTER_SUBPARAGRAPH:
1632 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1633 s << alphaCounter(par->getCounter(i-5));
1635 s << hebrewCounter(par->getCounter(i-5));
1638 << par->getCounter(i-4) << '.'
1639 << par->getCounter(i-3) << '.'
1640 << par->getCounter(i-2) << '.'
1641 << par->getCounter(i-1) << '.'
1642 << par->getCounter(i);
1646 // Can this ever be reached? And in the
1647 // case it is, how can this be correct?
1649 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1655 par->labelstring += s.str().c_str();
1656 // We really want to remove the c_str as soon as
1660 char * tmps = s.str();
1661 par->labelstring += tmps;
1665 for (i++; i < 10; ++i) {
1666 // reset the following counters
1667 par->setCounter(i, 0);
1669 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1670 for (i++; i < 10; ++i) {
1671 // reset the following counters
1672 par->setCounter(i, 0);
1674 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1675 par->incCounter(i + par->enumdepth);
1676 int number = par->getCounter(i + par->enumdepth);
1683 switch (par->enumdepth) {
1685 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1687 << loweralphaCounter(number)
1691 << hebrewCounter(number)
1695 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1696 s << romanCounter(number) << '.';
1698 s << '.' << romanCounter(number);
1701 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1702 s << alphaCounter(number)
1706 << alphaCounter(number);
1709 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1716 par->labelstring = s.str().c_str();
1717 // we really want to get rid of that c_str()
1720 char * tmps = s.str();
1721 par->labelstring = tmps;
1725 for (i += par->enumdepth + 1; i < 10; ++i)
1726 par->setCounter(i, 0); /* reset the following counters */
1729 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1730 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1732 int number = par->getCounter(i);
1734 par->bibkey = new InsetBibKey();
1735 par->bibkey->setCounter(number);
1736 par->labelstring = layout.labelstring();
1738 // In biblio should't be following counters but...
1740 string s = layout.labelstring();
1742 // the caption hack:
1744 if (layout.labeltype == LABEL_SENSITIVE) {
1745 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1746 && (par->footnotekind == LyXParagraph::FIG
1747 || par->footnotekind == LyXParagraph::WIDE_FIG))
1748 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1752 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1753 && (par->footnotekind == LyXParagraph::TAB
1754 || par->footnotekind == LyXParagraph::WIDE_TAB))
1755 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1759 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1760 && par->footnotekind == LyXParagraph::ALGORITHM)
1761 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1764 s = ":Ãúéøåâìà ";
1766 /* par->SetLayout(0);
1767 s = layout->labelstring; */
1768 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1771 s = " :úåòîùî øñç";
1775 par->labelstring = s;
1777 /* reset the enumeration counter. They are always resetted
1778 * when there is any other layout between */
1779 for (int i = 6 + par->enumdepth; i < 10; ++i)
1780 par->setCounter(i, 0);
1785 /* Updates all counters BEHIND the row. Changed paragraphs
1786 * with a dynamic left margin will be rebroken. */
1787 void LyXText::UpdateCounters(Row * row) const
1796 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1797 par = row->par->LastPhysicalPar()->Next();
1799 par = row->par->next;
1804 while (row->par != par)
1809 /* now check for the headline layouts. remember that they
1810 * have a dynamic left margin */
1812 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1813 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1816 /* Rebreak the paragraph */
1817 RemoveParagraph(row);
1818 AppendParagraph(row);
1820 /* think about the damned open footnotes! */
1821 while (par->Next() &&
1822 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1823 || par->Next()->IsDummy())){
1825 if (par->IsDummy()) {
1826 while (row->par != par)
1828 RemoveParagraph(row);
1829 AppendParagraph(row);
1834 par = par->LastPhysicalPar()->Next();
1840 /* insets an inset. */
1841 void LyXText::InsertInset(Inset *inset)
1843 SetUndo(Undo::INSERT,
1844 cursor.par->ParFromPos(cursor.pos)->previous,
1845 cursor.par->ParFromPos(cursor.pos)->next);
1846 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1847 cursor.par->InsertInset(cursor.pos, inset);
1848 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1849 * The character will not be inserted a
1854 // this is for the simple cut and paste mechanism
1855 static LyXParagraph * simple_cut_buffer = 0;
1856 static char simple_cut_buffer_textclass = 0;
1858 void DeleteSimpleCutBuffer()
1860 if (!simple_cut_buffer)
1862 LyXParagraph * tmppar;
1864 while (simple_cut_buffer) {
1865 tmppar = simple_cut_buffer;
1866 simple_cut_buffer = simple_cut_buffer->next;
1869 simple_cut_buffer = 0;
1873 void LyXText::copyEnvironmentType()
1875 copylayouttype = cursor.par->GetLayout();
1879 void LyXText::pasteEnvironmentType()
1881 SetLayout(copylayouttype);
1885 void LyXText::CutSelection(bool doclear)
1887 // This doesn't make sense, if there is no selection
1891 // OK, we have a selection. This is always between sel_start_cursor
1892 // and sel_end cursor
1893 LyXParagraph * tmppar;
1895 // Check whether there are half footnotes in the selection
1896 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1897 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1898 tmppar = sel_start_cursor.par;
1899 while (tmppar != sel_end_cursor.par){
1900 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1901 WriteAlert(_("Impossible operation"),
1902 _("Don't know what to do with half floats."),
1906 tmppar = tmppar->Next();
1910 /* table stuff -- begin */
1911 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1912 if ( sel_start_cursor.par != sel_end_cursor.par) {
1913 WriteAlert(_("Impossible operation"),
1914 _("Don't know what to do with half tables."),
1918 sel_start_cursor.par->table->Reinit();
1920 /* table stuff -- end */
1922 // make sure that the depth behind the selection are restored, too
1923 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1924 LyXParagraph * undoendpar = endpar;
1926 if (endpar && endpar->GetDepth()) {
1927 while (endpar && endpar->GetDepth()) {
1928 endpar = endpar->LastPhysicalPar()->Next();
1929 undoendpar = endpar;
1931 } else if (endpar) {
1932 endpar = endpar->Next(); // because of parindents etc.
1935 SetUndo(Undo::DELETE,
1937 .par->ParFromPos(sel_start_cursor.pos)->previous,
1940 // clear the simple_cut_buffer
1941 DeleteSimpleCutBuffer();
1943 // set the textclass
1944 simple_cut_buffer_textclass = parameters->textclass;
1946 #ifdef WITH_WARNINGS
1947 #warning Asger: Make cut more intelligent here.
1950 White paper for "intelligent" cutting:
1952 Example: "This is our text."
1953 Using " our " as selection, cutting will give "This istext.".
1954 Using "our" as selection, cutting will give "This is text.".
1955 Using " our" as selection, cutting will give "This is text.".
1956 Using "our " as selection, cutting will give "This is text.".
1958 All those four selections will (however) paste identically:
1959 Pasting with the cursor right after the "is" will give the
1960 original text with all four selections.
1962 The rationale is to be intelligent such that words are copied,
1963 cut and pasted in a functional manner.
1965 This is not implemented yet. (Asger)
1967 The changes below sees to do a lot of what you want. However
1968 I have not verified that all cases work as they should:
1970 - cut in multiple row
1972 - cut across footnotes and paragraph
1973 My simplistic tests show that the idea are basically sound but
1974 there are some items to fix up...we only need to find them
1977 As do redo Asger's example above (with | beeing the cursor in the
1978 result after cutting.):
1980 Example: "This is our text."
1981 Using " our " as selection, cutting will give "This is|text.".
1982 Using "our" as selection, cutting will give "This is | text.".
1983 Using " our" as selection, cutting will give "This is| text.".
1984 Using "our " as selection, cutting will give "This is |text.".
1989 #ifndef FIX_DOUBLE_SPACE
1990 bool space_wrapped =
1991 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1992 if (sel_end_cursor.pos > 0
1993 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1994 // please break before a space at the end
1995 sel_end_cursor.pos--;
1996 space_wrapped = true;
1998 // cut behind a space if there is one
1999 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2000 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2001 && (sel_start_cursor.par != sel_end_cursor.par
2002 || sel_start_cursor.pos < sel_end_cursor.pos))
2003 sel_start_cursor.pos++;
2005 // there are two cases: cut only within one paragraph or
2006 // more than one paragraph
2008 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2009 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2010 // only within one paragraph
2011 simple_cut_buffer = new LyXParagraph;
2012 LyXParagraph::size_type i =
2013 sel_start_cursor.pos;
2014 for (; i < sel_end_cursor.pos; ++i) {
2015 /* table stuff -- begin */
2016 if (sel_start_cursor.par->table
2017 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2018 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2019 sel_start_cursor.pos++;
2021 /* table stuff -- end */
2022 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2023 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2025 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2027 #ifndef FIX_DOUBLE_SPACE
2028 // check for double spaces
2029 if (sel_start_cursor.pos &&
2030 sel_start_cursor.par->Last() > sel_start_cursor.pos
2031 && sel_start_cursor.par
2032 ->IsLineSeparator(sel_start_cursor.pos - 1)
2033 && sel_start_cursor.par
2034 ->IsLineSeparator(sel_start_cursor.pos)) {
2035 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2038 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
2041 endpar = sel_end_cursor.par->Next();
2043 // cut more than one paragraph
2046 ->BreakParagraphConservative(sel_end_cursor.pos);
2047 #ifndef FIX_DOUBLE_SPACE
2048 // insert a space at the end if there was one
2051 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2053 sel_end_cursor.par = sel_end_cursor.par->Next();
2054 sel_end_cursor.pos = 0;
2056 cursor = sel_end_cursor;
2058 #ifndef FIX_DOUBLE_SPACE
2059 // please break behind a space, if there is one.
2060 // The space should be copied too
2061 if (sel_start_cursor.par
2062 ->IsLineSeparator(sel_start_cursor.pos))
2063 sel_start_cursor.pos++;
2065 sel_start_cursor.par
2066 ->BreakParagraphConservative(sel_start_cursor.pos);
2067 #ifndef FIX_DOUBLE_SPACE
2068 if (!sel_start_cursor.pos
2069 || sel_start_cursor.par
2070 ->IsLineSeparator(sel_start_cursor.pos - 1)
2071 || sel_start_cursor.par
2072 ->IsNewline(sel_start_cursor.pos - 1)) {
2073 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2076 // store the endparagraph for redoing later
2077 endpar = sel_end_cursor.par->Next(); /* needed because
2082 // store the selection
2083 simple_cut_buffer = sel_start_cursor.par
2084 ->ParFromPos(sel_start_cursor.pos)->next;
2085 simple_cut_buffer->previous = 0;
2086 sel_end_cursor.par->previous->next = 0;
2088 // cut the selection
2089 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2090 = sel_end_cursor.par;
2092 sel_end_cursor.par->previous
2093 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2095 // care about footnotes
2096 if (simple_cut_buffer->footnoteflag) {
2097 LyXParagraph * tmppar = simple_cut_buffer;
2099 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2100 tmppar = tmppar->next;
2104 // the cut selection should begin with standard layout
2105 simple_cut_buffer->Clear();
2107 // paste the paragraphs again, if possible
2109 sel_start_cursor.par->Next()->ClearParagraph();
2110 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2112 !sel_start_cursor.par->Next()->Last())
2113 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2115 #ifndef FIX_DOUBLE_SPACE
2116 // maybe a forgotten blank
2117 if (sel_start_cursor.pos
2118 && sel_start_cursor.par
2119 ->IsLineSeparator(sel_start_cursor.pos)
2120 && sel_start_cursor.par
2121 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2122 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2127 // sometimes necessary
2129 sel_start_cursor.par->ClearParagraph();
2131 RedoParagraphs(sel_start_cursor, endpar);
2134 cursor = sel_start_cursor;
2135 SetCursor(cursor.par, cursor.pos);
2136 sel_cursor = cursor;
2137 UpdateCounters(cursor.row);
2141 void LyXText::CopySelection()
2143 // this doesnt make sense, if there is no selection
2147 // ok we have a selection. This is always between sel_start_cursor
2148 // and sel_end cursor
2149 LyXParagraph * tmppar;
2151 /* check wether there are half footnotes in the selection */
2152 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2153 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2154 tmppar = sel_start_cursor.par;
2155 while (tmppar != sel_end_cursor.par) {
2156 if (tmppar->footnoteflag !=
2157 sel_end_cursor.par->footnoteflag) {
2158 WriteAlert(_("Impossible operation"),
2159 _("Don't know what to do"
2160 " with half floats."),
2164 tmppar = tmppar->Next();
2168 /* table stuff -- begin */
2169 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2170 if ( sel_start_cursor.par != sel_end_cursor.par){
2171 WriteAlert(_("Impossible operation"),
2172 _("Don't know what to do with half tables."),
2177 /* table stuff -- end */
2179 // delete the simple_cut_buffer
2180 DeleteSimpleCutBuffer();
2182 // set the textclass
2183 simple_cut_buffer_textclass = parameters->textclass;
2185 #ifdef FIX_DOUBLE_SPACE
2186 // copy behind a space if there is one
2187 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2188 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2189 && (sel_start_cursor.par != sel_end_cursor.par
2190 || sel_start_cursor.pos < sel_end_cursor.pos))
2191 sel_start_cursor.pos++;
2193 // there are two cases: copy only within one paragraph
2194 // or more than one paragraph
2195 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2196 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2197 // only within one paragraph
2198 simple_cut_buffer = new LyXParagraph;
2199 LyXParagraph::size_type i = 0;
2200 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2201 sel_start_cursor.par->CopyIntoMinibuffer(i);
2202 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2205 // copy more than one paragraph
2206 // clone the paragraphs within the selection
2208 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2209 simple_cut_buffer = tmppar->Clone();
2210 LyXParagraph *tmppar2 = simple_cut_buffer;
2212 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2214 tmppar = tmppar->next;
2215 tmppar2->next = tmppar->Clone();
2216 tmppar2->next->previous = tmppar2;
2217 tmppar2 = tmppar2->next;
2221 // care about footnotes
2222 if (simple_cut_buffer->footnoteflag) {
2223 tmppar = simple_cut_buffer;
2225 tmppar->footnoteflag =
2226 LyXParagraph::NO_FOOTNOTE;
2227 tmppar = tmppar->next;
2231 // the simple_cut_buffer paragraph is too big
2232 LyXParagraph::size_type tmpi2 =
2233 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2234 for (; tmpi2; --tmpi2)
2235 simple_cut_buffer->Erase(0);
2237 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2239 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2240 while (tmppar2->size() > tmpi2) {
2241 tmppar2->Erase(tmppar2->size() - 1);
2247 void LyXText::PasteSelection()
2249 // this does not make sense, if there is nothing to paste
2250 if (!simple_cut_buffer)
2253 LyXParagraph * tmppar;
2254 LyXParagraph * endpar;
2256 LyXCursor tmpcursor;
2258 // be carefull with footnotes in footnotes
2259 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2261 // check whether the cut_buffer includes a footnote
2262 tmppar = simple_cut_buffer;
2264 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2265 tmppar = tmppar->next;
2268 WriteAlert(_("Impossible operation"),
2269 _("Can't paste float into float!"),
2275 /* table stuff -- begin */
2276 if (cursor.par->table) {
2277 if (simple_cut_buffer->next) {
2278 WriteAlert(_("Impossible operation"),
2279 _("Table cell cannot include more than one paragraph!"),
2284 /* table stuff -- end */
2286 SetUndo(Undo::INSERT,
2287 cursor.par->ParFromPos(cursor.pos)->previous,
2288 cursor.par->ParFromPos(cursor.pos)->next);
2292 // There are two cases: cutbuffer only one paragraph or many
2293 if (!simple_cut_buffer->next) {
2294 // only within a paragraph
2296 #ifndef FIX_DOUBLE_SPACE
2297 // please break behind a space, if there is one
2298 while (tmpcursor.par->Last() > tmpcursor.pos
2299 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2302 tmppar = simple_cut_buffer->Clone();
2303 /* table stuff -- begin */
2304 bool table_too_small = false;
2305 if (tmpcursor.par->table) {
2306 while (simple_cut_buffer->size()
2307 && !table_too_small) {
2308 if (simple_cut_buffer->IsNewline(0)){
2309 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2311 simple_cut_buffer->Erase(0);
2312 if (tmpcursor.pos < tmpcursor.par->Last())
2315 table_too_small = true;
2317 #ifdef FIX_DOUBLE_SPACE
2318 // This is an attempt to fix the
2319 // "never insert a space at the
2320 // beginning of a paragraph" problem.
2321 if (tmpcursor.pos == 0
2322 && simple_cut_buffer->IsLineSeparator(0)) {
2323 simple_cut_buffer->Erase(0);
2325 simple_cut_buffer->CutIntoMinibuffer(0);
2326 simple_cut_buffer->Erase(0);
2327 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2331 simple_cut_buffer->CutIntoMinibuffer(0);
2332 simple_cut_buffer->Erase(0);
2333 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2339 /* table stuff -- end */
2340 // Some provisions should be done here for checking
2341 // if we are inserting at the beginning of a
2342 // paragraph. If there are a space at the beginning
2343 // of the text to insert and we are inserting at
2344 // the beginning of the paragraph the space should
2346 while (simple_cut_buffer->size()) {
2347 #ifdef FIX_DOUBLE_SPACE
2348 // This is an attempt to fix the
2349 // "never insert a space at the
2350 // beginning of a paragraph" problem.
2351 if (tmpcursor.pos == 0
2352 && simple_cut_buffer->IsLineSeparator(0)) {
2353 simple_cut_buffer->Erase(0);
2355 simple_cut_buffer->CutIntoMinibuffer(0);
2356 simple_cut_buffer->Erase(0);
2357 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2361 simple_cut_buffer->CutIntoMinibuffer(0);
2362 simple_cut_buffer->Erase(0);
2363 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2368 delete simple_cut_buffer;
2369 simple_cut_buffer = tmppar;
2370 endpar = tmpcursor.par->Next();
2374 // make a copy of the simple cut_buffer
2375 tmppar = simple_cut_buffer;
2376 LyXParagraph * simple_cut_clone = tmppar->Clone();
2377 LyXParagraph * tmppar2 = simple_cut_clone;
2378 if (cursor.par->footnoteflag){
2379 tmppar->footnoteflag = cursor.par->footnoteflag;
2380 tmppar->footnotekind = cursor.par->footnotekind;
2382 while (tmppar->next) {
2383 tmppar = tmppar->next;
2384 tmppar2->next = tmppar->Clone();
2385 tmppar2->next->previous = tmppar2;
2386 tmppar2 = tmppar2->next;
2387 if (cursor.par->footnoteflag){
2388 tmppar->footnoteflag = cursor.par->footnoteflag;
2389 tmppar->footnotekind = cursor.par->footnotekind;
2393 // make sure there is no class difference
2394 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2395 parameters->textclass,
2398 // make the simple_cut_buffer exactly the same layout than
2399 // the cursor paragraph
2400 simple_cut_buffer->MakeSameLayout(cursor.par);
2402 // find the end of the buffer
2403 LyXParagraph * lastbuffer = simple_cut_buffer;
2404 while (lastbuffer->Next())
2405 lastbuffer = lastbuffer->Next();
2407 #ifndef FIX_DOUBLE_SPACE
2408 // Please break behind a space, if there is one. The space
2409 // should be copied too.
2410 if (cursor.par->Last() > cursor.pos
2411 && cursor.par->IsLineSeparator(cursor.pos))
2414 bool paste_the_end = false;
2416 // open the paragraph for inserting the simple_cut_buffer
2418 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2419 cursor.par->BreakParagraphConservative(cursor.pos);
2420 paste_the_end = true;
2423 #ifndef FIX_DOUBLE_SPACE
2424 // be careful with double spaces
2425 if ((!cursor.par->Last()
2426 || cursor.par->IsLineSeparator(cursor.pos - 1)
2427 || cursor.par->IsNewline(cursor.pos - 1))
2428 && simple_cut_buffer->text.size()
2429 && simple_cut_buffer->IsLineSeparator(0))
2430 simple_cut_buffer->Erase(0);
2432 // set the end for redoing later
2433 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2436 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2437 cursor.par->ParFromPos(cursor.pos)->next;
2438 cursor.par->ParFromPos(cursor.pos)->next->previous =
2439 lastbuffer->ParFromPos(lastbuffer->Last());
2441 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2442 simple_cut_buffer->previous =
2443 cursor.par->ParFromPos(cursor.pos);
2445 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2446 lastbuffer = cursor.par;
2448 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2450 // store the new cursor position
2451 tmpcursor.par = lastbuffer;
2452 tmpcursor.pos = lastbuffer->Last();
2454 // maybe some pasting
2455 if (lastbuffer->Next() && paste_the_end) {
2456 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2457 #ifndef FIX_DOUBLE_SPACE
2458 // be careful with double spaces
2459 if ((!lastbuffer->Last()
2460 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2461 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2462 && lastbuffer->Next()->Last()
2463 && lastbuffer->Next()->IsLineSeparator(0))
2464 lastbuffer->Next()->Erase(0);
2466 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2468 } else if (!lastbuffer->Next()->Last()) {
2469 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2470 #ifndef FIX_DOUBLE_SPACE
2471 // be careful witth double spaces
2472 if ((!lastbuffer->Last()
2473 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2474 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2475 && lastbuffer->Next()->Last()
2476 && lastbuffer->Next()->IsLineSeparator(0))
2477 lastbuffer->Next()->Erase(0);
2479 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2481 } else if (!lastbuffer->Last()) {
2482 lastbuffer->MakeSameLayout(lastbuffer->next);
2483 #ifndef FIX_DOUBLE_SPACE
2484 // be careful witth double spaces
2485 if ((!lastbuffer->Last()
2486 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2487 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2488 && lastbuffer->Next()->Last()
2489 && lastbuffer->Next()->IsLineSeparator(0))
2490 lastbuffer->Next()->Erase(0);
2492 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2495 lastbuffer->Next()->ClearParagraph();
2498 // restore the simple cut buffer
2499 simple_cut_buffer = simple_cut_clone;
2502 RedoParagraphs(cursor, endpar);
2504 SetCursor(cursor.par, cursor.pos);
2507 sel_cursor = cursor;
2508 SetCursor(tmpcursor.par, tmpcursor.pos);
2510 UpdateCounters(cursor.row);
2514 // returns a pointer to the very first LyXParagraph
2515 LyXParagraph * LyXText::FirstParagraph() const
2517 return params->paragraph;
2521 // returns true if the specified string is at the specified position
2522 bool LyXText::IsStringInText(LyXParagraph * par,
2523 LyXParagraph::size_type pos,
2524 char const * str) const
2528 while (pos + i < par->Last() && str[i] &&
2529 str[i] == par->GetChar(pos + i)) {
2539 // sets the selection over the number of characters of string, no check!!
2540 void LyXText::SetSelectionOverString(char const * string)
2542 sel_cursor = cursor;
2543 for (int i = 0; string[i]; ++i)
2549 // simple replacing. The font of the first selected character is used
2550 void LyXText::ReplaceSelectionWithString(char const * str)
2555 if (!selection) { // create a dummy selection
2556 sel_end_cursor = cursor;
2557 sel_start_cursor = cursor;
2560 // Get font setting before we cut
2561 LyXParagraph::size_type pos = sel_end_cursor.pos;
2562 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2564 // Insert the new string
2565 for (int i = 0; str[i]; ++i) {
2566 sel_end_cursor.par->InsertChar(pos, str[i]);
2567 sel_end_cursor.par->SetFont(pos, font);
2571 // Cut the selection
2578 // if the string can be found: return true and set the cursor to
2580 bool LyXText::SearchForward(char const * str) const
2582 LyXParagraph * par = cursor.par;
2583 LyXParagraph::size_type pos = cursor.pos;
2584 while (par && !IsStringInText(par, pos, str)) {
2585 if (pos < par->Last() - 1)
2593 SetCursor(par, pos);
2601 bool LyXText::SearchBackward(char const * string) const
2603 LyXParagraph * par = cursor.par;
2604 int pos = cursor.pos;
2610 // We skip empty paragraphs (Asger)
2612 par = par->Previous();
2614 pos = par->Last() - 1;
2615 } while (par && pos < 0);
2617 } while (par && !IsStringInText(par, pos, string));
2620 SetCursor(par, pos);
2627 // needed to insert the selection
2628 void LyXText::InsertStringA(string const & str)
2630 LyXParagraph * par = cursor.par;
2631 LyXParagraph::size_type pos = cursor.pos;
2632 LyXParagraph::size_type a = 0;
2634 LyXParagraph * endpar = cursor.par->Next();
2639 textclasslist.Style(parameters->textclass,
2640 cursor.par->GetLayout()).isEnvironment();
2641 // only to be sure, should not be neccessary
2644 // insert the string, don't insert doublespace
2645 string::size_type i = 0;
2646 while (i < str.length()) {
2647 if (str[i] != '\n') {
2649 && i + 1 < str.length() && str[i + 1] != ' '
2650 && pos && par->GetChar(pos - 1)!= ' ') {
2651 par->InsertChar(pos,' ');
2652 par->SetFont(pos, current_font);
2654 } else if (par->table) {
2655 if (str[i] == '\t') {
2656 while((pos < par->size()) &&
2657 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2659 if (pos < par->size())
2661 else // no more fields to fill skip the rest
2663 } else if ((str[i] != 13) &&
2664 ((str[i] & 127) >= ' ')) {
2665 par->InsertChar(pos, str[i]);
2666 par->SetFont(pos, current_font);
2669 } else if (str[i] == ' ') {
2671 InsetSpecialChar * new_inset =
2672 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2673 par->InsertChar(pos, LyXParagraph::META_INSET);
2674 par->SetFont(pos, current_font);
2675 par->InsertInset(pos, new_inset);
2677 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2678 par->SetFont(pos, current_font);
2681 } else if (str[i] == '\t') {
2682 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2684 InsetSpecialChar * new_inset =
2685 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2686 par->InsertChar(pos, LyXParagraph::META_INSET);
2687 par->SetFont(pos, current_font);
2688 par->InsertInset(pos, new_inset);
2690 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2691 par->SetFont(a, current_font);
2695 } else if (str[i] != 13 &&
2696 // Ignore unprintables
2697 (str[i] & 127) >= ' ') {
2698 par->InsertChar(pos, str[i]);
2699 par->SetFont(pos, current_font);
2704 if (i + 1 >= str.length()) {
2708 while((pos < par->size()) &&
2709 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2712 cell = NumberOfCell(par, pos);
2713 while((pos < par->size()) &&
2714 !(par->table->IsFirstCell(cell))) {
2716 while((pos < par->size()) &&
2717 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2720 cell = NumberOfCell(par, pos);
2722 if (pos >= par->size())
2723 // no more fields to fill skip the rest
2726 if (!par->size()) { // par is empty
2728 InsetSpecialChar * new_inset =
2729 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2730 par->InsertChar(pos, LyXParagraph::META_INSET);
2731 par->SetFont(pos, current_font);
2732 par->InsertInset(pos, new_inset);
2734 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2735 par->SetFont(pos, current_font);
2739 par->BreakParagraph(pos, flag);
2747 RedoParagraphs(cursor, endpar);
2748 SetCursor(cursor.par, cursor.pos);
2749 sel_cursor = cursor;
2750 SetCursor(par, pos);
2755 /* turns double-CR to single CR, others where converted into one blank and 13s
2756 * that are ignored .Double spaces are also converted into one. Spaces at
2757 * the beginning of a paragraph are forbidden. tabs are converted into one
2758 * space. then InsertStringA is called */
2759 void LyXText::InsertStringB(string const & s)
2762 LyXParagraph * par = cursor.par;
2763 string::size_type i = 1;
2764 while (i < str.length()) {
2765 if (str[i] == '\t' && !par->table)
2767 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2769 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2770 if (str[i + 1] != '\n') {
2771 if (str[i - 1] != ' ')
2776 while (i + 1 < str.length()
2777 && (str[i + 1] == ' '
2778 || str[i + 1] == '\t'
2779 || str[i + 1] == '\n'
2780 || str[i + 1] == 13)) {
2791 bool LyXText::GotoNextError() const
2793 LyXCursor res = cursor;
2795 if (res.pos < res.par->Last() - 1) {
2799 res.par = res.par->Next();
2804 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2805 && res.par->GetInset(res.pos)->AutoDelete()));
2808 SetCursor(res.par, res.pos);
2815 bool LyXText::GotoNextNote() const
2817 LyXCursor res = cursor;
2819 if (res.pos < res.par->Last() - 1) {
2822 res.par = res.par->Next();
2827 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2828 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2831 SetCursor(res.par, res.pos);
2838 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2839 LyXTextClassList::size_type class2,
2843 if (!par || class1 == class2)
2845 par = par->FirstPhysicalPar();
2847 string name = textclasslist.NameOfLayout(class1, par->layout);
2849 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2850 textclasslist.NumberOfLayout(class2, name);
2853 } else { // layout not found
2854 // use default layout "Standard" (0)
2859 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2861 string s = "Layout had to be changed from\n"
2862 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2863 + "\nbecause of class conversion from\n"
2864 + textclasslist.NameOfClass(class1) + " to "
2865 + textclasslist.NameOfClass(class2);
2866 InsetError * new_inset = new InsetError(s);
2867 par->InsertChar(0, LyXParagraph::META_INSET);
2868 par->InsertInset(0, new_inset);
2877 void LyXText::CheckParagraph(LyXParagraph * par,
2878 LyXParagraph::size_type pos)
2881 LyXCursor tmpcursor;
2883 /* table stuff -- begin*/
2886 CheckParagraphInTable(par, pos);
2889 /* table stuff -- end*/
2892 LyXParagraph::size_type z;
2893 Row * row = GetRow(par, pos, y);
2895 // is there a break one row above
2896 if (row->previous && row->previous->par == row->par) {
2897 z = NextBreakPoint(row->previous, paperwidth);
2898 if ( z >= row->pos) {
2899 // set the dimensions of the row above
2900 y -= row->previous->height;
2902 refresh_row = row->previous;
2903 status = LyXText::NEED_MORE_REFRESH;
2905 BreakAgain(row->previous);
2907 // set the cursor again. Otherwise
2908 // dangling pointers are possible
2909 SetCursor(cursor.par, cursor.pos);
2910 sel_cursor = cursor;
2915 int tmpheight = row->height;
2916 LyXParagraph::size_type tmplast = RowLast(row);
2921 if (row->height == tmpheight && RowLast(row) == tmplast)
2922 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2924 status = LyXText::NEED_MORE_REFRESH;
2926 // check the special right address boxes
2927 if (textclasslist.Style(parameters->textclass,
2928 par->GetLayout()).margintype
2929 == MARGIN_RIGHT_ADDRESS_BOX) {
2930 tmpcursor.par = par;
2931 tmpcursor.row = row;
2934 tmpcursor.x_fix = 0;
2935 tmpcursor.pos = pos;
2936 RedoDrawingOfParagraph(tmpcursor);
2941 // set the cursor again. Otherwise dangling pointers are possible
2942 // also set the selection
2946 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2947 sel_cursor = cursor;
2948 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2949 sel_start_cursor = cursor;
2950 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2951 sel_end_cursor = cursor;
2952 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2953 last_sel_cursor = cursor;
2956 SetCursorIntern(cursor.par, cursor.pos);
2960 // returns 0 if inset wasn't found
2961 int LyXText::UpdateInset(Inset * inset)
2963 // first check the current paragraph
2964 int pos = cursor.par->GetPositionOfInset(inset);
2966 CheckParagraph(cursor.par, pos);
2970 // check every paragraph
2972 LyXParagraph * par = FirstParagraph();
2974 // make sure the paragraph is open
2975 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2976 pos = par->GetPositionOfInset(inset);
2978 CheckParagraph(par, pos);
2989 void LyXText::SetCursor(LyXParagraph * par,
2990 LyXParagraph::size_type pos, bool setfont) const
2992 LyXCursor old_cursor = cursor;
2993 SetCursorIntern(par, pos, setfont);
2994 DeleteEmptyParagraphMechanism(old_cursor);
2998 void LyXText::SetCursorIntern(LyXParagraph * par,
2999 LyXParagraph::size_type pos, bool setfont) const
3001 // correct the cursor position if impossible
3002 if (pos > par->Last()){
3003 LyXParagraph * tmppar = par->ParFromPos(pos);
3004 pos = par->PositionInParFromPos(pos);
3007 if (par->IsDummy() && par->previous &&
3008 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3009 while (par->previous &&
3010 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3011 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3012 par = par->previous ;
3013 if (par->IsDummy() &&
3014 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3015 pos += par->size() + 1;
3017 if (par->previous) {
3018 par = par->previous;
3020 pos += par->size() + 1;
3028 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3029 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3030 && !cursor.par->IsSeparator(cursor.pos))
3031 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3033 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3034 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3036 current_font = cursor.par->GetFontSettings(cursor.pos);
3037 real_current_font = GetFont(cursor.par, cursor.pos);
3040 /* get the cursor y position in text */
3042 Row * row = GetRow(par, pos, y);
3043 /* y is now the beginning of the cursor row */
3045 /* y is now the cursor baseline */
3048 /* now get the cursors x position */
3050 float fill_separator, fill_hfill, fill_label_hfill;
3051 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3052 LyXParagraph::size_type cursor_vpos;
3053 LyXParagraph::size_type last = RowLast(row);
3054 if (row->pos > last)
3056 else if (pos > last)
3057 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3058 ? log2vis(last)+1 : log2vis(last);
3060 LyXDirection letter_direction =
3061 row->par->getLetterDirection(pos);
3062 LyXDirection font_direction =
3063 (real_current_font.isVisibleRightToLeft())
3064 ? LYX_DIR_RIGHT_TO_LEFT : LYX_DIR_LEFT_TO_RIGHT;
3065 if (letter_direction == font_direction
3067 || (row->par->table && row->par->IsNewline(pos-1)))
3068 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3069 ? log2vis(pos) : log2vis(pos) + 1;
3071 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3072 ? log2vis(pos-1) + 1 : log2vis(pos - 1);
3075 /* table stuff -- begin*/
3076 if (row->par->table) {
3077 int cell = NumberOfCell(row->par, row->pos);
3079 x += row->par->table->GetBeginningOfTextInCell(cell);
3080 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3081 pos = vis2log(vpos);
3082 if (row->par->IsNewline(pos)) {
3083 x = x_old + row->par->table->WidthOfColumn(cell);
3086 x += row->par->table->GetBeginningOfTextInCell(cell);
3088 x += SingleWidth(row->par, pos);
3092 /* table stuff -- end*/
3093 LyXParagraph::size_type main_body =
3094 BeginningOfMainBody(row->par);
3095 if (main_body > 0 &&
3096 (main_body-1 > last ||
3097 !row->par->IsLineSeparator(main_body-1)))
3100 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3101 pos = vis2log(vpos);
3102 if (main_body > 0 && pos == main_body-1) {
3103 x += fill_label_hfill +
3104 GetFont(row->par, -2).stringWidth(
3105 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3106 if (row->par->IsLineSeparator(main_body-1))
3107 x -= SingleWidth(row->par, main_body-1);
3110 if (HfillExpansion(row, pos)) {
3111 x += SingleWidth(row->par, pos);
3112 if (pos >= main_body)
3115 x += fill_label_hfill;
3117 else if (row->par->IsSeparator(pos)) {
3120 row->next->par != row->par ||
3121 row->par->getParDirection() ==
3122 row->par->getLetterDirection(last)) {
3123 x += SingleWidth(row->par, pos);
3124 if (pos >= main_body)
3125 x += fill_separator;
3128 x += SingleWidth(row->par, pos);
3134 cursor.x_fix = cursor.x;
3139 void LyXText::SetCursorFromCoordinates(int x, long y) const
3141 LyXCursor old_cursor = cursor;
3143 /* get the row first */
3145 Row * row = GetRowNearY(y);
3147 cursor.par = row->par;
3149 int column = GetColumnNearX(row, x);
3150 cursor.pos = row->pos + column;
3152 cursor.y = y + row->baseline;
3157 (cursor.pos == cursor.par->Last()
3158 || cursor.par->IsSeparator(cursor.pos)
3159 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3160 && !cursor.par->IsSeparator(cursor.pos))
3161 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3163 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3164 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3166 current_font = cursor.par->GetFontSettings(cursor.pos);
3167 real_current_font = GetFont(cursor.par, cursor.pos);
3169 DeleteEmptyParagraphMechanism(old_cursor);
3172 void LyXText::SetCursorFromCoordinates(LyXCursor & cur, int x, long y) const
3174 /* get the row first */
3176 Row * row = GetRowNearY(y);
3177 int column = GetColumnNearX(row, x);
3180 cur.pos = row->pos + column;
3182 cur.y = y + row->baseline;
3187 void LyXText::CursorLeft() const
3190 if (cursor.par->table) {
3191 int cell = NumberOfCell(cursor.par, cursor.pos);
3192 if (cursor.par->table->IsContRow(cell) &&
3193 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3200 void LyXText::CursorLeftIntern() const
3202 if (cursor.pos > 0) {
3203 SetCursor(cursor.par, cursor.pos - 1);
3205 else if (cursor.par->Previous()) { // steps into the above paragraph.
3206 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3211 void LyXText::CursorRight() const
3213 CursorRightIntern();
3214 if (cursor.par->table) {
3215 int cell = NumberOfCell(cursor.par, cursor.pos);
3216 if (cursor.par->table->IsContRow(cell) &&
3217 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3224 void LyXText::CursorRightIntern() const
3226 if (cursor.pos < cursor.par->Last()) {
3227 SetCursor(cursor.par, cursor.pos + 1);
3229 else if (cursor.par->Next()) {
3230 SetCursor(cursor.par->Next(), 0);
3235 void LyXText::CursorUp() const
3237 SetCursorFromCoordinates(cursor.x_fix,
3238 cursor.y - cursor.row->baseline - 1);
3239 if (cursor.par->table) {
3240 int cell = NumberOfCell(cursor.par, cursor.pos);
3241 if (cursor.par->table->IsContRow(cell) &&
3242 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3249 void LyXText::CursorDown() const
3251 if (cursor.par->table &&
3252 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3255 SetCursorFromCoordinates(cursor.x_fix,
3256 cursor.y - cursor.row->baseline
3257 + cursor.row->height + 1);
3258 if (cursor.par->table) {
3259 int cell = NumberOfCell(cursor.par, cursor.pos);
3260 int cell_above = cursor.par->table->GetCellAbove(cell);
3261 while(cursor.par->table &&
3262 cursor.par->table->IsContRow(cell) &&
3263 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3264 SetCursorFromCoordinates(cursor.x_fix,
3265 cursor.y - cursor.row->baseline
3266 + cursor.row->height + 1);
3267 if (cursor.par->table) {
3268 cell = NumberOfCell(cursor.par, cursor.pos);
3269 cell_above = cursor.par->table->GetCellAbove(cell);
3276 void LyXText::CursorUpParagraph() const
3278 if (cursor.pos > 0) {
3279 SetCursor(cursor.par, 0);
3281 else if (cursor.par->Previous()) {
3282 SetCursor(cursor.par->Previous(), 0);
3287 void LyXText::CursorDownParagraph() const
3289 if (cursor.par->Next()) {
3290 SetCursor(cursor.par->Next(), 0);
3292 SetCursor(cursor.par, cursor.par->Last());
3298 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3300 DebugTracer trc1("1");
3302 // Would be wrong to delete anything if we have a selection.
3303 if (selection) return;
3305 // We allow all kinds of "mumbo-jumbo" when freespacing.
3306 if (textclasslist.Style(parameters->textclass,
3307 old_cursor.par->GetLayout()).free_spacing)
3310 bool deleted = false;
3312 #ifdef FIX_DOUBLE_SPACE
3313 /* Ok I'll put some comments here about what is missing.
3314 I have fixed BackSpace (and thus Delete) to not delete
3315 double-spaces automagically. I have also changed Cut,
3316 Copy and Paste to hopefully do some sensible things.
3317 There are still some small problems that can lead to
3318 double spaces stored in the document file or space at
3319 the beginning of paragraphs. This happens if you have
3320 the cursor betwenn to spaces and then save. Or if you
3321 cut and paste and the selection have a space at the
3322 beginning and then save right after the paste. I am
3323 sure none of these are very hard to fix, but I will
3324 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3325 that I can get some feedback. (Lgb)
3328 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3329 // delete the LineSeparator.
3332 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3333 // delete the LineSeparator.
3336 // If the pos around the old_cursor were spaces, delete one of them.
3337 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3338 DebugTracer trc2("2");
3340 if (old_cursor.pos > 0
3341 && old_cursor.pos < old_cursor.par->Last()
3342 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3343 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3344 DebugTracer trc3("3");
3346 lyxerr << "Old cursor pos: " << old_cursor.pos << endl;
3347 old_cursor.par->Erase(old_cursor.pos - 1);
3348 status = LyXText::NEED_MORE_REFRESH;
3350 if (old_cursor.par == cursor.par &&
3351 cursor.pos > old_cursor.pos) {
3352 DebugTracer trc4("4");
3355 //SetCursor(cursor.par, cursor.pos - 1);
3360 // SetCursor(cursor.par, cursor.pos);
3366 // Do not delete empty paragraphs with keepempty set.
3367 if ((textclasslist.Style(parameters->textclass,
3368 old_cursor.par->GetLayout())).keepempty)
3371 LyXCursor tmpcursor;
3373 if (old_cursor.par != cursor.par) {
3374 DebugTracer trc5("5");
3376 if ( (old_cursor.par->Last() == 0
3377 || (old_cursor.par->Last() == 1
3378 && old_cursor.par->IsLineSeparator(0)))
3379 && old_cursor.par->FirstPhysicalPar()
3380 == old_cursor.par->LastPhysicalPar()) {
3381 DebugTracer trc6("6");
3384 // ok, we will delete anything
3386 // make sure that you do not delete any environments
3387 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3388 !(old_cursor.row->previous
3389 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3390 && !(old_cursor.row->next
3391 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3392 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3393 && ((old_cursor.row->previous
3394 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3395 || (old_cursor.row->next
3396 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3398 DebugTracer trc7("7");
3400 status = LyXText::NEED_MORE_REFRESH;
3403 if (old_cursor.row->previous) {
3404 DebugTracer trc8("8");
3406 refresh_row = old_cursor.row->previous;
3407 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3409 cursor = old_cursor; // that undo can restore the right cursor position
3410 LyXParagraph * endpar = old_cursor.par->next;
3411 if (endpar && endpar->GetDepth()) {
3412 DebugTracer trc9("9");
3414 while (endpar && endpar->GetDepth()) {
3415 endpar = endpar->LastPhysicalPar()->Next();
3418 SetUndo(Undo::DELETE,
3419 old_cursor.par->previous,
3424 RemoveRow(old_cursor.row);
3425 if (params->paragraph == old_cursor.par) {
3426 DebugTracer trc10("10");
3429 params->paragraph = params->paragraph->next;
3432 delete old_cursor.par;
3434 /* Breakagain the next par. Needed
3435 * because of the parindent that
3436 * can occur or dissappear. The
3437 * next row can change its height,
3438 * if there is another layout before */
3439 if (refresh_row->next) {
3440 DebugTracer trc11("11");
3442 BreakAgain(refresh_row->next);
3443 UpdateCounters(refresh_row);
3445 SetHeightOfRow(refresh_row);
3447 DebugTracer trc12("12");
3449 refresh_row = old_cursor.row->next;
3450 refresh_y = old_cursor.y - old_cursor.row->baseline;
3453 cursor = old_cursor; // that undo can restore the right cursor position
3454 LyXParagraph *endpar = old_cursor.par->next;
3455 if (endpar && endpar->GetDepth()) {
3456 DebugTracer trc13("13");
3458 while (endpar && endpar->GetDepth()) {
3459 endpar = endpar->LastPhysicalPar()->Next();
3462 SetUndo(Undo::DELETE,
3463 old_cursor.par->previous,
3468 RemoveRow(old_cursor.row);
3470 if (params->paragraph == old_cursor.par) {
3471 DebugTracer trc14("14");
3473 params->paragraph = params->paragraph->next;
3475 delete old_cursor.par;
3477 /* Breakagain the next par. Needed
3478 because of the parindent that can
3479 occur or dissappear.
3480 The next row can change its height,
3481 if there is another layout before
3484 DebugTracer trc15("15");
3486 BreakAgain(refresh_row);
3487 UpdateCounters(refresh_row->previous);
3492 SetCursor(cursor.par, cursor.pos);
3494 /* if (cursor.y > old_cursor.y)
3495 cursor.y -= old_cursor.row->height; */
3497 if (sel_cursor.par == old_cursor.par
3498 && sel_cursor.pos == sel_cursor.pos) {
3499 DebugTracer trc16("16");
3501 // correct selection
3502 sel_cursor = cursor;
3507 DebugTracer trc17("17");
3509 if (old_cursor.par->ClearParagraph()){
3510 DebugTracer trc18("18");
3512 RedoParagraphs(old_cursor, old_cursor.par->Next());
3514 SetCursor(cursor.par, cursor.pos);
3515 sel_cursor = cursor;
3523 LyXParagraph * LyXText::GetParFromID(int id)
3525 LyXParagraph * result = FirstParagraph();
3526 while (result && result->id() != id)
3527 result = result->next;
3533 bool LyXText::TextUndo()
3535 // returns false if no undo possible
3536 Undo * undo = params->undostack.pop();
3541 .push(CreateUndo(undo->kind,
3542 GetParFromID(undo->number_of_before_par),
3543 GetParFromID(undo->number_of_behind_par)));
3545 return TextHandleUndo(undo);
3549 bool LyXText::TextRedo()
3551 // returns false if no redo possible
3552 Undo * undo = params->redostack.pop();
3557 .push(CreateUndo(undo->kind,
3558 GetParFromID(undo->number_of_before_par),
3559 GetParFromID(undo->number_of_behind_par)));
3561 return TextHandleUndo(undo);
3565 bool LyXText::TextHandleUndo(Undo * undo)
3567 // returns false if no undo possible
3568 bool result = false;
3570 LyXParagraph * before =
3571 GetParFromID(undo->number_of_before_par);
3572 LyXParagraph * behind =
3573 GetParFromID(undo->number_of_behind_par);
3574 LyXParagraph * tmppar;
3575 LyXParagraph * tmppar2;
3576 LyXParagraph * endpar;
3577 LyXParagraph * tmppar5;
3579 // if there's no before take the beginning
3580 // of the document for redoing
3582 SetCursorIntern(FirstParagraph(), 0);
3584 // replace the paragraphs with the undo informations
3586 LyXParagraph * tmppar3 = undo->par;
3587 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3588 LyXParagraph * tmppar4 = tmppar3;
3590 while (tmppar4->next)
3591 tmppar4 = tmppar4->next;
3592 } // get last undo par
3594 // now remove the old text if there is any
3595 if (before != behind || (!behind && !before)){
3597 tmppar5 = before->next;
3599 tmppar5 = params->paragraph;
3601 while (tmppar5 && tmppar5 != behind){
3603 tmppar5 = tmppar5->next;
3604 // a memory optimization for edit: Only layout information
3605 // is stored in the undo. So restore the text informations.
3606 if (undo->kind == Undo::EDIT) {
3607 tmppar2->setContentsFromPar(tmppar);
3608 tmppar->clearContents();
3609 //tmppar2->text = tmppar->text;
3610 //tmppar->text.clear();
3611 tmppar2 = tmppar2->next;
3613 if ( currentrow && currentrow->par == tmppar )
3614 currentrow = currentrow -> previous;
3615 // Commenting out this might remove the error
3616 // reported by Purify, but it might also
3617 // introduce a memory leak. We need to
3623 // put the new stuff in the list if there is one
3626 before->next = tmppar3;
3628 params->paragraph = tmppar3;
3629 tmppar3->previous = before;
3633 params->paragraph = behind;
3636 tmppar4->next = behind;
3638 behind->previous = tmppar4;
3642 // Set the cursor for redoing
3644 SetCursorIntern(before->FirstSelfrowPar(), 0);
3645 // check wether before points to a closed float and open it if necessary
3646 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3647 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3649 while (tmppar4->previous &&
3650 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3651 tmppar4 = tmppar4->previous;
3652 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3653 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3654 tmppar4 = tmppar4->next;
3659 // open a cosed footnote at the end if necessary
3660 if (behind && behind->previous &&
3661 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3662 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3663 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3664 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3665 behind = behind->next;
3669 // calculate the endpar for redoing the paragraphs.
3671 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3672 endpar = behind->LastPhysicalPar()->Next();
3674 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3679 tmppar = GetParFromID(undo->number_of_cursor_par);
3680 RedoParagraphs(cursor, endpar);
3682 SetCursorIntern(tmppar, undo->cursor_pos);
3683 UpdateCounters(cursor.row);
3693 void LyXText::FinishUndo()
3695 // makes sure the next operation will be stored
3696 undo_finished = True;
3700 void LyXText::FreezeUndo()
3702 // this is dangerous and for internal use only
3707 void LyXText::UnFreezeUndo()
3709 // this is dangerous and for internal use only
3710 undo_frozen = false;
3714 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3715 LyXParagraph const * behind) const
3718 params->undostack.push(CreateUndo(kind, before, behind));
3719 params->redostack.clear();
3723 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3724 LyXParagraph const * behind)
3726 params->redostack.push(CreateUndo(kind, before, behind));
3730 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3731 LyXParagraph const * behind) const
3733 int before_number = -1;
3734 int behind_number = -1;
3736 before_number = before->id();
3738 behind_number = behind->id();
3739 // Undo::EDIT and Undo::FINISH are
3740 // always finished. (no overlapping there)
3741 // overlapping only with insert and delete inside one paragraph:
3742 // Nobody wants all removed character
3743 // appear one by one when undoing.
3744 // EDIT is special since only layout information, not the
3745 // contents of a paragaph are stored.
3746 if (!undo_finished && kind != Undo::EDIT &&
3747 kind != Undo::FINISH){
3748 // check wether storing is needed
3749 if (!params->undostack.empty() &&
3750 params->undostack.top()->kind == kind &&
3751 params->undostack.top()->number_of_before_par == before_number &&
3752 params->undostack.top()->number_of_behind_par == behind_number ){
3757 // create a new Undo
3758 LyXParagraph * undopar;
3759 LyXParagraph * tmppar;
3760 LyXParagraph * tmppar2;
3762 LyXParagraph * start = 0;
3763 LyXParagraph * end = 0;
3766 start = before->next;
3768 start = FirstParagraph();
3770 end = behind->previous;
3772 end = FirstParagraph();
3778 && start != end->next
3779 && (before != behind || (!before && !behind))) {
3781 tmppar2 = tmppar->Clone();
3782 tmppar2->id(tmppar->id());
3784 // a memory optimization: Just store the layout information
3786 if (kind == Undo::EDIT){
3787 //tmppar2->text.clear();
3788 tmppar2->clearContents();
3793 while (tmppar != end && tmppar->next) {
3794 tmppar = tmppar->next;
3795 tmppar2->next = tmppar->Clone();
3796 tmppar2->next->id(tmppar->id());
3797 // a memory optimization: Just store the layout
3798 // information when only edit
3799 if (kind == Undo::EDIT){
3800 //tmppar2->next->text.clear();
3801 tmppar2->clearContents();
3803 tmppar2->next->previous = tmppar2;
3804 tmppar2 = tmppar2->next;
3808 undopar = 0; // nothing to replace (undo of delete maybe)
3810 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3811 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3813 Undo * undo = new Undo(kind,
3814 before_number, behind_number,
3815 cursor_par, cursor_pos,
3818 undo_finished = false;
3823 void LyXText::SetCursorParUndo()
3825 SetUndo(Undo::FINISH,
3826 cursor.par->ParFromPos(cursor.pos)->previous,
3827 cursor.par->ParFromPos(cursor.pos)->next);
3831 void LyXText::RemoveTableRow(LyXCursor * cur) const
3837 // move to the previous row
3838 int cell_act = NumberOfCell(cur->par, cur->pos);
3841 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3844 !cur->par->table->IsFirstCell(cell_act)) {
3846 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3851 // now we have to pay attention if the actual table is the
3852 // main row of TableContRows and if yes to delete all of them
3857 // delete up to the next row
3858 while (cur->pos < cur->par->Last() &&
3860 || !cur->par->table->IsFirstCell(cell_act))) {
3861 while (cur->pos < cur->par->Last() &&
3862 !cur->par->IsNewline(cur->pos))
3863 cur->par->Erase(cur->pos);
3866 if (cur->pos < cur->par->Last())
3867 cur->par->Erase(cur->pos);
3869 if (cur->pos && cur->pos == cur->par->Last()) {
3871 cur->par->Erase(cur->pos); // no newline at very end!
3873 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3874 !cur->par->table->IsContRow(cell_org) &&
3875 cur->par->table->IsContRow(cell));
3876 cur->par->table->DeleteRow(cell_org);
3881 bool LyXText::IsEmptyTableCell() const
3883 LyXParagraph::size_type pos = cursor.pos - 1;
3884 while (pos >= 0 && pos < cursor.par->Last()
3885 && !cursor.par->IsNewline(pos))
3887 return cursor.par->IsNewline(pos + 1);
3891 void LyXText::toggleAppendix(){
3892 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3893 bool start = !par->start_of_appendix;
3895 // ensure that we have only one start_of_appendix in this document
3896 LyXParagraph * tmp = FirstParagraph();
3897 for (; tmp; tmp = tmp->next)
3898 tmp->start_of_appendix = 0;
3899 par->start_of_appendix = start;
3901 // we can set the refreshing parameters now
3902 status = LyXText::NEED_MORE_REFRESH;
3904 refresh_row = 0; // not needed for full update
3906 SetCursor(cursor.par, cursor.pos);