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"
38 #include "CutAndPaste.h"
43 //#define USE_OLD_CUT_AND_PASTE 1
49 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
57 //bparams = &p->params;
61 status = LyXText::UNCHANGED;
62 LyXParagraph * par = p->paragraph;
63 current_font = GetFont(par, 0);
68 InsertParagraph(par, lastrow);
72 // set cursor at the very top position
73 selection = true; /* these setting is necessary
74 because of the delete-empty-
75 paragraph mechanism in
77 SetCursor(firstrow->par, 0);
82 // no rebreak necessary
88 // Default layouttype for copy environment type
95 // Delete all rows, this does not touch the paragraphs!
96 Row * tmprow = firstrow;
98 tmprow = firstrow->next;
105 void LyXText::owner(BufferView * bv)
107 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
111 // Gets the fully instantiated font at a given position in a paragraph
112 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
113 // The difference is that this one is used for displaying, and thus we
114 // are allowed to make cosmetic improvements. For instance make footnotes
116 // If position is -1, we get the layout font of the paragraph.
117 // If position is -2, we get the font of the manual label of the paragraph.
118 LyXFont LyXText::GetFont(LyXParagraph * par,
119 LyXParagraph::size_type pos) const
121 LyXLayout const & layout =
122 textclasslist.Style(buffer->params.textclass,
125 char par_depth = par->GetDepth();
126 // We specialize the 95% common case:
127 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
130 if (layout.labeltype == LABEL_MANUAL
131 && pos < BeginningOfMainBody(par)) {
133 return par->GetFontSettings(pos).
134 realize(layout.reslabelfont);
136 return par->GetFontSettings(pos).
137 realize(layout.resfont);
140 // process layoutfont for pos == -1 and labelfont for pos < -1
142 return layout.resfont;
144 return layout.reslabelfont;
148 // The uncommon case need not be optimized as much
150 LyXFont layoutfont, tmpfont;
154 if (pos < BeginningOfMainBody(par)) {
156 layoutfont = layout.labelfont;
159 layoutfont = layout.font;
161 tmpfont = par->GetFontSettings(pos);
162 tmpfont.realize(layoutfont);
165 // process layoutfont for pos == -1 and labelfont for pos < -1
167 tmpfont = layout.font;
169 tmpfont = layout.labelfont;
172 // Resolve against environment font information
173 while (par && par_depth && !tmpfont.resolved()) {
174 par = par->DepthHook(par_depth - 1);
176 tmpfont.realize(textclasslist.
177 Style(buffer->params.textclass,
178 par->GetLayout()).font);
179 par_depth = par->GetDepth();
183 tmpfont.realize(textclasslist.TextClass(buffer->params.textclass).defaultfont());
185 // Cosmetic improvement: If this is an open footnote, make the font
187 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
188 && par->footnotekind == LyXParagraph::FOOTNOTE) {
196 void LyXText::SetCharFont(LyXParagraph * par,
197 LyXParagraph::size_type pos,
201 // Let the insets convert their font
202 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
203 if (par->GetInset(pos))
204 font = par->GetInset(pos)->ConvertFont(font);
207 LyXLayout const & layout =
208 textclasslist.Style(buffer->params.textclass,
211 // Get concrete layout font to reduce against
214 if (pos < BeginningOfMainBody(par))
215 layoutfont = layout.labelfont;
217 layoutfont = layout.font;
219 // Realize against environment font information
220 if (par->GetDepth()){
221 LyXParagraph * tp = par;
222 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
223 tp = tp->DepthHook(tp->GetDepth()-1);
225 layoutfont.realize(textclasslist.
226 Style(buffer->params.textclass,
227 tp->GetLayout()).font);
231 layoutfont.realize(textclasslist.TextClass(buffer->params.textclass).defaultfont());
233 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
234 && par->footnotekind == LyXParagraph::FOOTNOTE) {
235 layoutfont.decSize();
238 // Now, reduce font against full layout font
239 font.reduce(layoutfont);
241 par->SetFont(pos, font);
245 /* inserts a new row behind the specified row, increments
246 * the touched counters */
247 void LyXText::InsertRow(Row * row, LyXParagraph * par,
248 LyXParagraph::size_type pos) const
250 Row * tmprow = new Row;
252 tmprow->previous = 0;
253 tmprow->next = firstrow;
256 tmprow->previous = row;
257 tmprow->next = row->next;
262 tmprow->next->previous = tmprow;
264 if (tmprow->previous)
265 tmprow->previous->next = tmprow;
273 ++number_of_rows; // one more row
277 // removes the row and reset the touched counters
278 void LyXText::RemoveRow(Row * row) const
280 /* this must not happen before the currentrow for clear reasons.
281 so the trick is just to set the current row onto the previous
284 GetRow(row->par, row->pos, unused_y);
285 currentrow = currentrow->previous;
287 currentrow_y -= currentrow->height;
292 row->next->previous = row->previous;
293 if (!row->previous) {
294 firstrow = row->next;
296 row->previous->next = row->next;
299 lastrow = row->previous;
301 height -= row->height; // the text becomes smaller
304 --number_of_rows; // one row less
308 // remove all following rows of the paragraph of the specified row.
309 void LyXText::RemoveParagraph(Row * row) const
311 LyXParagraph * tmppar = row->par;
315 while (row && row->par == tmppar) {
323 // insert the specified paragraph behind the specified row
324 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
326 InsertRow(row, par, 0); /* insert a new row, starting
329 SetCounter(par); // set the counters
331 // and now append the whole paragraph behind the new row
333 firstrow->height = 0;
334 AppendParagraph(firstrow);
336 row->next->height = 0;
337 AppendParagraph(row->next);
342 void LyXText::ToggleFootnote()
344 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
346 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
348 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
350 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
356 void LyXText::OpenStuff()
358 if (cursor.pos == 0 && cursor.par->bibkey){
359 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
361 else if (cursor.pos < cursor.par->Last()
362 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
363 && cursor.par->GetInset(cursor.pos)->Editable()) {
364 owner_->owner()->getMiniBuffer()
365 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
366 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
368 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
375 void LyXText::CloseFootnote()
377 LyXParagraph * tmppar;
378 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
380 // if the cursor is not in an open footnote, or
381 // there is no open footnote in this paragraph, just return.
382 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
385 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
386 owner_->owner()->getMiniBuffer()
387 ->Set(_("Nothing to do"));
391 // ok, move the cursor right before the footnote
392 // just a little faster than using CursorRight()
394 cursor.par->ParFromPos(cursor.pos) != par;
398 // now the cursor is at the beginning of the physical par
399 SetCursor(cursor.par,
401 cursor.par->ParFromPos(cursor.pos)->size());
403 /* we are in a footnote, so let us move at the beginning */
404 /* this is just faster than using just CursorLeft() */
407 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
408 // just a little bit faster than movin the cursor
409 tmppar = tmppar->Previous();
411 SetCursor(tmppar, tmppar->Last());
414 // the cursor must be exactly before the footnote
415 par = cursor.par->ParFromPos(cursor.pos);
417 status = LyXText::NEED_MORE_REFRESH;
418 refresh_row = cursor.row;
419 refresh_y = cursor.y - cursor.row->baseline;
422 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
423 Row * row = cursor.row;
425 tmppar->CloseFootnote(cursor.pos);
427 while (tmppar != endpar) {
428 RemoveRow(row->next);
430 tmppar = row->next->par;
435 AppendParagraph(cursor.row);
437 SetCursor(cursor.par, cursor.pos);
441 if (cursor.row->next)
442 SetHeightOfRow(cursor.row->next);
446 /* used in setlayout */
447 // Asger is not sure we want to do this...
448 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
451 LyXLayout const & layout =
452 textclasslist.Style(buffer->params.textclass,
455 LyXFont layoutfont, tmpfont;
456 for (LyXParagraph::size_type pos = 0;
457 pos < par->Last(); ++pos) {
458 if (pos < BeginningOfMainBody(par))
459 layoutfont = layout.labelfont;
461 layoutfont = layout.font;
463 tmpfont = par->GetFontSettings(pos);
464 tmpfont.reduce(layoutfont);
465 par->SetFont(pos, tmpfont);
470 // set layout over selection and make a total rebreak of those paragraphs
471 void LyXText::SetLayout(LyXTextClass::size_type layout)
475 // if there is no selection just set the layout
476 // of the current paragraph */
478 sel_start_cursor = cursor; // dummy selection
479 sel_end_cursor = cursor;
482 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
483 LyXParagraph * undoendpar = endpar;
485 if (endpar && endpar->GetDepth()) {
486 while (endpar && endpar->GetDepth()) {
487 endpar = endpar->LastPhysicalPar()->Next();
492 endpar = endpar->Next(); // because of parindents etc.
496 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
499 tmpcursor = cursor; /* store the current cursor */
501 /* ok we have a selection. This is always between sel_start_cursor
502 * and sel_end cursor */
503 cursor = sel_start_cursor;
505 LyXLayout const & lyxlayout =
506 textclasslist.Style(buffer->params.textclass, layout);
508 while (cursor.par != sel_end_cursor.par) {
509 if (cursor.par->footnoteflag ==
510 sel_start_cursor.par->footnoteflag) {
511 cursor.par->SetLayout(layout);
512 MakeFontEntriesLayoutSpecific(cursor.par);
513 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
514 fppar->added_space_top = lyxlayout.fill_top ?
515 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
516 fppar->added_space_bottom = lyxlayout.fill_bottom ?
517 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
518 if (lyxlayout.margintype == MARGIN_MANUAL)
519 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
520 if (lyxlayout.labeltype != LABEL_BIBLIO
522 delete fppar->bibkey;
526 cursor.par = cursor.par->Next();
528 if (cursor.par->footnoteflag ==
529 sel_start_cursor.par->footnoteflag) {
530 cursor.par->SetLayout(layout);
531 MakeFontEntriesLayoutSpecific(cursor.par);
532 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
533 fppar->added_space_top = lyxlayout.fill_top ?
534 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
535 fppar->added_space_bottom = lyxlayout.fill_bottom ?
536 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
537 if (lyxlayout.margintype == MARGIN_MANUAL)
538 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
539 if (lyxlayout.labeltype != LABEL_BIBLIO
541 delete fppar->bibkey;
546 RedoParagraphs(sel_start_cursor, endpar);
548 // we have to reset the selection, because the
549 // geometry could have changed */
550 SetCursor(sel_start_cursor.par, sel_start_cursor.pos, false);
552 SetCursor(sel_end_cursor.par, sel_end_cursor.pos, false);
553 UpdateCounters(cursor.row);
556 SetCursor(tmpcursor.par, tmpcursor.pos, true);
560 // increment depth over selection and
561 // make a total rebreak of those paragraphs
562 void LyXText::IncDepth()
564 // If there is no selection, just use the current paragraph
566 sel_start_cursor = cursor; // dummy selection
567 sel_end_cursor = cursor;
570 // We end at the next paragraph with depth 0
571 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
572 LyXParagraph * undoendpar = endpar;
574 if (endpar && endpar->GetDepth()) {
575 while (endpar && endpar->GetDepth()) {
576 endpar = endpar->LastPhysicalPar()->Next();
581 endpar = endpar->Next(); // because of parindents etc.
586 .par->ParFromPos(sel_start_cursor.pos)->previous,
589 LyXCursor tmpcursor = cursor; // store the current cursor
591 // ok we have a selection. This is always between sel_start_cursor
592 // and sel_end cursor
593 cursor = sel_start_cursor;
595 bool anything_changed = false;
598 // NOTE: you can't change the depth of a bibliography entry
599 if (cursor.par->footnoteflag ==
600 sel_start_cursor.par->footnoteflag
601 && textclasslist.Style(buffer->params.textclass,
602 cursor.par->GetLayout()
603 ).labeltype != LABEL_BIBLIO) {
604 LyXParagraph * prev =
605 cursor.par->FirstPhysicalPar()->Previous();
607 && (prev->GetDepth() - cursor.par->GetDepth() > 0
608 || (prev->GetDepth() == cursor.par->GetDepth()
609 && textclasslist.Style(buffer->params.textclass,
610 prev->GetLayout()).isEnvironment()))) {
611 cursor.par->FirstPhysicalPar()->depth++;
612 anything_changed = true;
615 if (cursor.par == sel_end_cursor.par)
617 cursor.par = cursor.par->Next();
620 // if nothing changed set all depth to 0
621 if (!anything_changed) {
622 cursor = sel_start_cursor;
623 while (cursor.par != sel_end_cursor.par) {
624 cursor.par->FirstPhysicalPar()->depth = 0;
625 cursor.par = cursor.par->Next();
627 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
628 cursor.par->FirstPhysicalPar()->depth = 0;
631 RedoParagraphs(sel_start_cursor, endpar);
633 // we have to reset the selection, because the
634 // geometry could have changed
635 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
637 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
638 UpdateCounters(cursor.row);
641 SetCursor(tmpcursor.par, tmpcursor.pos);
645 // decrement depth over selection and
646 // make a total rebreak of those paragraphs
647 void LyXText::DecDepth()
649 // if there is no selection just set the layout
650 // of the current paragraph
652 sel_start_cursor = cursor; // dummy selection
653 sel_end_cursor = cursor;
656 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
657 LyXParagraph * undoendpar = endpar;
659 if (endpar && endpar->GetDepth()) {
660 while (endpar && endpar->GetDepth()) {
661 endpar = endpar->LastPhysicalPar()->Next();
666 endpar = endpar->Next(); // because of parindents etc.
671 .par->ParFromPos(sel_start_cursor.pos)->previous,
674 LyXCursor tmpcursor = cursor; // store the current cursor
676 // ok we have a selection. This is always between sel_start_cursor
677 // and sel_end cursor
678 cursor = sel_start_cursor;
681 if (cursor.par->footnoteflag ==
682 sel_start_cursor.par->footnoteflag) {
683 if (cursor.par->FirstPhysicalPar()->depth)
684 cursor.par->FirstPhysicalPar()->depth--;
686 if (cursor.par == sel_end_cursor.par)
688 cursor.par = cursor.par->Next();
691 RedoParagraphs(sel_start_cursor, endpar);
693 // we have to reset the selection, because the
694 // geometry could have changed
695 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
697 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
698 UpdateCounters(cursor.row);
701 SetCursor(tmpcursor.par, tmpcursor.pos);
705 // set font over selection and make a total rebreak of those paragraphs
706 void LyXText::SetFont(LyXFont const & font, bool toggleall)
708 // if there is no selection just set the current_font
710 // Determine basis font
712 if (cursor.pos < BeginningOfMainBody(cursor.par))
713 layoutfont = GetFont(cursor.par, -2);
715 layoutfont = GetFont(cursor.par, -1);
716 // Update current font
717 real_current_font.update(font,
718 buffer->params.language_info,
721 // Reduce to implicit settings
722 current_font = real_current_font;
723 current_font.reduce(layoutfont);
724 // And resolve it completely
725 real_current_font.realize(layoutfont);
729 LyXCursor tmpcursor = cursor; // store the current cursor
731 // ok we have a selection. This is always between sel_start_cursor
732 // and sel_end cursor
735 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
736 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
737 cursor = sel_start_cursor;
738 while (cursor.par != sel_end_cursor.par ||
739 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
740 && cursor.pos < sel_end_cursor.pos))
742 if (cursor.pos < cursor.par->Last()
743 && cursor.par->footnoteflag
744 == sel_start_cursor.par->footnoteflag) {
745 // an open footnote should behave
747 LyXFont newfont = GetFont(cursor.par, cursor.pos);
749 buffer->params.language_info,
751 SetCharFont(cursor.par, cursor.pos, newfont);
755 cursor.par = cursor.par->Next();
759 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
761 // we have to reset the selection, because the
762 // geometry could have changed
763 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
765 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
768 SetCursor(tmpcursor.par, tmpcursor.pos);
772 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
774 Row * tmprow = cur.row;
775 long y = cur.y - tmprow->baseline;
777 SetHeightOfRow(tmprow);
778 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
779 // find the first row of the paragraph
780 if (first_phys_par != tmprow->par)
781 while (tmprow->previous
782 && tmprow->previous->par != first_phys_par) {
783 tmprow = tmprow->previous;
785 SetHeightOfRow(tmprow);
787 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
788 tmprow = tmprow->previous;
790 SetHeightOfRow(tmprow);
793 // we can set the refreshing parameters now
794 status = LyXText::NEED_MORE_REFRESH;
796 refresh_row = tmprow;
797 SetCursor(cur.par, cur.pos);
801 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
803 Row * tmprow = cur.row;
805 long y = cur.y - tmprow->baseline;
806 SetHeightOfRow(tmprow);
807 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
808 // find the first row of the paragraph
809 if (first_phys_par != tmprow->par)
810 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
811 tmprow = tmprow->previous;
814 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
815 tmprow = tmprow->previous;
819 // we can set the refreshing parameters now
820 if (status == LyXText::UNCHANGED || y < refresh_y) {
822 refresh_row = tmprow;
824 status = LyXText::NEED_MORE_REFRESH;
825 SetCursor(cur.par, cur.pos);
829 /* deletes and inserts again all paragaphs between the cursor
830 * and the specified par
831 * This function is needed after SetLayout and SetFont etc. */
832 void LyXText::RedoParagraphs(LyXCursor const & cur,
833 LyXParagraph const * endpar) const
836 LyXParagraph * tmppar, * first_phys_par;
838 Row * tmprow = cur.row;
840 long y = cur.y - tmprow->baseline;
842 if (!tmprow->previous){
843 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
845 first_phys_par = tmprow->par->FirstPhysicalPar();
846 // find the first row of the paragraph
847 if (first_phys_par != tmprow->par)
848 while (tmprow->previous &&
849 (tmprow->previous->par != first_phys_par)) {
850 tmprow = tmprow->previous;
853 while (tmprow->previous
854 && tmprow->previous->par == first_phys_par) {
855 tmprow = tmprow->previous;
860 // we can set the refreshing parameters now
861 status = LyXText::NEED_MORE_REFRESH;
863 refresh_row = tmprow->previous; /* the real refresh row will
864 be deleted, so I store
868 tmppar = tmprow->next->par;
871 while (tmppar != endpar) {
872 RemoveRow(tmprow->next);
874 tmppar = tmprow->next->par;
879 // remove the first one
880 tmprow2 = tmprow; /* this is because tmprow->previous
882 tmprow = tmprow->previous;
885 tmppar = first_phys_par;
889 InsertParagraph(tmppar, tmprow);
892 while (tmprow->next && tmprow->next->par == tmppar)
893 tmprow = tmprow->next;
894 tmppar = tmppar->Next();
896 } while (tmppar != endpar);
898 // this is because of layout changes
900 refresh_y -= refresh_row->height;
901 SetHeightOfRow(refresh_row);
903 refresh_row = firstrow;
905 SetHeightOfRow(refresh_row);
908 if (tmprow && tmprow->next)
909 SetHeightOfRow(tmprow->next);
913 int LyXText::FullRebreak()
915 if (need_break_row) {
916 BreakAgain(need_break_row);
924 /* important for the screen */
927 /* the cursor set functions have a special mechanism. When they
928 * realize, that you left an empty paragraph, they will delete it.
929 * They also delet the corresponding row */
931 // need the selection cursor:
932 void LyXText::SetSelection()
935 last_sel_cursor = sel_cursor;
936 sel_start_cursor = sel_cursor;
937 sel_end_cursor = sel_cursor;
942 // first the toggling area
943 if (cursor.y < last_sel_cursor.y ||
944 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
945 toggle_end_cursor = last_sel_cursor;
946 toggle_cursor = cursor;
949 toggle_end_cursor = cursor;
950 toggle_cursor = last_sel_cursor;
953 last_sel_cursor = cursor;
955 // and now the whole selection
957 if (sel_cursor.par == cursor.par)
958 if (sel_cursor.pos < cursor.pos) {
959 sel_end_cursor = cursor;
960 sel_start_cursor = sel_cursor;
962 sel_end_cursor = sel_cursor;
963 sel_start_cursor = cursor;
965 else if (sel_cursor.y < cursor.y ||
966 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
967 sel_end_cursor = cursor;
968 sel_start_cursor = sel_cursor;
971 sel_end_cursor = sel_cursor;
972 sel_start_cursor = cursor;
975 // a selection with no contents is not a selection
976 if (sel_start_cursor.x == sel_end_cursor.x &&
977 sel_start_cursor.y == sel_end_cursor.y)
982 void LyXText::ClearSelection() const
989 void LyXText::CursorHome() const
991 SetCursor(cursor.par, cursor.row->pos);
995 void LyXText::CursorEnd() const
997 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
998 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1000 if (cursor.par->Last() &&
1001 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1002 || cursor.par->IsNewline(RowLast(cursor.row))))
1003 SetCursor(cursor.par, RowLast(cursor.row));
1005 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1007 if (cursor.par->table) {
1008 int cell = NumberOfCell(cursor.par, cursor.pos);
1009 if (cursor.par->table->RowHasContRow(cell) &&
1010 cursor.par->table->CellHasContRow(cell)<0) {
1011 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1012 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1014 if (cursor.par->Last() &&
1015 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1016 || cursor.par->IsNewline(RowLast(cursor.row))))
1017 SetCursor(cursor.par, RowLast(cursor.row));
1019 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1026 void LyXText::CursorTop() const
1028 while (cursor.par->Previous())
1029 cursor.par = cursor.par->Previous();
1030 SetCursor(cursor.par, 0);
1034 void LyXText::CursorBottom() const
1036 while (cursor.par->Next())
1037 cursor.par = cursor.par->Next();
1038 SetCursor(cursor.par, cursor.par->Last());
1042 /* returns a pointer to the row near the specified y-coordinate
1043 * (relative to the whole text). y is set to the real beginning
1045 Row * LyXText::GetRowNearY(long & y) const
1051 tmprow = currentrow;
1052 tmpy = currentrow_y;
1059 while (tmprow->next && tmpy + tmprow->height <= y) {
1060 tmpy += tmprow->height;
1061 tmprow = tmprow->next;
1064 while (tmprow->previous && tmpy > y) {
1065 tmprow = tmprow->previous;
1066 tmpy -= tmprow->height;
1069 currentrow = tmprow;
1070 currentrow_y = tmpy;
1072 y = tmpy; // return the real y
1077 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1079 // If the mask is completely neutral, tell user
1080 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1081 // Could only happen with user style
1082 owner_->owner()->getMiniBuffer()
1083 ->Set(_("No font change defined. Use Character under"
1084 " the Layout menu to define font change."));
1088 // Try implicit word selection
1089 // If there is a change in the language the implicit word selection
1091 LyXCursor resetCursor = cursor;
1092 bool implicitSelection = (font.language() == ignore_language)
1093 ? SelectWordWhenUnderCursor() : false;
1096 SetFont(font, toggleall);
1098 /* Implicit selections are cleared afterwards and cursor is set to the
1099 original position. */
1100 if (implicitSelection) {
1102 cursor = resetCursor;
1103 SetCursor( cursor.par, cursor.pos );
1104 sel_cursor = cursor;
1109 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1111 if (textclasslist.Style(buffer->params.textclass,
1112 par->GetLayout()).labeltype != LABEL_MANUAL)
1115 return par->BeginningOfMainBody();
1119 /* if there is a selection, reset every environment you can find
1120 * in the selection, otherwise just the environment you are in */
1121 void LyXText::MeltFootnoteEnvironment()
1123 LyXParagraph * tmppar, * firsttmppar;
1127 /* is is only allowed, if the cursor is IN an open footnote.
1128 * Otherwise it is too dangerous */
1129 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1132 SetUndo(Undo::FINISH,
1133 cursor.par->PreviousBeforeFootnote()->previous,
1134 cursor.par->NextAfterFootnote()->next);
1136 /* ok, move to the beginning of the footnote. */
1137 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1138 cursor.par = cursor.par->Previous();
1140 SetCursor(cursor.par, cursor.par->Last());
1141 /* this is just faster than using CursorLeft(); */
1143 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1144 tmppar = firsttmppar;
1145 /* tmppar is now the paragraph right before the footnote */
1147 bool first_footnote_par_is_not_empty = tmppar->next->size();
1150 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1151 tmppar = tmppar->next; /* I use next instead of Next(),
1152 * because there cannot be any
1153 * footnotes in a footnote
1155 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1157 /* remember the captions and empty paragraphs */
1158 if ((textclasslist.Style(buffer->params.textclass,
1159 tmppar->GetLayout())
1160 .labeltype == LABEL_SENSITIVE)
1162 tmppar->SetLayout(0);
1165 // now we will paste the ex-footnote, if the layouts allow it
1166 // first restore the layout of the paragraph right behind
1169 tmppar->next->MakeSameLayout(cursor.par);
1172 if ((!tmppar->GetLayout() && !tmppar->table)
1174 && (!tmppar->Next()->Last()
1175 || tmppar->Next()->HasSameLayout(tmppar)))) {
1176 if (tmppar->Next()->Last()
1177 && tmppar->Next()->IsLineSeparator(0))
1178 tmppar->Next()->Erase(0);
1179 tmppar->PasteParagraph();
1182 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1183 * by the pasting of the beginning */
1185 /* then the beginning */
1186 /* if there is no space between the text and the footnote, so we insert
1188 * (only if the previous par and the footnotepar are not empty!) */
1189 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1190 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1191 if (firsttmppar->size()
1192 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1193 && first_footnote_par_is_not_empty) {
1194 firsttmppar->next->InsertChar(0, ' ');
1196 firsttmppar->PasteParagraph();
1199 /* now redo the paragaphs */
1200 RedoParagraphs(cursor, tmppar);
1202 SetCursor(cursor.par, cursor.pos);
1204 /* sometimes it can happen, that there is a counter change */
1205 Row * row = cursor.row;
1206 while (row->next && row->par != tmppar && row->next->par != tmppar)
1208 UpdateCounters(row);
1215 /* the DTP switches for paragraphs. LyX will store them in the
1216 * first physicla paragraph. When a paragraph is broken, the top settings
1217 * rest, the bottom settings are given to the new one. So I can make shure,
1218 * they do not duplicate themself and you cannnot make dirty things with
1221 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1222 bool pagebreak_top, bool pagebreak_bottom,
1223 VSpace const & space_top,
1224 VSpace const & space_bottom,
1226 string labelwidthstring,
1229 LyXCursor tmpcursor = cursor;
1231 sel_start_cursor = cursor;
1232 sel_end_cursor = cursor;
1235 // make sure that the depth behind the selection are restored, too
1236 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1237 LyXParagraph * undoendpar = endpar;
1239 if (endpar && endpar->GetDepth()) {
1240 while (endpar && endpar->GetDepth()) {
1241 endpar = endpar->LastPhysicalPar()->Next();
1242 undoendpar = endpar;
1246 endpar = endpar->Next(); // because of parindents etc.
1251 .par->ParFromPos(sel_start_cursor.pos)->previous,
1255 LyXParagraph * tmppar = sel_end_cursor.par;
1256 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1257 SetCursor(tmppar->FirstPhysicalPar(), 0);
1258 status = LyXText::NEED_MORE_REFRESH;
1259 refresh_row = cursor.row;
1260 refresh_y = cursor.y - cursor.row->baseline;
1261 if (cursor.par->footnoteflag ==
1262 sel_start_cursor.par->footnoteflag) {
1263 cursor.par->line_top = line_top;
1264 cursor.par->line_bottom = line_bottom;
1265 cursor.par->pagebreak_top = pagebreak_top;
1266 cursor.par->pagebreak_bottom = pagebreak_bottom;
1267 cursor.par->added_space_top = space_top;
1268 cursor.par->added_space_bottom = space_bottom;
1269 // does the layout allow the new alignment?
1270 if (align == LYX_ALIGN_LAYOUT)
1271 align = textclasslist
1272 .Style(buffer->params.textclass,
1273 cursor.par->GetLayout()).align;
1274 if (align & textclasslist
1275 .Style(buffer->params.textclass,
1276 cursor.par->GetLayout()).alignpossible) {
1277 if (align == textclasslist
1278 .Style(buffer->params.textclass,
1279 cursor.par->GetLayout()).align)
1280 cursor.par->align = LYX_ALIGN_LAYOUT;
1282 cursor.par->align = align;
1284 cursor.par->SetLabelWidthString(labelwidthstring);
1285 cursor.par->noindent = noindent;
1288 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1291 RedoParagraphs(sel_start_cursor, endpar);
1294 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1295 sel_cursor = cursor;
1296 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1298 SetCursor(tmpcursor.par, tmpcursor.pos);
1302 void LyXText::SetParagraphExtraOpt(int type,
1304 char const * widthp,
1305 int alignment, bool hfill,
1306 bool start_minipage)
1308 LyXCursor tmpcursor = cursor;
1309 LyXParagraph * tmppar;
1311 sel_start_cursor = cursor;
1312 sel_end_cursor = cursor;
1315 // make sure that the depth behind the selection are restored, too
1316 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1317 LyXParagraph * undoendpar = endpar;
1319 if (endpar && endpar->GetDepth()) {
1320 while (endpar && endpar->GetDepth()) {
1321 endpar = endpar->LastPhysicalPar()->Next();
1322 undoendpar = endpar;
1326 endpar = endpar->Next(); // because of parindents etc.
1331 .par->ParFromPos(sel_start_cursor.pos)->previous,
1334 tmppar = sel_end_cursor.par;
1335 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1336 SetCursor(tmppar->FirstPhysicalPar(), 0);
1337 status = LyXText::NEED_MORE_REFRESH;
1338 refresh_row = cursor.row;
1339 refresh_y = cursor.y - cursor.row->baseline;
1340 if (cursor.par->footnoteflag ==
1341 sel_start_cursor.par->footnoteflag) {
1342 if (type == LyXParagraph::PEXTRA_NONE) {
1343 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1344 cursor.par->UnsetPExtraType();
1345 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1348 cursor.par->SetPExtraType(type, width, widthp);
1349 cursor.par->pextra_hfill = hfill;
1350 cursor.par->pextra_start_minipage = start_minipage;
1351 cursor.par->pextra_alignment = alignment;
1354 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1356 RedoParagraphs(sel_start_cursor, endpar);
1358 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1359 sel_cursor = cursor;
1360 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1362 SetCursor(tmpcursor.par, tmpcursor.pos);
1366 char loweralphaCounter(int n)
1368 if (n < 1 || n > 26)
1374 char alphaCounter(int n)
1376 if (n < 1 || n > 26)
1382 char hebrewCounter(int n)
1384 static const char hebrew[22] = {
1385 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1386 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1387 '÷', 'ø', 'ù', 'ú'
1389 if (n < 1 || n > 22)
1395 static char const * romanCounter(int n)
1397 static char const * roman[20] = {
1398 "i", "ii", "iii", "iv", "v",
1399 "vi", "vii", "viii", "ix", "x",
1400 "xi", "xii", "xiii", "xiv", "xv",
1401 "xvi", "xvii", "xviii", "xix", "xx"
1403 if (n < 1 || n > 20)
1409 // set the counter of a paragraph. This includes the labels
1410 void LyXText::SetCounter(LyXParagraph * par) const
1412 // this is only relevant for the beginning of paragraph
1413 par = par->FirstPhysicalPar();
1415 LyXLayout const & layout =
1416 textclasslist.Style(buffer->params.textclass,
1419 LyXTextClass const & textclass =
1420 textclasslist.TextClass(buffer->params.textclass);
1422 /* copy the prev-counters to this one, unless this is the start of a
1423 footnote or of a bibliography or the very first paragraph */
1425 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1426 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1427 && par->footnotekind == LyXParagraph::FOOTNOTE)
1428 && !(textclasslist.Style(buffer->params.textclass,
1429 par->Previous()->GetLayout()
1430 ).labeltype != LABEL_BIBLIO
1431 && layout.labeltype == LABEL_BIBLIO)) {
1432 for (int i = 0; i < 10; ++i) {
1433 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1435 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1436 if (!par->appendix && par->start_of_appendix){
1437 par->appendix = true;
1438 for (int i = 0; i < 10; ++i) {
1439 par->setCounter(i, 0);
1442 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1443 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1446 for (int i = 0; i < 10; ++i) {
1447 par->setCounter(i, 0);
1449 par->appendix = par->start_of_appendix;
1454 // if this is an open marginnote and this is the first
1455 // entry in the marginnote and the enclosing
1456 // environment is an enum/item then correct for the
1457 // LaTeX behaviour (ARRae)
1458 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1459 && par->footnotekind == LyXParagraph::MARGIN
1461 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1462 && (par->PreviousBeforeFootnote()
1463 && textclasslist.Style(buffer->params.textclass,
1464 par->PreviousBeforeFootnote()->GetLayout()
1465 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1466 // Any itemize or enumerate environment in a marginnote
1467 // that is embedded in an itemize or enumerate
1468 // paragraph is seen by LaTeX as being at a deeper
1469 // level within that enclosing itemization/enumeration
1470 // even if there is a "standard" layout at the start of
1476 /* Maybe we have to increment the enumeration depth.
1477 * BUT, enumeration in a footnote is considered in isolation from its
1478 * surrounding paragraph so don't increment if this is the
1479 * first line of the footnote
1480 * AND, bibliographies can't have their depth changed ie. they
1481 * are always of depth 0
1484 && par->Previous()->GetDepth() < par->GetDepth()
1485 && textclasslist.Style(buffer->params.textclass,
1486 par->Previous()->GetLayout()
1487 ).labeltype == LABEL_COUNTER_ENUMI
1488 && par->enumdepth < 3
1489 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1490 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1491 && par->footnotekind == LyXParagraph::FOOTNOTE)
1492 && layout.labeltype != LABEL_BIBLIO) {
1496 /* Maybe we have to decrement the enumeration depth, see note above */
1498 && par->Previous()->GetDepth() > par->GetDepth()
1499 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1500 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1501 && par->footnotekind == LyXParagraph::FOOTNOTE)
1502 && layout.labeltype != LABEL_BIBLIO) {
1503 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1504 par->setCounter(6 + par->enumdepth,
1505 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1506 /* reset the counters.
1507 * A depth change is like a breaking layout
1509 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1510 par->setCounter(i, 0);
1513 if (!par->labelstring.empty()) {
1514 par->labelstring.clear();
1517 if (layout.margintype == MARGIN_MANUAL) {
1518 if (par->labelwidthstring.empty()) {
1519 par->SetLabelWidthString(layout.labelstring());
1522 par->SetLabelWidthString(string());
1525 /* is it a layout that has an automatic label ? */
1526 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1528 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1529 if (i >= 0 && i<= buffer->params.secnumdepth) {
1530 par->incCounter(i); // increment the counter
1532 // Is there a label? Useful for Chapter layout
1533 if (!par->appendix){
1534 if (!layout.labelstring().empty())
1535 par->labelstring = layout.labelstring();
1537 par->labelstring.clear();
1539 if (!layout.labelstring_appendix().empty())
1540 par->labelstring = layout.labelstring_appendix();
1542 par->labelstring.clear();
1546 std::ostringstream s;
1550 if (!par->appendix) {
1551 switch (2 * LABEL_FIRST_COUNTER -
1552 textclass.maxcounter() + i) {
1553 case LABEL_COUNTER_CHAPTER:
1554 s << par->getCounter(i);
1556 case LABEL_COUNTER_SECTION:
1557 s << par->getCounter(i - 1) << '.'
1558 << par->getCounter(i);
1560 case LABEL_COUNTER_SUBSECTION:
1561 s << par->getCounter(i - 2) << '.'
1562 << par->getCounter(i - 1) << '.'
1563 << par->getCounter(i);
1565 case LABEL_COUNTER_SUBSUBSECTION:
1566 s << par->getCounter(i - 3) << '.'
1567 << par->getCounter(i - 2) << '.'
1568 << par->getCounter(i - 1) << '.'
1569 << par->getCounter(i);
1572 case LABEL_COUNTER_PARAGRAPH:
1573 s << par->getCounter(i - 4) << '.'
1574 << par->getCounter(i - 3) << '.'
1575 << par->getCounter(i - 2) << '.'
1576 << par->getCounter(i - 1) << '.'
1577 << par->getCounter(i);
1579 case LABEL_COUNTER_SUBPARAGRAPH:
1580 s << par->getCounter(i - 5) << '.'
1581 << par->getCounter(i - 4) << '.'
1582 << par->getCounter(i - 3) << '.'
1583 << par->getCounter(i - 2) << '.'
1584 << par->getCounter(i - 1) << '.'
1585 << par->getCounter(i);
1589 s << par->getCounter(i) << '.';
1592 } else { // appendix
1593 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1594 case LABEL_COUNTER_CHAPTER:
1595 if (par->isRightToLeftPar())
1596 s << hebrewCounter(par->getCounter(i));
1598 s << alphaCounter(par->getCounter(i));
1600 case LABEL_COUNTER_SECTION:
1601 if (par->isRightToLeftPar())
1602 s << hebrewCounter(par->getCounter(i - 1));
1604 s << alphaCounter(par->getCounter(i - 1));
1607 << par->getCounter(i);
1610 case LABEL_COUNTER_SUBSECTION:
1611 if (par->isRightToLeftPar())
1612 s << hebrewCounter(par->getCounter(i - 2));
1614 s << alphaCounter(par->getCounter(i - 2));
1617 << par->getCounter(i-1) << '.'
1618 << par->getCounter(i);
1621 case LABEL_COUNTER_SUBSUBSECTION:
1622 if (par->isRightToLeftPar())
1623 s << hebrewCounter(par->getCounter(i-3));
1625 s << alphaCounter(par->getCounter(i-3));
1628 << par->getCounter(i-2) << '.'
1629 << par->getCounter(i-1) << '.'
1630 << par->getCounter(i);
1633 case LABEL_COUNTER_PARAGRAPH:
1634 if (par->isRightToLeftPar())
1635 s << hebrewCounter(par->getCounter(i-4));
1637 s << alphaCounter(par->getCounter(i-4));
1640 << par->getCounter(i-3) << '.'
1641 << par->getCounter(i-2) << '.'
1642 << par->getCounter(i-1) << '.'
1643 << par->getCounter(i);
1646 case LABEL_COUNTER_SUBPARAGRAPH:
1647 if (par->isRightToLeftPar())
1648 s << hebrewCounter(par->getCounter(i-5));
1650 s << alphaCounter(par->getCounter(i-5));
1653 << par->getCounter(i-4) << '.'
1654 << par->getCounter(i-3) << '.'
1655 << par->getCounter(i-2) << '.'
1656 << par->getCounter(i-1) << '.'
1657 << par->getCounter(i);
1661 // Can this ever be reached? And in the
1662 // case it is, how can this be correct?
1664 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1670 par->labelstring += s.str().c_str();
1671 // We really want to remove the c_str as soon as
1675 char * tmps = s.str();
1676 par->labelstring += tmps;
1680 for (i++; i < 10; ++i) {
1681 // reset the following counters
1682 par->setCounter(i, 0);
1684 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1685 for (i++; i < 10; ++i) {
1686 // reset the following counters
1687 par->setCounter(i, 0);
1689 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1690 par->incCounter(i + par->enumdepth);
1691 int number = par->getCounter(i + par->enumdepth);
1694 std::ostringstream s;
1698 switch (par->enumdepth) {
1700 if (par->isRightToLeftPar())
1702 << hebrewCounter(number)
1706 << loweralphaCounter(number)
1710 if (par->isRightToLeftPar())
1711 s << '.' << romanCounter(number);
1713 s << romanCounter(number) << '.';
1716 if (par->isRightToLeftPar())
1718 << alphaCounter(number);
1720 s << alphaCounter(number)
1724 if (par->isRightToLeftPar())
1731 par->labelstring = s.str().c_str();
1732 // we really want to get rid of that c_str()
1735 char * tmps = s.str();
1736 par->labelstring = tmps;
1740 for (i += par->enumdepth + 1; i < 10; ++i)
1741 par->setCounter(i, 0); /* reset the following counters */
1744 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1745 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1747 int number = par->getCounter(i);
1749 par->bibkey = new InsetBibKey();
1750 par->bibkey->setCounter(number);
1751 par->labelstring = layout.labelstring();
1753 // In biblio should't be following counters but...
1755 string s = layout.labelstring();
1757 // the caption hack:
1759 if (layout.labeltype == LABEL_SENSITIVE) {
1760 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1761 && (par->footnotekind == LyXParagraph::FIG
1762 || par->footnotekind == LyXParagraph::WIDE_FIG))
1763 s = (par->getParLanguage()->lang == "hebrew")
1764 ? ":øåéà " : "Figure:";
1765 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1766 && (par->footnotekind == LyXParagraph::TAB
1767 || par->footnotekind == LyXParagraph::WIDE_TAB))
1768 s = (par->getParLanguage()->lang == "hebrew")
1769 ? ":äìáè" : "Table:";
1770 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1771 && par->footnotekind == LyXParagraph::ALGORITHM)
1772 s = (par->getParLanguage()->lang == "hebrew")
1773 ? ":Ãúéøåâìà " : "Algorithm:";
1775 /* par->SetLayout(0);
1776 s = layout->labelstring; */
1777 s = (par->getParLanguage()->lang == "hebrew")
1778 ? " :úåòîùî øñç" : "Senseless: ";
1781 par->labelstring = s;
1783 /* reset the enumeration counter. They are always resetted
1784 * when there is any other layout between */
1785 for (int i = 6 + par->enumdepth; i < 10; ++i)
1786 par->setCounter(i, 0);
1791 /* Updates all counters BEHIND the row. Changed paragraphs
1792 * with a dynamic left margin will be rebroken. */
1793 void LyXText::UpdateCounters(Row * row) const
1802 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1803 par = row->par->LastPhysicalPar()->Next();
1805 par = row->par->next;
1810 while (row->par != par)
1815 /* now check for the headline layouts. remember that they
1816 * have a dynamic left margin */
1818 && ( textclasslist.Style(buffer->params.textclass,
1819 par->layout).margintype == MARGIN_DYNAMIC
1820 || textclasslist.Style(buffer->params.textclass,
1821 par->layout).labeltype == LABEL_SENSITIVE)
1824 /* Rebreak the paragraph */
1825 RemoveParagraph(row);
1826 AppendParagraph(row);
1828 /* think about the damned open footnotes! */
1829 while (par->Next() &&
1830 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1831 || par->Next()->IsDummy())){
1833 if (par->IsDummy()) {
1834 while (row->par != par)
1836 RemoveParagraph(row);
1837 AppendParagraph(row);
1842 par = par->LastPhysicalPar()->Next();
1848 /* insets an inset. */
1849 void LyXText::InsertInset(Inset *inset)
1851 if (!cursor.par->InsertInsetAllowed(inset))
1853 SetUndo(Undo::INSERT,
1854 cursor.par->ParFromPos(cursor.pos)->previous,
1855 cursor.par->ParFromPos(cursor.pos)->next);
1856 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1857 cursor.par->InsertInset(cursor.pos, inset);
1858 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1859 * The character will not be inserted a
1864 #ifdef USE_OLD_CUT_AND_PASTE
1865 // this is for the simple cut and paste mechanism
1866 static LyXParagraph * simple_cut_buffer = 0;
1867 static char simple_cut_buffer_textclass = 0;
1869 void DeleteSimpleCutBuffer()
1871 if (!simple_cut_buffer)
1873 LyXParagraph * tmppar;
1875 while (simple_cut_buffer) {
1876 tmppar = simple_cut_buffer;
1877 simple_cut_buffer = simple_cut_buffer->next;
1880 simple_cut_buffer = 0;
1884 void LyXText::copyEnvironmentType()
1886 copylayouttype = cursor.par->GetLayout();
1890 void LyXText::pasteEnvironmentType()
1892 SetLayout(copylayouttype);
1895 #ifdef USE_OLD_CUT_AND_PASTE
1896 void LyXText::CutSelection(bool doclear)
1898 // This doesn't make sense, if there is no selection
1902 // OK, we have a selection. This is always between sel_start_cursor
1903 // and sel_end cursor
1904 LyXParagraph * tmppar;
1906 // Check whether there are half footnotes in the selection
1907 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1908 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1909 tmppar = sel_start_cursor.par;
1910 while (tmppar != sel_end_cursor.par){
1911 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1912 WriteAlert(_("Impossible operation"),
1913 _("Don't know what to do with half floats."),
1917 tmppar = tmppar->Next();
1921 /* table stuff -- begin */
1922 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1923 if ( sel_start_cursor.par != sel_end_cursor.par) {
1924 WriteAlert(_("Impossible operation"),
1925 _("Don't know what to do with half tables."),
1929 sel_start_cursor.par->table->Reinit();
1931 /* table stuff -- end */
1933 // make sure that the depth behind the selection are restored, too
1934 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1935 LyXParagraph * undoendpar = endpar;
1937 if (endpar && endpar->GetDepth()) {
1938 while (endpar && endpar->GetDepth()) {
1939 endpar = endpar->LastPhysicalPar()->Next();
1940 undoendpar = endpar;
1942 } else if (endpar) {
1943 endpar = endpar->Next(); // because of parindents etc.
1946 SetUndo(Undo::DELETE,
1948 .par->ParFromPos(sel_start_cursor.pos)->previous,
1951 // clear the simple_cut_buffer
1952 DeleteSimpleCutBuffer();
1954 // set the textclass
1955 simple_cut_buffer_textclass = buffer->params.textclass;
1957 #ifdef WITH_WARNINGS
1958 #warning Asger: Make cut more intelligent here.
1961 White paper for "intelligent" cutting:
1963 Example: "This is our text."
1964 Using " our " as selection, cutting will give "This istext.".
1965 Using "our" as selection, cutting will give "This is text.".
1966 Using " our" as selection, cutting will give "This is text.".
1967 Using "our " as selection, cutting will give "This is text.".
1969 All those four selections will (however) paste identically:
1970 Pasting with the cursor right after the "is" will give the
1971 original text with all four selections.
1973 The rationale is to be intelligent such that words are copied,
1974 cut and pasted in a functional manner.
1976 This is not implemented yet. (Asger)
1978 The changes below sees to do a lot of what you want. However
1979 I have not verified that all cases work as they should:
1981 - cut in multiple row
1983 - cut across footnotes and paragraph
1984 My simplistic tests show that the idea are basically sound but
1985 there are some items to fix up...we only need to find them
1988 As do redo Asger's example above (with | beeing the cursor in the
1989 result after cutting.):
1991 Example: "This is our text."
1992 Using " our " as selection, cutting will give "This is|text.".
1993 Using "our" as selection, cutting will give "This is | text.".
1994 Using " our" as selection, cutting will give "This is| text.".
1995 Using "our " as selection, cutting will give "This is |text.".
2000 // there are two cases: cut only within one paragraph or
2001 // more than one paragraph
2003 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2004 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2005 // only within one paragraph
2006 simple_cut_buffer = new LyXParagraph;
2007 LyXParagraph::size_type i =
2008 sel_start_cursor.pos;
2009 for (; i < sel_end_cursor.pos; ++i) {
2010 /* table stuff -- begin */
2011 if (sel_start_cursor.par->table
2012 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2013 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2014 sel_start_cursor.pos++;
2016 /* table stuff -- end */
2017 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2018 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2020 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2022 endpar = sel_end_cursor.par->Next();
2024 // cut more than one paragraph
2027 ->BreakParagraphConservative(sel_end_cursor.pos);
2028 sel_end_cursor.par = sel_end_cursor.par->Next();
2029 sel_end_cursor.pos = 0;
2031 cursor = sel_end_cursor;
2033 sel_start_cursor.par
2034 ->BreakParagraphConservative(sel_start_cursor.pos);
2035 // store the endparagraph for redoing later
2036 endpar = sel_end_cursor.par->Next(); /* needed because
2041 // store the selection
2042 simple_cut_buffer = sel_start_cursor.par
2043 ->ParFromPos(sel_start_cursor.pos)->next;
2044 simple_cut_buffer->previous = 0;
2045 sel_end_cursor.par->previous->next = 0;
2047 // cut the selection
2048 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2049 = sel_end_cursor.par;
2051 sel_end_cursor.par->previous
2052 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2054 // care about footnotes
2055 if (simple_cut_buffer->footnoteflag) {
2056 LyXParagraph * tmppar = simple_cut_buffer;
2058 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2059 tmppar = tmppar->next;
2063 // the cut selection should begin with standard layout
2064 simple_cut_buffer->Clear();
2066 // paste the paragraphs again, if possible
2068 sel_start_cursor.par->Next()->ClearParagraph();
2069 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2071 !sel_start_cursor.par->Next()->Last())
2072 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2075 // sometimes necessary
2077 sel_start_cursor.par->ClearParagraph();
2079 RedoParagraphs(sel_start_cursor, endpar);
2082 cursor = sel_start_cursor;
2083 SetCursor(cursor.par, cursor.pos);
2084 sel_cursor = cursor;
2085 UpdateCounters(cursor.row);
2088 #else ///////////////////////////////////////////////////////////////////
2090 void LyXText::CutSelection(bool doclear)
2092 // This doesn't make sense, if there is no selection
2096 // OK, we have a selection. This is always between sel_start_cursor
2097 // and sel_end cursor
2098 LyXParagraph * tmppar;
2100 // Check whether there are half footnotes in the selection
2101 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2102 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2103 tmppar = sel_start_cursor.par;
2104 while (tmppar != sel_end_cursor.par){
2105 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
2106 WriteAlert(_("Impossible operation"),
2107 _("Don't know what to do with half floats."),
2111 tmppar = tmppar->Next();
2115 /* table stuff -- begin */
2116 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
2117 if ( sel_start_cursor.par != sel_end_cursor.par) {
2118 WriteAlert(_("Impossible operation"),
2119 _("Don't know what to do with half tables."),
2123 sel_start_cursor.par->table->Reinit();
2125 /* table stuff -- end */
2127 // make sure that the depth behind the selection are restored, too
2128 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
2129 LyXParagraph * undoendpar = endpar;
2131 if (endpar && endpar->GetDepth()) {
2132 while (endpar && endpar->GetDepth()) {
2133 endpar = endpar->LastPhysicalPar()->Next();
2134 undoendpar = endpar;
2136 } else if (endpar) {
2137 endpar = endpar->Next(); // because of parindents etc.
2140 SetUndo(Undo::DELETE, sel_start_cursor
2141 .par->ParFromPos(sel_start_cursor.pos)->previous, undoendpar);
2145 // there are two cases: cut only within one paragraph or
2146 // more than one paragraph
2147 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2148 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2149 // only within one paragraph
2150 endpar = sel_start_cursor.par;
2151 cap.cutSelection(sel_start_cursor.par, &endpar,
2152 sel_start_cursor.pos, sel_end_cursor.pos,
2153 buffer->params.textclass, doclear);
2155 endpar = sel_end_cursor.par;
2157 cap.cutSelection(sel_start_cursor.par, &endpar,
2158 sel_start_cursor.pos, sel_end_cursor.pos,
2159 buffer->params.textclass, doclear);
2160 cursor.par = sel_end_cursor.par = endpar;
2161 cursor.pos = sel_end_cursor.pos;
2163 endpar = sel_end_cursor.par->Next();
2165 // sometimes necessary
2167 sel_start_cursor.par->ClearParagraph();
2169 RedoParagraphs(sel_start_cursor, endpar);
2172 cursor = sel_start_cursor;
2173 SetCursor(cursor.par, cursor.pos);
2174 sel_cursor = cursor;
2175 UpdateCounters(cursor.row);
2179 #ifdef USE_OLD_CUT_AND_PASTE
2180 void LyXText::CopySelection()
2182 // this doesnt make sense, if there is no selection
2186 // ok we have a selection. This is always between sel_start_cursor
2187 // and sel_end cursor
2188 LyXParagraph * tmppar;
2190 /* check wether there are half footnotes in the selection */
2191 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2192 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2193 tmppar = sel_start_cursor.par;
2194 while (tmppar != sel_end_cursor.par) {
2195 if (tmppar->footnoteflag !=
2196 sel_end_cursor.par->footnoteflag) {
2197 WriteAlert(_("Impossible operation"),
2198 _("Don't know what to do"
2199 " with half floats."),
2203 tmppar = tmppar->Next();
2207 /* table stuff -- begin */
2208 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2209 if ( sel_start_cursor.par != sel_end_cursor.par){
2210 WriteAlert(_("Impossible operation"),
2211 _("Don't know what to do with half tables."),
2216 /* table stuff -- end */
2218 // delete the simple_cut_buffer
2219 DeleteSimpleCutBuffer();
2221 // set the textclass
2222 simple_cut_buffer_textclass = buffer->params->textclass;
2224 // copy behind a space if there is one
2225 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2226 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2227 && (sel_start_cursor.par != sel_end_cursor.par
2228 || sel_start_cursor.pos < sel_end_cursor.pos))
2229 sel_start_cursor.pos++;
2231 // there are two cases: copy only within one paragraph
2232 // or more than one paragraph
2233 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2234 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2235 // only within one paragraph
2236 simple_cut_buffer = new LyXParagraph;
2237 LyXParagraph::size_type i = 0;
2238 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2239 sel_start_cursor.par->CopyIntoMinibuffer(i);
2240 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2243 // copy more than one paragraph
2244 // clone the paragraphs within the selection
2246 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2247 simple_cut_buffer = tmppar->Clone();
2248 LyXParagraph *tmppar2 = simple_cut_buffer;
2250 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2252 tmppar = tmppar->next;
2253 tmppar2->next = tmppar->Clone();
2254 tmppar2->next->previous = tmppar2;
2255 tmppar2 = tmppar2->next;
2259 // care about footnotes
2260 if (simple_cut_buffer->footnoteflag) {
2261 tmppar = simple_cut_buffer;
2263 tmppar->footnoteflag =
2264 LyXParagraph::NO_FOOTNOTE;
2265 tmppar = tmppar->next;
2269 // the simple_cut_buffer paragraph is too big
2270 LyXParagraph::size_type tmpi2 =
2271 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2272 for (; tmpi2; --tmpi2)
2273 simple_cut_buffer->Erase(0);
2275 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2277 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2278 while (tmppar2->size() > tmpi2) {
2279 tmppar2->Erase(tmppar2->size() - 1);
2284 #else //////////////////////////////////////////////////////////////////////
2286 void LyXText::CopySelection()
2288 // this doesnt make sense, if there is no selection
2292 // ok we have a selection. This is always between sel_start_cursor
2293 // and sel_end cursor
2294 LyXParagraph * tmppar;
2296 /* check wether there are half footnotes in the selection */
2297 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2298 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2299 tmppar = sel_start_cursor.par;
2300 while (tmppar != sel_end_cursor.par) {
2301 if (tmppar->footnoteflag !=
2302 sel_end_cursor.par->footnoteflag) {
2303 WriteAlert(_("Impossible operation"),
2304 _("Don't know what to do"
2305 " with half floats."),
2309 tmppar = tmppar->Next();
2313 /* table stuff -- begin */
2314 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2315 if ( sel_start_cursor.par != sel_end_cursor.par){
2316 WriteAlert(_("Impossible operation"),
2317 _("Don't know what to do with half tables."),
2322 /* table stuff -- end */
2324 // copy behind a space if there is one
2325 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2326 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2327 && (sel_start_cursor.par != sel_end_cursor.par
2328 || sel_start_cursor.pos < sel_end_cursor.pos))
2329 sel_start_cursor.pos++;
2333 cap.copySelection(sel_start_cursor.par, sel_end_cursor.par,
2334 sel_start_cursor.pos, sel_end_cursor.pos,
2335 buffer->params.textclass);
2339 #ifdef USE_OLD_CUT_AND_PASTE
2340 void LyXText::PasteSelection()
2342 // this does not make sense, if there is nothing to paste
2343 if (!simple_cut_buffer)
2346 LyXParagraph * tmppar;
2347 LyXParagraph * endpar;
2349 LyXCursor tmpcursor;
2351 // be carefull with footnotes in footnotes
2352 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2354 // check whether the cut_buffer includes a footnote
2355 tmppar = simple_cut_buffer;
2357 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2358 tmppar = tmppar->next;
2361 WriteAlert(_("Impossible operation"),
2362 _("Can't paste float into float!"),
2368 /* table stuff -- begin */
2369 if (cursor.par->table) {
2370 if (simple_cut_buffer->next) {
2371 WriteAlert(_("Impossible operation"),
2372 _("Table cell cannot include more than one paragraph!"),
2377 /* table stuff -- end */
2379 SetUndo(Undo::INSERT,
2380 cursor.par->ParFromPos(cursor.pos)->previous,
2381 cursor.par->ParFromPos(cursor.pos)->next);
2385 // There are two cases: cutbuffer only one paragraph or many
2386 if (!simple_cut_buffer->next) {
2387 // only within a paragraph
2389 tmppar = simple_cut_buffer->Clone();
2390 /* table stuff -- begin */
2391 bool table_too_small = false;
2392 if (tmpcursor.par->table) {
2393 while (simple_cut_buffer->size()
2394 && !table_too_small) {
2395 if (simple_cut_buffer->IsNewline(0)){
2396 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2398 simple_cut_buffer->Erase(0);
2399 if (tmpcursor.pos < tmpcursor.par->Last())
2402 table_too_small = true;
2404 // This is an attempt to fix the
2405 // "never insert a space at the
2406 // beginning of a paragraph" problem.
2407 if (tmpcursor.pos == 0
2408 && simple_cut_buffer->IsLineSeparator(0)) {
2409 simple_cut_buffer->Erase(0);
2411 simple_cut_buffer->CutIntoMinibuffer(0);
2412 simple_cut_buffer->Erase(0);
2413 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2419 /* table stuff -- end */
2420 // Some provisions should be done here for checking
2421 // if we are inserting at the beginning of a
2422 // paragraph. If there are a space at the beginning
2423 // of the text to insert and we are inserting at
2424 // the beginning of the paragraph the space should
2426 while (simple_cut_buffer->size()) {
2427 // This is an attempt to fix the
2428 // "never insert a space at the
2429 // beginning of a paragraph" problem.
2430 if (tmpcursor.pos == 0
2431 && simple_cut_buffer->IsLineSeparator(0)) {
2432 simple_cut_buffer->Erase(0);
2434 simple_cut_buffer->CutIntoMinibuffer(0);
2435 simple_cut_buffer->Erase(0);
2436 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2441 delete simple_cut_buffer;
2442 simple_cut_buffer = tmppar;
2443 endpar = tmpcursor.par->Next();
2448 // make a copy of the simple cut_buffer
2449 tmppar = simple_cut_buffer;
2450 LyXParagraph * simple_cut_clone = tmppar->Clone();
2451 LyXParagraph * tmppar2 = simple_cut_clone;
2452 if (cursor.par->footnoteflag){
2453 tmppar->footnoteflag = cursor.par->footnoteflag;
2454 tmppar->footnotekind = cursor.par->footnotekind;
2456 while (tmppar->next) {
2457 tmppar = tmppar->next;
2458 tmppar2->next = tmppar->Clone();
2459 tmppar2->next->previous = tmppar2;
2460 tmppar2 = tmppar2->next;
2461 if (cursor.par->footnoteflag){
2462 tmppar->footnoteflag = cursor.par->footnoteflag;
2463 tmppar->footnotekind = cursor.par->footnotekind;
2467 // make sure there is no class difference
2468 cap.SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2469 buffer->params->textclass,
2472 // make the simple_cut_buffer exactly the same layout than
2473 // the cursor paragraph
2474 simple_cut_buffer->MakeSameLayout(cursor.par);
2476 // find the end of the buffer
2477 LyXParagraph * lastbuffer = simple_cut_buffer;
2478 while (lastbuffer->Next())
2479 lastbuffer = lastbuffer->Next();
2481 bool paste_the_end = false;
2483 // open the paragraph for inserting the simple_cut_buffer
2485 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2486 cursor.par->BreakParagraphConservative(cursor.pos);
2487 paste_the_end = true;
2490 // set the end for redoing later
2491 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2494 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2495 cursor.par->ParFromPos(cursor.pos)->next;
2496 cursor.par->ParFromPos(cursor.pos)->next->previous =
2497 lastbuffer->ParFromPos(lastbuffer->Last());
2499 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2500 simple_cut_buffer->previous =
2501 cursor.par->ParFromPos(cursor.pos);
2503 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2504 lastbuffer = cursor.par;
2506 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2508 // store the new cursor position
2509 tmpcursor.par = lastbuffer;
2510 tmpcursor.pos = lastbuffer->Last();
2512 // maybe some pasting
2513 if (lastbuffer->Next() && paste_the_end) {
2514 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2515 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2517 } else if (!lastbuffer->Next()->Last()) {
2518 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2519 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2521 } else if (!lastbuffer->Last()) {
2522 lastbuffer->MakeSameLayout(lastbuffer->next);
2523 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2526 lastbuffer->Next()->ClearParagraph();
2529 // restore the simple cut buffer
2530 simple_cut_buffer = simple_cut_clone;
2533 RedoParagraphs(cursor, endpar);
2535 SetCursor(cursor.par, cursor.pos);
2538 sel_cursor = cursor;
2539 SetCursor(tmpcursor.par, tmpcursor.pos);
2541 UpdateCounters(cursor.row);
2544 #else ////////////////////////////////////////////////////////////////////
2546 void LyXText::PasteSelection()
2550 // this does not make sense, if there is nothing to paste
2551 if (!cap.checkPastePossible(cursor.par, cursor.pos))
2554 SetUndo(Undo::INSERT,
2555 cursor.par->ParFromPos(cursor.pos)->previous,
2556 cursor.par->ParFromPos(cursor.pos)->next);
2558 LyXParagraph *endpar;
2559 LyXParagraph *actpar = cursor.par;
2560 int endpos = cursor.pos;
2562 cap.pasteSelection(&actpar, &endpar, endpos, buffer->params.textclass);
2564 RedoParagraphs(cursor, endpar);
2566 SetCursor(cursor.par, cursor.pos);
2569 sel_cursor = cursor;
2570 SetCursor(actpar, endpos);
2572 UpdateCounters(cursor.row);
2576 // returns a pointer to the very first LyXParagraph
2577 LyXParagraph * LyXText::FirstParagraph() const
2579 return buffer->paragraph;
2583 // returns true if the specified string is at the specified position
2584 bool LyXText::IsStringInText(LyXParagraph * par,
2585 LyXParagraph::size_type pos,
2586 char const * str) const
2590 while (pos + i < par->Last() && str[i] &&
2591 str[i] == par->GetChar(pos + i)) {
2601 // sets the selection over the number of characters of string, no check!!
2602 void LyXText::SetSelectionOverString(char const * string)
2604 sel_cursor = cursor;
2605 for (int i = 0; string[i]; ++i)
2611 // simple replacing. The font of the first selected character is used
2612 void LyXText::ReplaceSelectionWithString(char const * str)
2617 if (!selection) { // create a dummy selection
2618 sel_end_cursor = cursor;
2619 sel_start_cursor = cursor;
2622 // Get font setting before we cut
2623 LyXParagraph::size_type pos = sel_end_cursor.pos;
2624 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2626 // Insert the new string
2627 for (int i = 0; str[i]; ++i) {
2628 sel_end_cursor.par->InsertChar(pos, str[i]);
2629 sel_end_cursor.par->SetFont(pos, font);
2633 // Cut the selection
2640 // if the string can be found: return true and set the cursor to
2642 bool LyXText::SearchForward(char const * str) const
2644 LyXParagraph * par = cursor.par;
2645 LyXParagraph::size_type pos = cursor.pos;
2646 while (par && !IsStringInText(par, pos, str)) {
2647 if (pos < par->Last() - 1)
2655 SetCursor(par, pos);
2663 bool LyXText::SearchBackward(char const * string) const
2665 LyXParagraph * par = cursor.par;
2666 int pos = cursor.pos;
2672 // We skip empty paragraphs (Asger)
2674 par = par->Previous();
2676 pos = par->Last() - 1;
2677 } while (par && pos < 0);
2679 } while (par && !IsStringInText(par, pos, string));
2682 SetCursor(par, pos);
2689 // needed to insert the selection
2690 void LyXText::InsertStringA(string const & str)
2692 LyXParagraph * par = cursor.par;
2693 LyXParagraph::size_type pos = cursor.pos;
2694 LyXParagraph::size_type a = 0;
2696 LyXParagraph * endpar = cursor.par->Next();
2701 textclasslist.Style(buffer->params.textclass,
2702 cursor.par->GetLayout()).isEnvironment();
2703 // only to be sure, should not be neccessary
2706 // insert the string, don't insert doublespace
2707 string::size_type i = 0;
2708 while (i < str.length()) {
2709 if (str[i] != '\n') {
2711 && i + 1 < str.length() && str[i + 1] != ' '
2712 && pos && par->GetChar(pos - 1)!= ' ') {
2713 par->InsertChar(pos,' ');
2714 par->SetFont(pos, current_font);
2716 } else if (par->table) {
2717 if (str[i] == '\t') {
2718 while((pos < par->size()) &&
2719 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2721 if (pos < par->size())
2723 else // no more fields to fill skip the rest
2725 } else if ((str[i] != 13) &&
2726 ((str[i] & 127) >= ' ')) {
2727 par->InsertChar(pos, str[i]);
2728 par->SetFont(pos, current_font);
2731 } else if (str[i] == ' ') {
2732 InsetSpecialChar * new_inset =
2733 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2734 if (par->InsertInsetAllowed(new_inset)) {
2735 par->InsertChar(pos, LyXParagraph::META_INSET);
2736 par->SetFont(pos, current_font);
2737 par->InsertInset(pos, new_inset);
2742 } else if (str[i] == '\t') {
2743 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2744 InsetSpecialChar * new_inset =
2745 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2746 if (par->InsertInsetAllowed(new_inset)) {
2747 par->InsertChar(pos, LyXParagraph::META_INSET);
2748 par->SetFont(pos, current_font);
2749 par->InsertInset(pos, new_inset);
2755 } else if (str[i] != 13 &&
2756 // Ignore unprintables
2757 (str[i] & 127) >= ' ') {
2758 par->InsertChar(pos, str[i]);
2759 par->SetFont(pos, current_font);
2764 if (i + 1 >= str.length()) {
2768 while((pos < par->size()) &&
2769 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2772 cell = NumberOfCell(par, pos);
2773 while((pos < par->size()) &&
2774 !(par->table->IsFirstCell(cell))) {
2776 while((pos < par->size()) &&
2777 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2780 cell = NumberOfCell(par, pos);
2782 if (pos >= par->size())
2783 // no more fields to fill skip the rest
2786 if (!par->size()) { // par is empty
2787 InsetSpecialChar * new_inset =
2788 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2789 if (par->InsertInsetAllowed(new_inset)) {
2790 par->InsertChar(pos, LyXParagraph::META_INSET);
2791 par->SetFont(pos, current_font);
2792 par->InsertInset(pos, new_inset);
2798 par->BreakParagraph(pos, flag);
2806 RedoParagraphs(cursor, endpar);
2807 SetCursor(cursor.par, cursor.pos);
2808 sel_cursor = cursor;
2809 SetCursor(par, pos);
2814 /* turns double-CR to single CR, others where converted into one blank and 13s
2815 * that are ignored .Double spaces are also converted into one. Spaces at
2816 * the beginning of a paragraph are forbidden. tabs are converted into one
2817 * space. then InsertStringA is called */
2818 void LyXText::InsertStringB(string const & s)
2821 LyXParagraph * par = cursor.par;
2822 string::size_type i = 1;
2823 while (i < str.length()) {
2824 if (str[i] == '\t' && !par->table)
2826 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2828 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2829 if (str[i + 1] != '\n') {
2830 if (str[i - 1] != ' ')
2835 while (i + 1 < str.length()
2836 && (str[i + 1] == ' '
2837 || str[i + 1] == '\t'
2838 || str[i + 1] == '\n'
2839 || str[i + 1] == 13)) {
2850 bool LyXText::GotoNextError() const
2852 LyXCursor res = cursor;
2854 if (res.pos < res.par->Last() - 1) {
2858 res.par = res.par->Next();
2863 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2864 && res.par->GetInset(res.pos)->AutoDelete()));
2867 SetCursor(res.par, res.pos);
2874 bool LyXText::GotoNextNote() const
2876 LyXCursor res = cursor;
2878 if (res.pos < res.par->Last() - 1) {
2881 res.par = res.par->Next();
2886 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2887 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2890 SetCursor(res.par, res.pos);
2897 void LyXText::CheckParagraph(LyXParagraph * par,
2898 LyXParagraph::size_type pos)
2901 LyXCursor tmpcursor;
2903 /* table stuff -- begin*/
2906 CheckParagraphInTable(par, pos);
2909 /* table stuff -- end*/
2912 LyXParagraph::size_type z;
2913 Row * row = GetRow(par, pos, y);
2915 // is there a break one row above
2916 if (row->previous && row->previous->par == row->par) {
2917 z = NextBreakPoint(row->previous, paperwidth);
2918 if ( z >= row->pos) {
2919 // set the dimensions of the row above
2920 y -= row->previous->height;
2922 refresh_row = row->previous;
2923 status = LyXText::NEED_MORE_REFRESH;
2925 BreakAgain(row->previous);
2927 // set the cursor again. Otherwise
2928 // dangling pointers are possible
2929 SetCursor(cursor.par, cursor.pos);
2930 sel_cursor = cursor;
2935 int tmpheight = row->height;
2936 LyXParagraph::size_type tmplast = RowLast(row);
2941 if (row->height == tmpheight && RowLast(row) == tmplast)
2942 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2944 status = LyXText::NEED_MORE_REFRESH;
2946 // check the special right address boxes
2947 if (textclasslist.Style(buffer->params.textclass,
2948 par->GetLayout()).margintype
2949 == MARGIN_RIGHT_ADDRESS_BOX) {
2950 tmpcursor.par = par;
2951 tmpcursor.row = row;
2954 tmpcursor.x_fix = 0;
2955 tmpcursor.pos = pos;
2956 RedoDrawingOfParagraph(tmpcursor);
2961 // set the cursor again. Otherwise dangling pointers are possible
2962 // also set the selection
2966 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2967 sel_cursor = cursor;
2968 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2969 sel_start_cursor = cursor;
2970 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2971 sel_end_cursor = cursor;
2972 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2973 last_sel_cursor = cursor;
2976 SetCursorIntern(cursor.par, cursor.pos);
2980 // returns 0 if inset wasn't found
2981 int LyXText::UpdateInset(Inset * inset)
2983 // first check the current paragraph
2984 int pos = cursor.par->GetPositionOfInset(inset);
2986 CheckParagraph(cursor.par, pos);
2990 // check every paragraph
2992 LyXParagraph * par = FirstParagraph();
2994 // make sure the paragraph is open
2995 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2996 pos = par->GetPositionOfInset(inset);
2998 CheckParagraph(par, pos);
3009 void LyXText::SetCursor(LyXParagraph * par,
3010 LyXParagraph::size_type pos, bool setfont) const
3012 LyXCursor old_cursor = cursor;
3013 SetCursorIntern(par, pos, setfont);
3014 DeleteEmptyParagraphMechanism(old_cursor);
3018 void LyXText::SetCursor(LyXCursor & cur, LyXParagraph * par,
3019 LyXParagraph::size_type pos) const
3021 // correct the cursor position if impossible
3022 if (pos > par->Last()){
3023 LyXParagraph * tmppar = par->ParFromPos(pos);
3024 pos = par->PositionInParFromPos(pos);
3027 if (par->IsDummy() && par->previous &&
3028 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3029 while (par->previous &&
3030 ((par->previous->IsDummy() &&
3031 (par->previous->previous->footnoteflag ==
3032 LyXParagraph::CLOSED_FOOTNOTE)) ||
3033 (par->previous->footnoteflag ==
3034 LyXParagraph::CLOSED_FOOTNOTE))) {
3035 par = par->previous ;
3036 if (par->IsDummy() &&
3037 (par->previous->footnoteflag ==
3038 LyXParagraph::CLOSED_FOOTNOTE))
3039 pos += par->size() + 1;
3041 if (par->previous) {
3042 par = par->previous;
3044 pos += par->size() + 1;
3050 /* get the cursor y position in text */
3052 Row * row = GetRow(par, pos, y);
3053 /* y is now the beginning of the cursor row */
3055 /* y is now the cursor baseline */
3058 /* now get the cursors x position */
3060 float fill_separator, fill_hfill, fill_label_hfill;
3061 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3062 LyXParagraph::size_type cursor_vpos;
3063 LyXParagraph::size_type last = RowLastPrintable(row);
3065 if (pos > last + 1) // This shouldn't happen.
3068 if (last < row->pos)
3070 else if ((pos > last) ||
3071 ((pos - 1 >= row->pos) &&
3072 (row->par->IsSeparator(pos) ||
3073 (row->par->table && row->par->IsNewline(pos)))))
3074 /// Place cursor after char at (logical) position pos-1
3075 cursor_vpos = !(bidi_level(pos-1) % 2)
3076 ? log2vis(pos-1) + 1 : log2vis(pos-1);
3078 /// Place cursor before char at (logical) position pos
3079 cursor_vpos = !(bidi_level(pos) % 2)
3080 ? log2vis(pos) : log2vis(pos) + 1;
3082 /* table stuff -- begin*/
3083 if (row->par->table) {
3084 int cell = NumberOfCell(row->par, row->pos);
3086 x += row->par->table->GetBeginningOfTextInCell(cell);
3087 for (LyXParagraph::size_type vpos = row->pos;
3088 vpos < cursor_vpos; ++vpos) {
3089 pos = vis2log(vpos);
3090 if (row->par->IsNewline(pos)) {
3091 x = x_old + row->par->table->WidthOfColumn(cell);
3094 x += row->par->table->GetBeginningOfTextInCell(cell);
3096 x += SingleWidth(row->par, pos);
3100 /* table stuff -- end*/
3101 LyXParagraph::size_type main_body =
3102 BeginningOfMainBody(row->par);
3103 if ((main_body > 0) &&
3104 ((main_body-1 > last) ||
3105 !row->par->IsLineSeparator(main_body-1)))
3108 for (LyXParagraph::size_type vpos = row->pos;
3109 vpos < cursor_vpos; ++vpos) {
3110 pos = vis2log(vpos);
3111 if (main_body > 0 && pos == main_body-1) {
3112 x += fill_label_hfill +
3113 lyxfont::width(textclasslist.Style(
3114 buffer->params.textclass,
3115 row->par->GetLayout())
3117 GetFont(row->par, -2));
3118 if (row->par->IsLineSeparator(main_body-1))
3119 x -= SingleWidth(row->par,main_body-1);
3121 if (HfillExpansion(row, pos)) {
3122 x += SingleWidth(row->par, pos);
3123 if (pos >= main_body)
3126 x += fill_label_hfill;
3127 } else if (row->par->IsSeparator(pos)) {
3128 x += SingleWidth(row->par, pos);
3129 if (pos >= main_body)
3130 x += fill_separator;
3132 x += SingleWidth(row->par, pos);
3142 void LyXText::SetCursorIntern(LyXParagraph * par,
3143 LyXParagraph::size_type pos, bool setfont) const
3145 SetCursor(cursor, par, pos);
3146 #warning Remove this when verified working (Jug 20000413)
3148 // correct the cursor position if impossible
3149 if (pos > par->Last()){
3150 LyXParagraph * tmppar = par->ParFromPos(pos);
3151 pos = par->PositionInParFromPos(pos);
3154 if (par->IsDummy() && par->previous &&
3155 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3156 while (par->previous &&
3157 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3158 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3159 par = par->previous ;
3160 if (par->IsDummy() &&
3161 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3162 pos += par->size() + 1;
3164 if (par->previous) {
3165 par = par->previous;
3167 pos += par->size() + 1;
3173 /* get the cursor y position in text */
3175 Row * row = GetRow(par, pos, y);
3176 /* y is now the beginning of the cursor row */
3178 /* y is now the cursor baseline */
3181 /* now get the cursors x position */
3183 float fill_separator, fill_hfill, fill_label_hfill;
3184 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3185 LyXParagraph::size_type cursor_vpos;
3186 LyXParagraph::size_type last = RowLastPrintable(row);
3188 if (pos > last + 1) // This shouldn't happen.
3191 if (last < row->pos)
3193 else if (pos > last ||
3194 (pos - 1 >= row->pos &&
3195 (row->par->IsSeparator(pos) ||
3196 (row->par->table && row->par->IsNewline(pos))
3198 /// Place cursor after char at (logical) position pos-1
3199 cursor_vpos = (bidi_level(pos-1) % 2 == 0)
3200 ? log2vis(pos-1) + 1 : log2vis(pos-1);
3202 /// Place cursor before char at (logical) position pos
3203 cursor_vpos = (bidi_level(pos) % 2 == 0)
3204 ? log2vis(pos) : log2vis(pos) + 1;
3206 /* table stuff -- begin*/
3207 if (row->par->table) {
3208 int cell = NumberOfCell(row->par, row->pos);
3210 x += row->par->table->GetBeginningOfTextInCell(cell);
3211 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3212 pos = vis2log(vpos);
3213 if (row->par->IsNewline(pos)) {
3214 x = x_old + row->par->table->WidthOfColumn(cell);
3217 x += row->par->table->GetBeginningOfTextInCell(cell);
3219 x += SingleWidth(row->par, pos);
3223 /* table stuff -- end*/
3224 LyXParagraph::size_type main_body =
3225 BeginningOfMainBody(row->par);
3226 if (main_body > 0 &&
3227 (main_body-1 > last ||
3228 !row->par->IsLineSeparator(main_body-1)))
3231 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3232 pos = vis2log(vpos);
3233 if (main_body > 0 && pos == main_body-1) {
3234 x += fill_label_hfill +
3235 lyxfont::width(textclasslist
3236 .Style(buffer->params.textclass,
3237 row->par->GetLayout())
3239 GetFont(row->par, -2));
3240 if (row->par->IsLineSeparator(main_body-1))
3241 x -= SingleWidth(row->par, main_body-1);
3243 if (HfillExpansion(row, pos)) {
3244 x += SingleWidth(row->par, pos);
3245 if (pos >= main_body)
3248 x += fill_label_hfill;
3250 else if (row->par->IsSeparator(pos)) {
3251 x += SingleWidth(row->par, pos);
3252 if (pos >= main_body)
3253 x += fill_separator;
3255 x += SingleWidth(row->par, pos);
3261 cursor.x_fix = cursor.x;
3266 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3267 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3269 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3270 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3272 current_font = cursor.par->GetFontSettings(cursor.pos);
3273 real_current_font = GetFont(cursor.par, cursor.pos);
3279 void LyXText::SetCursorFromCoordinates(int x, long y) const
3281 LyXCursor old_cursor = cursor;
3283 /* get the row first */
3285 Row * row = GetRowNearY(y);
3287 cursor.par = row->par;
3289 int column = GetColumnNearX(row, x);
3290 cursor.pos = row->pos + column;
3292 cursor.y = y + row->baseline;
3297 (cursor.pos == cursor.par->Last()
3298 || cursor.par->IsSeparator(cursor.pos)
3299 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3300 && !cursor.par->IsSeparator(cursor.pos))
3301 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3303 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3304 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3306 current_font = cursor.par->GetFontSettings(cursor.pos);
3307 real_current_font = GetFont(cursor.par, cursor.pos);
3309 DeleteEmptyParagraphMechanism(old_cursor);
3312 void LyXText::SetCursorFromCoordinates(LyXCursor & cur, int x, long y) const
3314 /* get the row first */
3316 Row * row = GetRowNearY(y);
3317 int column = GetColumnNearX(row, x);
3320 cur.pos = row->pos + column;
3322 cur.y = y + row->baseline;
3327 void LyXText::CursorLeft() const
3330 if (cursor.par->table) {
3331 int cell = NumberOfCell(cursor.par, cursor.pos);
3332 if (cursor.par->table->IsContRow(cell) &&
3333 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell)) < 0) {
3340 void LyXText::CursorLeftIntern() const
3342 if (cursor.pos > 0) {
3343 SetCursor(cursor.par, cursor.pos - 1);
3345 else if (cursor.par->Previous()) { // steps into the above paragraph.
3346 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3351 void LyXText::CursorRight() const
3353 CursorRightIntern();
3354 if (cursor.par->table) {
3355 int cell = NumberOfCell(cursor.par, cursor.pos);
3356 if (cursor.par->table->IsContRow(cell) &&
3357 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3364 void LyXText::CursorRightIntern() const
3366 if (cursor.pos < cursor.par->Last()) {
3367 SetCursor(cursor.par, cursor.pos + 1);
3369 else if (cursor.par->Next()) {
3370 SetCursor(cursor.par->Next(), 0);
3375 void LyXText::CursorUp() const
3377 SetCursorFromCoordinates(cursor.x_fix,
3378 cursor.y - cursor.row->baseline - 1);
3379 if (cursor.par->table) {
3380 int cell = NumberOfCell(cursor.par, cursor.pos);
3381 if (cursor.par->table->IsContRow(cell) &&
3382 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3389 void LyXText::CursorDown() const
3391 if (cursor.par->table &&
3392 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3395 SetCursorFromCoordinates(cursor.x_fix,
3396 cursor.y - cursor.row->baseline
3397 + cursor.row->height + 1);
3398 if (cursor.par->table) {
3399 int cell = NumberOfCell(cursor.par, cursor.pos);
3400 int cell_above = cursor.par->table->GetCellAbove(cell);
3401 while(cursor.par->table &&
3402 cursor.par->table->IsContRow(cell) &&
3403 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3404 SetCursorFromCoordinates(cursor.x_fix,
3405 cursor.y - cursor.row->baseline
3406 + cursor.row->height + 1);
3407 if (cursor.par->table) {
3408 cell = NumberOfCell(cursor.par, cursor.pos);
3409 cell_above = cursor.par->table->GetCellAbove(cell);
3416 void LyXText::CursorUpParagraph() const
3418 if (cursor.pos > 0) {
3419 SetCursor(cursor.par, 0);
3421 else if (cursor.par->Previous()) {
3422 SetCursor(cursor.par->Previous(), 0);
3427 void LyXText::CursorDownParagraph() const
3429 if (cursor.par->Next()) {
3430 SetCursor(cursor.par->Next(), 0);
3432 SetCursor(cursor.par, cursor.par->Last());
3438 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3440 // Would be wrong to delete anything if we have a selection.
3441 if (selection) return;
3443 // We allow all kinds of "mumbo-jumbo" when freespacing.
3444 if (textclasslist.Style(buffer->params.textclass,
3445 old_cursor.par->GetLayout()).free_spacing)
3448 bool deleted = false;
3450 /* Ok I'll put some comments here about what is missing.
3451 I have fixed BackSpace (and thus Delete) to not delete
3452 double-spaces automagically. I have also changed Cut,
3453 Copy and Paste to hopefully do some sensible things.
3454 There are still some small problems that can lead to
3455 double spaces stored in the document file or space at
3456 the beginning of paragraphs. This happens if you have
3457 the cursor betwenn to spaces and then save. Or if you
3458 cut and paste and the selection have a space at the
3459 beginning and then save right after the paste. I am
3460 sure none of these are very hard to fix, but I will
3461 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3462 that I can get some feedback. (Lgb)
3465 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3466 // delete the LineSeparator.
3469 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3470 // delete the LineSeparator.
3473 // If the pos around the old_cursor were spaces, delete one of them.
3474 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3476 if (old_cursor.pos > 0
3477 && old_cursor.pos < old_cursor.par->Last()
3478 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3479 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3480 old_cursor.par->Erase(old_cursor.pos - 1);
3481 RedoParagraphs(old_cursor, old_cursor.par->Next());
3483 if (old_cursor.par == cursor.par &&
3484 cursor.pos > old_cursor.pos) {
3485 SetCursorIntern(cursor.par, cursor.pos - 1);
3487 SetCursorIntern(cursor.par, cursor.pos);
3492 // Do not delete empty paragraphs with keepempty set.
3493 if ((textclasslist.Style(buffer->params.textclass,
3494 old_cursor.par->GetLayout())).keepempty)
3497 LyXCursor tmpcursor;
3499 if (old_cursor.par != cursor.par) {
3500 if ( (old_cursor.par->Last() == 0
3501 || (old_cursor.par->Last() == 1
3502 && old_cursor.par->IsLineSeparator(0)))
3503 && old_cursor.par->FirstPhysicalPar()
3504 == old_cursor.par->LastPhysicalPar()) {
3505 // ok, we will delete anything
3507 // make sure that you do not delete any environments
3508 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3509 !(old_cursor.row->previous
3510 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3511 && !(old_cursor.row->next
3512 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3513 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3514 && ((old_cursor.row->previous
3515 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3516 || (old_cursor.row->next
3517 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3519 status = LyXText::NEED_MORE_REFRESH;
3522 if (old_cursor.row->previous) {
3523 refresh_row = old_cursor.row->previous;
3524 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3526 cursor = old_cursor; // that undo can restore the right cursor position
3527 LyXParagraph * endpar = old_cursor.par->next;
3528 if (endpar && endpar->GetDepth()) {
3529 while (endpar && endpar->GetDepth()) {
3530 endpar = endpar->LastPhysicalPar()->Next();
3533 SetUndo(Undo::DELETE,
3534 old_cursor.par->previous,
3539 RemoveRow(old_cursor.row);
3540 if (buffer->paragraph == old_cursor.par) {
3541 buffer->paragraph = buffer->paragraph->next;
3544 delete old_cursor.par;
3546 /* Breakagain the next par. Needed
3547 * because of the parindent that
3548 * can occur or dissappear. The
3549 * next row can change its height,
3550 * if there is another layout before */
3551 if (refresh_row->next) {
3552 BreakAgain(refresh_row->next);
3553 UpdateCounters(refresh_row);
3555 SetHeightOfRow(refresh_row);
3557 refresh_row = old_cursor.row->next;
3558 refresh_y = old_cursor.y - old_cursor.row->baseline;
3561 cursor = old_cursor; // that undo can restore the right cursor position
3562 LyXParagraph *endpar = old_cursor.par->next;
3563 if (endpar && endpar->GetDepth()) {
3564 while (endpar && endpar->GetDepth()) {
3565 endpar = endpar->LastPhysicalPar()->Next();
3568 SetUndo(Undo::DELETE,
3569 old_cursor.par->previous,
3574 RemoveRow(old_cursor.row);
3576 if (buffer->paragraph == old_cursor.par) {
3577 buffer->paragraph = buffer->paragraph->next;
3579 delete old_cursor.par;
3581 /* Breakagain the next par. Needed
3582 because of the parindent that can
3583 occur or dissappear.
3584 The next row can change its height,
3585 if there is another layout before
3588 BreakAgain(refresh_row);
3589 UpdateCounters(refresh_row->previous);
3595 SetCursorIntern(cursor.par, cursor.pos);
3597 if (sel_cursor.par == old_cursor.par
3598 && sel_cursor.pos == sel_cursor.pos) {
3599 // correct selection
3600 sel_cursor = cursor;
3605 if (old_cursor.par->ClearParagraph()) {
3606 RedoParagraphs(old_cursor, old_cursor.par->Next());
3608 SetCursorIntern(cursor.par, cursor.pos);
3609 sel_cursor = cursor;
3616 LyXParagraph * LyXText::GetParFromID(int id)
3618 LyXParagraph * result = FirstParagraph();
3619 while (result && result->id() != id)
3620 result = result->next;
3626 bool LyXText::TextUndo()
3628 // returns false if no undo possible
3629 Undo * undo = buffer->undostack.pop();
3634 .push(CreateUndo(undo->kind,
3635 GetParFromID(undo->number_of_before_par),
3636 GetParFromID(undo->number_of_behind_par)));
3638 return TextHandleUndo(undo);
3642 bool LyXText::TextRedo()
3644 // returns false if no redo possible
3645 Undo * undo = buffer->redostack.pop();
3650 .push(CreateUndo(undo->kind,
3651 GetParFromID(undo->number_of_before_par),
3652 GetParFromID(undo->number_of_behind_par)));
3654 return TextHandleUndo(undo);
3658 bool LyXText::TextHandleUndo(Undo * undo)
3660 // returns false if no undo possible
3661 bool result = false;
3663 LyXParagraph * before =
3664 GetParFromID(undo->number_of_before_par);
3665 LyXParagraph * behind =
3666 GetParFromID(undo->number_of_behind_par);
3667 LyXParagraph * tmppar;
3668 LyXParagraph * tmppar2;
3669 LyXParagraph * endpar;
3670 LyXParagraph * tmppar5;
3672 // if there's no before take the beginning
3673 // of the document for redoing
3675 SetCursorIntern(FirstParagraph(), 0);
3677 // replace the paragraphs with the undo informations
3679 LyXParagraph * tmppar3 = undo->par;
3680 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3681 LyXParagraph * tmppar4 = tmppar3;
3683 while (tmppar4->next)
3684 tmppar4 = tmppar4->next;
3685 } // get last undo par
3687 // now remove the old text if there is any
3688 if (before != behind || (!behind && !before)){
3690 tmppar5 = before->next;
3692 tmppar5 = buffer->paragraph;
3694 while (tmppar5 && tmppar5 != behind){
3696 tmppar5 = tmppar5->next;
3697 // a memory optimization for edit: Only layout information
3698 // is stored in the undo. So restore the text informations.
3699 if (undo->kind == Undo::EDIT) {
3700 tmppar2->setContentsFromPar(tmppar);
3701 tmppar->clearContents();
3702 //tmppar2->text = tmppar->text;
3703 //tmppar->text.clear();
3704 tmppar2 = tmppar2->next;
3706 if ( currentrow && currentrow->par == tmppar )
3707 currentrow = currentrow -> previous;
3708 // Commenting out this might remove the error
3709 // reported by Purify, but it might also
3710 // introduce a memory leak. We need to
3716 // put the new stuff in the list if there is one
3719 before->next = tmppar3;
3721 buffer->paragraph = tmppar3;
3722 tmppar3->previous = before;
3726 buffer->paragraph = behind;
3729 tmppar4->next = behind;
3731 behind->previous = tmppar4;
3735 // Set the cursor for redoing
3737 SetCursorIntern(before->FirstSelfrowPar(), 0);
3738 // check wether before points to a closed float and open it if necessary
3739 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3740 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3742 while (tmppar4->previous &&
3743 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3744 tmppar4 = tmppar4->previous;
3745 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3746 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3747 tmppar4 = tmppar4->next;
3752 // open a cosed footnote at the end if necessary
3753 if (behind && behind->previous &&
3754 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3755 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3756 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3757 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3758 behind = behind->next;
3762 // calculate the endpar for redoing the paragraphs.
3764 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3765 endpar = behind->LastPhysicalPar()->Next();
3767 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3772 tmppar = GetParFromID(undo->number_of_cursor_par);
3773 RedoParagraphs(cursor, endpar);
3775 SetCursorIntern(tmppar, undo->cursor_pos);
3776 UpdateCounters(cursor.row);
3786 void LyXText::FinishUndo()
3788 // makes sure the next operation will be stored
3789 undo_finished = True;
3793 void LyXText::FreezeUndo()
3795 // this is dangerous and for internal use only
3800 void LyXText::UnFreezeUndo()
3802 // this is dangerous and for internal use only
3803 undo_frozen = false;
3807 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3808 LyXParagraph const * behind) const
3811 buffer->undostack.push(CreateUndo(kind, before, behind));
3812 buffer->redostack.clear();
3816 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3817 LyXParagraph const * behind)
3819 buffer->redostack.push(CreateUndo(kind, before, behind));
3823 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3824 LyXParagraph const * behind) const
3826 int before_number = -1;
3827 int behind_number = -1;
3829 before_number = before->id();
3831 behind_number = behind->id();
3832 // Undo::EDIT and Undo::FINISH are
3833 // always finished. (no overlapping there)
3834 // overlapping only with insert and delete inside one paragraph:
3835 // Nobody wants all removed character
3836 // appear one by one when undoing.
3837 // EDIT is special since only layout information, not the
3838 // contents of a paragaph are stored.
3839 if (!undo_finished && kind != Undo::EDIT &&
3840 kind != Undo::FINISH){
3841 // check wether storing is needed
3842 if (!buffer->undostack.empty() &&
3843 buffer->undostack.top()->kind == kind &&
3844 buffer->undostack.top()->number_of_before_par == before_number &&
3845 buffer->undostack.top()->number_of_behind_par == behind_number ){
3850 // create a new Undo
3851 LyXParagraph * undopar;
3852 LyXParagraph * tmppar;
3853 LyXParagraph * tmppar2;
3855 LyXParagraph * start = 0;
3856 LyXParagraph * end = 0;
3859 start = before->next;
3861 start = FirstParagraph();
3863 end = behind->previous;
3865 end = FirstParagraph();
3871 && start != end->next
3872 && (before != behind || (!before && !behind))) {
3874 tmppar2 = tmppar->Clone();
3875 tmppar2->id(tmppar->id());
3877 // a memory optimization: Just store the layout information
3879 if (kind == Undo::EDIT){
3880 //tmppar2->text.clear();
3881 tmppar2->clearContents();
3886 while (tmppar != end && tmppar->next) {
3887 tmppar = tmppar->next;
3888 tmppar2->next = tmppar->Clone();
3889 tmppar2->next->id(tmppar->id());
3890 // a memory optimization: Just store the layout
3891 // information when only edit
3892 if (kind == Undo::EDIT){
3893 //tmppar2->next->text.clear();
3894 tmppar2->clearContents();
3896 tmppar2->next->previous = tmppar2;
3897 tmppar2 = tmppar2->next;
3901 undopar = 0; // nothing to replace (undo of delete maybe)
3903 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3904 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3906 Undo * undo = new Undo(kind,
3907 before_number, behind_number,
3908 cursor_par, cursor_pos,
3911 undo_finished = false;
3916 void LyXText::SetCursorParUndo()
3918 SetUndo(Undo::FINISH,
3919 cursor.par->ParFromPos(cursor.pos)->previous,
3920 cursor.par->ParFromPos(cursor.pos)->next);
3924 void LyXText::RemoveTableRow(LyXCursor * cur) const
3930 // move to the previous row
3931 int cell_act = NumberOfCell(cur->par, cur->pos);
3934 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3937 !cur->par->table->IsFirstCell(cell_act)) {
3939 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3944 // now we have to pay attention if the actual table is the
3945 // main row of TableContRows and if yes to delete all of them
3950 // delete up to the next row
3951 while (cur->pos < cur->par->Last() &&
3953 || !cur->par->table->IsFirstCell(cell_act))) {
3954 while (cur->pos < cur->par->Last() &&
3955 !cur->par->IsNewline(cur->pos))
3956 cur->par->Erase(cur->pos);
3959 if (cur->pos < cur->par->Last())
3960 cur->par->Erase(cur->pos);
3962 if (cur->pos && cur->pos == cur->par->Last()) {
3964 cur->par->Erase(cur->pos); // no newline at very end!
3966 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3967 !cur->par->table->IsContRow(cell_org) &&
3968 cur->par->table->IsContRow(cell));
3969 cur->par->table->DeleteRow(cell_org);
3974 bool LyXText::IsEmptyTableCell() const
3976 LyXParagraph::size_type pos = cursor.pos - 1;
3977 while (pos >= 0 && pos < cursor.par->Last()
3978 && !cursor.par->IsNewline(pos))
3980 return cursor.par->IsNewline(pos + 1);
3984 void LyXText::toggleAppendix(){
3985 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3986 bool start = !par->start_of_appendix;
3988 // ensure that we have only one start_of_appendix in this document
3989 LyXParagraph * tmp = FirstParagraph();
3990 for (; tmp; tmp = tmp->next)
3991 tmp->start_of_appendix = 0;
3992 par->start_of_appendix = start;
3994 // we can set the refreshing parameters now
3995 status = LyXText::NEED_MORE_REFRESH;
3997 refresh_row = 0; // not needed for full update
3999 SetCursor(cursor.par, cursor.pos);