1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include FORMS_H_LOCATION
18 #pragma implementation "lyxtext.h"
22 #include "lyxparagraph.h"
23 #include "insets/inseterror.h"
24 #include "insets/insetbib.h"
27 #include "support/textutils.h"
30 #include "minibuffer.h"
32 #include "bufferparams.h"
33 #include "lyx_gui_misc.h"
36 #include "BufferView.h"
40 #define FIX_DOUBLE_SPACE 1
44 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
52 parameters = &p->params;
56 status = LyXText::UNCHANGED;
57 LyXParagraph * par = p->paragraph;
58 current_font = GetFont(par, 0);
63 InsertParagraph(par, lastrow);
66 // set cursor at the very top position
67 selection = true; /* these setting is necessary
68 because of the delete-empty-
69 paragraph mechanism in
71 SetCursor(firstrow->par, 0);
76 // no rebreak necessary
82 // Default layouttype for copy environment type
89 // Delete all rows, this does not touch the paragraphs!
90 Row * tmprow = firstrow;
92 tmprow = firstrow->next;
99 void LyXText::owner(BufferView * bv)
101 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 // If position is -1, we get the layout font of the paragraph.
111 // If position is -2, we get the font of the manual label of the paragraph.
112 LyXFont LyXText::GetFont(LyXParagraph * par,
113 LyXParagraph::size_type pos) const
115 LyXLayout const & layout =
116 textclasslist.Style(parameters->textclass, par->GetLayout());
118 char par_depth = par->GetDepth();
119 // We specialize the 95% common case:
120 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
123 if (layout.labeltype == LABEL_MANUAL
124 && pos < BeginningOfMainBody(par)) {
126 return par->GetFontSettings(pos).
127 realize(layout.reslabelfont);
129 return par->GetFontSettings(pos).
130 realize(layout.resfont);
133 // process layoutfont for pos == -1 and labelfont for pos < -1
135 return layout.resfont;
137 return layout.reslabelfont;
141 // The uncommon case need not be optimized as much
143 LyXFont layoutfont, tmpfont;
147 if (pos < BeginningOfMainBody(par)) {
149 layoutfont = layout.labelfont;
152 layoutfont = layout.font;
154 tmpfont = par->GetFontSettings(pos);
155 tmpfont.realize(layoutfont);
158 // process layoutfont for pos == -1 and labelfont for pos < -1
160 tmpfont = layout.font;
162 tmpfont = layout.labelfont;
165 // Resolve against environment font information
166 while (par && par_depth && !tmpfont.resolved()) {
167 par = par->DepthHook(par_depth - 1);
169 tmpfont.realize(textclasslist.
170 Style(parameters->textclass,
171 par->GetLayout()).font);
172 par_depth = par->GetDepth();
176 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
178 // Cosmetic improvement: If this is an open footnote, make the font
180 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
181 && par->footnotekind == LyXParagraph::FOOTNOTE) {
189 void LyXText::SetCharFont(LyXParagraph * par,
190 LyXParagraph::size_type pos,
194 // Let the insets convert their font
195 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
196 if (par->GetInset(pos))
197 font = par->GetInset(pos)->ConvertFont(font);
200 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
203 // Get concrete layout font to reduce against
206 if (pos < BeginningOfMainBody(par))
207 layoutfont = layout.labelfont;
209 layoutfont = layout.font;
211 // Realize against environment font information
212 if (par->GetDepth()){
213 LyXParagraph * tp = par;
214 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
215 tp = tp->DepthHook(tp->GetDepth()-1);
217 layoutfont.realize(textclasslist.
218 Style(parameters->textclass,
219 tp->GetLayout()).font);
223 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
225 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
226 && par->footnotekind == LyXParagraph::FOOTNOTE) {
227 layoutfont.decSize();
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 par->SetFont(pos, font);
237 /* inserts a new row behind the specified row, increments
238 * the touched counters */
239 void LyXText::InsertRow(Row * row, LyXParagraph * par,
240 LyXParagraph::size_type pos) const
242 Row * tmprow = new Row;
244 tmprow->previous = 0;
245 tmprow->next = firstrow;
249 tmprow->previous = row;
250 tmprow->next = row->next;
255 tmprow->next->previous = tmprow;
257 if (tmprow->previous)
258 tmprow->previous->next = tmprow;
266 ++number_of_rows; // one more row
270 // removes the row and reset the touched counters
271 void LyXText::RemoveRow(Row * row) const
273 /* this must not happen before the currentrow for clear reasons.
274 so the trick is just to set the current row onto the previous
277 GetRow(row->par, row->pos, unused_y);
278 currentrow = currentrow->previous;
280 currentrow_y -= currentrow->height;
285 row->next->previous = row->previous;
286 if (!row->previous) {
287 firstrow = row->next;
290 row->previous->next = row->next;
293 lastrow = row->previous;
295 height -= row->height; // the text becomes smaller
298 --number_of_rows; // one row less
302 // remove all following rows of the paragraph of the specified row.
303 void LyXText::RemoveParagraph(Row * row) const
305 LyXParagraph * tmppar = row->par;
309 while (row && row->par == tmppar) {
317 // insert the specified paragraph behind the specified row
318 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
320 InsertRow(row, par, 0); /* insert a new row, starting
323 SetCounter(par); // set the counters
325 // and now append the whole paragraph behind the new row
327 firstrow->height = 0;
328 AppendParagraph(firstrow);
331 row->next->height = 0;
332 AppendParagraph(row->next);
337 void LyXText::ToggleFootnote()
339 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
341 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
343 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
345 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
351 void LyXText::OpenStuff()
353 if (cursor.pos == 0 && cursor.par->bibkey){
354 cursor.par->bibkey->Edit(owner_, 0, 0);
356 else if (cursor.pos < cursor.par->Last()
357 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
358 && cursor.par->GetInset(cursor.pos)->Editable()) {
359 owner_->owner()->getMiniBuffer()
360 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
361 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
363 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0);
370 void LyXText::CloseFootnote()
372 LyXParagraph * tmppar;
373 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
375 // if the cursor is not in an open footnote, or
376 // there is no open footnote in this paragraph, just return.
377 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
380 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
381 owner_->owner()->getMiniBuffer()
382 ->Set(_("Nothing to do"));
386 // ok, move the cursor right before the footnote
387 // just a little faster than using CursorRight()
389 cursor.par->ParFromPos(cursor.pos) != par;
393 // now the cursor is at the beginning of the physical par
394 SetCursor(cursor.par,
396 cursor.par->ParFromPos(cursor.pos)->text.size());
398 /* we are in a footnote, so let us move at the beginning */
399 /* this is just faster than using just CursorLeft() */
402 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
403 // just a little bit faster than movin the cursor
404 tmppar = tmppar->Previous();
406 SetCursor(tmppar, tmppar->Last());
409 // the cursor must be exactly before the footnote
410 par = cursor.par->ParFromPos(cursor.pos);
412 status = LyXText::NEED_MORE_REFRESH;
413 refresh_row = cursor.row;
414 refresh_y = cursor.y - cursor.row->baseline;
417 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
418 Row * row = cursor.row;
420 tmppar->CloseFootnote(cursor.pos);
422 while (tmppar != endpar) {
423 RemoveRow(row->next);
425 tmppar = row->next->par;
430 AppendParagraph(cursor.row);
432 SetCursor(cursor.par, cursor.pos);
436 if (cursor.row->next)
437 SetHeightOfRow(cursor.row->next);
441 /* used in setlayout */
442 // Asger is not sure we want to do this...
443 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
445 LyXFont layoutfont, tmpfont;
447 LyXLayout const & layout =
448 textclasslist.Style(parameters->textclass, par->GetLayout());
450 for (LyXParagraph::size_type pos = 0;
451 pos < par->Last(); ++pos) {
452 if (pos < BeginningOfMainBody(par))
453 layoutfont = layout.labelfont;
455 layoutfont = layout.font;
457 tmpfont = par->GetFontSettings(pos);
458 tmpfont.reduce(layoutfont);
459 par->SetFont(pos, tmpfont);
464 // set layout over selection and make a total rebreak of those paragraphs
465 void LyXText::SetLayout(char layout)
469 // if there is no selection just set the layout
470 // of the current paragraph */
472 sel_start_cursor = cursor; // dummy selection
473 sel_end_cursor = cursor;
476 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
477 LyXParagraph * undoendpar = endpar;
479 if (endpar && endpar->GetDepth()) {
480 while (endpar && endpar->GetDepth()) {
481 endpar = endpar->LastPhysicalPar()->Next();
486 endpar = endpar->Next(); // because of parindents etc.
490 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
493 tmpcursor = cursor; /* store the current cursor */
495 /* ok we have a selection. This is always between sel_start_cursor
496 * and sel_end cursor */
497 cursor = sel_start_cursor;
499 LyXLayout const & lyxlayout =
500 textclasslist.Style(parameters->textclass, layout);
502 while (cursor.par != sel_end_cursor.par) {
503 if (cursor.par->footnoteflag ==
504 sel_start_cursor.par->footnoteflag) {
505 cursor.par->SetLayout(layout);
506 MakeFontEntriesLayoutSpecific(cursor.par);
507 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
508 fppar->added_space_top = lyxlayout.fill_top ?
509 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
510 fppar->added_space_bottom = lyxlayout.fill_bottom ?
511 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
512 if (lyxlayout.margintype == MARGIN_MANUAL)
513 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
514 if (lyxlayout.labeltype != LABEL_BIBLIO
516 delete fppar->bibkey;
520 cursor.par = cursor.par->Next();
522 if (cursor.par->footnoteflag ==
523 sel_start_cursor.par->footnoteflag) {
524 cursor.par->SetLayout(layout);
525 MakeFontEntriesLayoutSpecific(cursor.par);
526 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
527 fppar->added_space_top = lyxlayout.fill_top ?
528 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
529 fppar->added_space_bottom = lyxlayout.fill_bottom ?
530 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
531 if (lyxlayout.margintype == MARGIN_MANUAL)
532 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
533 if (lyxlayout.labeltype != LABEL_BIBLIO
535 delete fppar->bibkey;
540 RedoParagraphs(sel_start_cursor, endpar);
542 // we have to reset the selection, because the
543 // geometry could have changed */
544 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
546 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
547 UpdateCounters(cursor.row);
550 SetCursor(tmpcursor.par, tmpcursor.pos);
554 // increment depth over selection and
555 // make a total rebreak of those paragraphs
556 void LyXText::IncDepth()
558 // If there is no selection, just use the current paragraph
560 sel_start_cursor = cursor; // dummy selection
561 sel_end_cursor = cursor;
564 // We end at the next paragraph with depth 0
565 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
566 LyXParagraph * undoendpar = endpar;
568 if (endpar && endpar->GetDepth()) {
569 while (endpar && endpar->GetDepth()) {
570 endpar = endpar->LastPhysicalPar()->Next();
575 endpar = endpar->Next(); // because of parindents etc.
580 .par->ParFromPos(sel_start_cursor.pos)->previous,
583 LyXCursor tmpcursor = cursor; // store the current cursor
585 // ok we have a selection. This is always between sel_start_cursor
586 // and sel_end cursor
587 cursor = sel_start_cursor;
589 bool anything_changed = false;
592 // NOTE: you can't change the depth of a bibliography entry
593 if (cursor.par->footnoteflag ==
594 sel_start_cursor.par->footnoteflag
595 && textclasslist.Style(parameters->textclass,
596 cursor.par->GetLayout()
597 ).labeltype != LABEL_BIBLIO) {
598 LyXParagraph * prev =
599 cursor.par->FirstPhysicalPar()->Previous();
601 && (prev->GetDepth() - cursor.par->GetDepth() > 0
602 || (prev->GetDepth() == cursor.par->GetDepth()
603 && textclasslist.Style(parameters->textclass,
604 prev->GetLayout()).isEnvironment()))) {
605 cursor.par->FirstPhysicalPar()->depth++;
606 anything_changed = true;
609 if (cursor.par == sel_end_cursor.par)
611 cursor.par = cursor.par->Next();
614 // if nothing changed set all depth to 0
615 if (!anything_changed) {
616 cursor = sel_start_cursor;
617 while (cursor.par != sel_end_cursor.par) {
618 cursor.par->FirstPhysicalPar()->depth = 0;
619 cursor.par = cursor.par->Next();
621 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
622 cursor.par->FirstPhysicalPar()->depth = 0;
625 RedoParagraphs(sel_start_cursor, endpar);
627 // we have to reset the selection, because the
628 // geometry could have changed
629 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
631 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
632 UpdateCounters(cursor.row);
635 SetCursor(tmpcursor.par, tmpcursor.pos);
639 // decrement depth over selection and
640 // make a total rebreak of those paragraphs
641 void LyXText::DecDepth()
643 // if there is no selection just set the layout
644 // of the current paragraph
646 sel_start_cursor = cursor; // dummy selection
647 sel_end_cursor = cursor;
650 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
651 LyXParagraph * undoendpar = endpar;
653 if (endpar && endpar->GetDepth()) {
654 while (endpar && endpar->GetDepth()) {
655 endpar = endpar->LastPhysicalPar()->Next();
660 endpar = endpar->Next(); // because of parindents etc.
665 .par->ParFromPos(sel_start_cursor.pos)->previous,
668 LyXCursor tmpcursor = cursor; // store the current cursor
670 // ok we have a selection. This is always between sel_start_cursor
671 // and sel_end cursor
672 cursor = sel_start_cursor;
675 if (cursor.par->footnoteflag ==
676 sel_start_cursor.par->footnoteflag) {
677 if (cursor.par->FirstPhysicalPar()->depth)
678 cursor.par->FirstPhysicalPar()->depth--;
680 if (cursor.par == sel_end_cursor.par)
682 cursor.par = cursor.par->Next();
685 RedoParagraphs(sel_start_cursor, endpar);
687 // we have to reset the selection, because the
688 // geometry could have changed
689 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
691 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
692 UpdateCounters(cursor.row);
695 SetCursor(tmpcursor.par, tmpcursor.pos);
699 // set font over selection and make a total rebreak of those paragraphs
700 void LyXText::SetFont(LyXFont const & font, bool toggleall)
702 // if there is no selection just set the current_font
704 // Determine basis font
706 if (cursor.pos < BeginningOfMainBody(cursor.par))
707 layoutfont = GetFont(cursor.par, -2);
709 layoutfont = GetFont(cursor.par, -1);
710 // Update current font
711 real_current_font.update(font, toggleall);
713 // Reduce to implicit settings
714 current_font = real_current_font;
715 current_font.reduce(layoutfont);
716 // And resolve it completely
717 real_current_font.realize(layoutfont);
721 LyXCursor tmpcursor = cursor; // store the current cursor
723 // ok we have a selection. This is always between sel_start_cursor
724 // and sel_end cursor
727 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
728 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
729 cursor = sel_start_cursor;
730 while (cursor.par != sel_end_cursor.par ||
731 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
732 && cursor.pos < sel_end_cursor.pos))
734 if (cursor.pos < cursor.par->Last()
735 && cursor.par->footnoteflag
736 == sel_start_cursor.par->footnoteflag) {
737 // an open footnote should behave
739 LyXFont newfont = GetFont(cursor.par, cursor.pos);
740 newfont.update(font, toggleall);
741 SetCharFont(cursor.par, cursor.pos, newfont);
745 cursor.par = cursor.par->Next();
749 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
751 // we have to reset the selection, because the
752 // geometry could have changed
753 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
755 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
758 SetCursor(tmpcursor.par, tmpcursor.pos);
762 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
764 Row * tmprow = cur.row;
765 long y = cur.y - tmprow->baseline;
767 SetHeightOfRow(tmprow);
768 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
769 // find the first row of the paragraph
770 if (first_phys_par != tmprow->par)
771 while (tmprow->previous
772 && tmprow->previous->par != first_phys_par) {
773 tmprow = tmprow->previous;
775 SetHeightOfRow(tmprow);
777 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
778 tmprow = tmprow->previous;
780 SetHeightOfRow(tmprow);
783 // we can set the refreshing parameters now
784 status = LyXText::NEED_MORE_REFRESH;
786 refresh_row = tmprow;
787 SetCursor(cur.par, cur.pos);
791 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
793 Row * tmprow = cur.row;
795 long y = cur.y - tmprow->baseline;
796 SetHeightOfRow(tmprow);
797 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
798 // find the first row of the paragraph
799 if (first_phys_par != tmprow->par)
800 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
801 tmprow = tmprow->previous;
804 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
805 tmprow = tmprow->previous;
809 // we can set the refreshing parameters now
810 if (status == LyXText::UNCHANGED || y < refresh_y) {
812 refresh_row = tmprow;
814 status = LyXText::NEED_MORE_REFRESH;
815 SetCursor(cur.par, cur.pos);
819 /* deletes and inserts again all paragaphs between the cursor
820 * and the specified par
821 * This function is needed after SetLayout and SetFont etc. */
822 void LyXText::RedoParagraphs(LyXCursor const & cur,
823 LyXParagraph const * endpar) const
826 LyXParagraph * tmppar, * first_phys_par;
828 Row * tmprow = cur.row;
830 long y = cur.y - tmprow->baseline;
832 if (!tmprow->previous){
833 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
835 first_phys_par = tmprow->par->FirstPhysicalPar();
836 // find the first row of the paragraph
837 if (first_phys_par != tmprow->par)
838 while (tmprow->previous
839 && tmprow->previous->par != first_phys_par) {
840 tmprow = tmprow->previous;
843 while (tmprow->previous
844 && tmprow->previous->par == first_phys_par) {
845 tmprow = tmprow->previous;
850 // we can set the refreshing parameters now
851 status = LyXText::NEED_MORE_REFRESH;
853 refresh_row = tmprow->previous; /* the real refresh row will
854 be deleted, so I store
858 tmppar = tmprow->next->par;
861 while (tmppar != endpar) {
862 RemoveRow(tmprow->next);
864 tmppar = tmprow->next->par;
869 // remove the first one
870 tmprow2 = tmprow; /* this is because tmprow->previous
872 tmprow = tmprow->previous;
875 tmppar = first_phys_par;
879 InsertParagraph(tmppar, tmprow);
882 while (tmprow->next && tmprow->next->par == tmppar)
883 tmprow = tmprow->next;
884 tmppar = tmppar->Next();
886 } while (tmppar != endpar);
888 // this is because of layout changes
890 refresh_y -= refresh_row->height;
891 SetHeightOfRow(refresh_row);
893 refresh_row = firstrow;
895 SetHeightOfRow(refresh_row);
898 if (tmprow && tmprow->next)
899 SetHeightOfRow(tmprow->next);
903 int LyXText::FullRebreak()
905 if (need_break_row) {
906 BreakAgain(need_break_row);
914 /* important for the screen */
917 /* the cursor set functions have a special mechanism. When they
918 * realize, that you left an empty paragraph, they will delete it.
919 * They also delet the corresponding row */
921 // need the selection cursor:
922 void LyXText::SetSelection()
925 last_sel_cursor = sel_cursor;
926 sel_start_cursor = sel_cursor;
927 sel_end_cursor = sel_cursor;
932 // first the toggling area
933 if (cursor.y < last_sel_cursor.y ||
934 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
935 toggle_end_cursor = last_sel_cursor;
936 toggle_cursor = cursor;
939 toggle_end_cursor = cursor;
940 toggle_cursor = last_sel_cursor;
943 last_sel_cursor = cursor;
945 // and now the whole selection
947 if (sel_cursor.par == cursor.par)
948 if (sel_cursor.pos < cursor.pos) {
949 sel_end_cursor = cursor;
950 sel_start_cursor = sel_cursor;
952 sel_end_cursor = sel_cursor;
953 sel_start_cursor = cursor;
955 else if (sel_cursor.y < cursor.y ||
956 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
957 sel_end_cursor = cursor;
958 sel_start_cursor = sel_cursor;
961 sel_end_cursor = sel_cursor;
962 sel_start_cursor = cursor;
965 // a selection with no contents is not a selection
966 if (sel_start_cursor.x == sel_end_cursor.x &&
967 sel_start_cursor.y == sel_end_cursor.y)
972 void LyXText::ClearSelection() const
979 void LyXText::CursorHome() const
981 SetCursor(cursor.par, cursor.row->pos);
985 void LyXText::CursorEnd() const
987 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
988 SetCursor(cursor.par, RowLast(cursor.row) + 1);
990 if (cursor.par->Last() &&
991 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
992 || cursor.par->IsNewline(RowLast(cursor.row))))
993 SetCursor(cursor.par, RowLast(cursor.row));
995 SetCursor(cursor.par, RowLast(cursor.row) + 1);
997 if (cursor.par->table) {
998 int cell = NumberOfCell(cursor.par, cursor.pos);
999 if (cursor.par->table->RowHasContRow(cell) &&
1000 cursor.par->table->CellHasContRow(cell)<0) {
1001 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1002 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1004 if (cursor.par->Last() &&
1005 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1006 || cursor.par->IsNewline(RowLast(cursor.row))))
1007 SetCursor(cursor.par, RowLast(cursor.row));
1009 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1016 void LyXText::CursorTop() const
1018 while (cursor.par->Previous())
1019 cursor.par = cursor.par->Previous();
1020 SetCursor(cursor.par, 0);
1024 void LyXText::CursorBottom() const
1026 while (cursor.par->Next())
1027 cursor.par = cursor.par->Next();
1028 SetCursor(cursor.par, cursor.par->Last());
1032 /* returns a pointer to the row near the specified y-coordinate
1033 * (relative to the whole text). y is set to the real beginning
1035 Row * LyXText::GetRowNearY(long & y) const
1041 tmprow = currentrow;
1042 tmpy = currentrow_y;
1049 while (tmprow->next && tmpy + tmprow->height <= y) {
1050 tmpy += tmprow->height;
1051 tmprow = tmprow->next;
1054 while (tmprow->previous && tmpy > y) {
1055 tmprow = tmprow->previous;
1056 tmpy -= tmprow->height;
1059 currentrow = tmprow;
1060 currentrow_y = tmpy;
1062 y = tmpy; // return the real y
1067 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1069 // If the mask is completely neutral, tell user
1070 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1071 // Could only happen with user style
1072 owner_->owner()->getMiniBuffer()
1073 ->Set(_("No font change defined. Use Character under"
1074 " the Layout menu to define font change."));
1078 // Try implicit word selection
1079 LyXCursor resetCursor = cursor;
1080 int implicitSelection = SelectWordWhenUnderCursor();
1083 SetFont(font, toggleall);
1085 /* Implicit selections are cleared afterwards and cursor is set to the
1086 original position. */
1087 if (implicitSelection) {
1089 cursor = resetCursor;
1090 SetCursor( cursor.par, cursor.pos );
1091 sel_cursor = cursor;
1096 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1098 if (textclasslist.Style(parameters->textclass,
1099 par->GetLayout()).labeltype != LABEL_MANUAL)
1102 return par->BeginningOfMainBody();
1106 /* if there is a selection, reset every environment you can find
1107 * in the selection, otherwise just the environment you are in */
1108 void LyXText::MeltFootnoteEnvironment()
1110 LyXParagraph * tmppar, * firsttmppar;
1114 /* is is only allowed, if the cursor is IN an open footnote.
1115 * Otherwise it is too dangerous */
1116 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1119 SetUndo(Undo::FINISH,
1120 cursor.par->PreviousBeforeFootnote()->previous,
1121 cursor.par->NextAfterFootnote()->next);
1123 /* ok, move to the beginning of the footnote. */
1124 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1125 cursor.par = cursor.par->Previous();
1127 SetCursor(cursor.par, cursor.par->Last());
1128 /* this is just faster than using CursorLeft(); */
1130 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1131 tmppar = firsttmppar;
1132 /* tmppar is now the paragraph right before the footnote */
1134 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1137 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1138 tmppar = tmppar->next; /* I use next instead of Next(),
1139 * because there cannot be any
1140 * footnotes in a footnote
1142 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1144 /* remember the captions and empty paragraphs */
1145 if ((textclasslist.Style(parameters->textclass,
1146 tmppar->GetLayout())
1147 .labeltype == LABEL_SENSITIVE)
1149 tmppar->SetLayout(0);
1152 // now we will paste the ex-footnote, if the layouts allow it
1153 // first restore the layout of the paragraph right behind
1156 tmppar->next->MakeSameLayout(cursor.par);
1159 if ((!tmppar->GetLayout() && !tmppar->table)
1161 && (!tmppar->Next()->Last()
1162 || tmppar->Next()->HasSameLayout(tmppar)))) {
1163 if (tmppar->Next()->Last()
1164 && tmppar->Next()->IsLineSeparator(0))
1165 tmppar->Next()->Erase(0);
1166 tmppar->PasteParagraph();
1169 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1170 * by the pasting of the beginning */
1172 /* then the beginning */
1173 /* if there is no space between the text and the footnote, so we insert
1175 * (only if the previous par and the footnotepar are not empty!) */
1176 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1177 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1178 if (firsttmppar->text.size()
1179 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1180 && first_footnote_par_is_not_empty) {
1181 firsttmppar->next->InsertChar(0, ' ');
1183 firsttmppar->PasteParagraph();
1186 /* now redo the paragaphs */
1187 RedoParagraphs(cursor, tmppar);
1189 SetCursor(cursor.par, cursor.pos);
1191 /* sometimes it can happen, that there is a counter change */
1192 Row * row = cursor.row;
1193 while (row->next && row->par != tmppar && row->next->par != tmppar)
1195 UpdateCounters(row);
1202 /* the DTP switches for paragraphs. LyX will store them in the
1203 * first physicla paragraph. When a paragraph is broken, the top settings
1204 * rest, the bottom settings are given to the new one. So I can make shure,
1205 * they do not duplicate themself and you cannnot make dirty things with
1208 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1209 bool pagebreak_top, bool pagebreak_bottom,
1210 VSpace const & space_top,
1211 VSpace const & space_bottom,
1213 string labelwidthstring,
1216 LyXCursor tmpcursor = cursor;
1218 sel_start_cursor = cursor;
1219 sel_end_cursor = cursor;
1222 // make sure that the depth behind the selection are restored, too
1223 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1224 LyXParagraph * undoendpar = endpar;
1226 if (endpar && endpar->GetDepth()) {
1227 while (endpar && endpar->GetDepth()) {
1228 endpar = endpar->LastPhysicalPar()->Next();
1229 undoendpar = endpar;
1233 endpar = endpar->Next(); // because of parindents etc.
1238 .par->ParFromPos(sel_start_cursor.pos)->previous,
1242 LyXParagraph * tmppar = sel_end_cursor.par;
1243 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1244 SetCursor(tmppar->FirstPhysicalPar(), 0);
1245 status = LyXText::NEED_MORE_REFRESH;
1246 refresh_row = cursor.row;
1247 refresh_y = cursor.y - cursor.row->baseline;
1248 if (cursor.par->footnoteflag ==
1249 sel_start_cursor.par->footnoteflag) {
1250 cursor.par->line_top = line_top;
1251 cursor.par->line_bottom = line_bottom;
1252 cursor.par->pagebreak_top = pagebreak_top;
1253 cursor.par->pagebreak_bottom = pagebreak_bottom;
1254 cursor.par->added_space_top = space_top;
1255 cursor.par->added_space_bottom = space_bottom;
1256 // does the layout allow the new alignment?
1257 if (align == LYX_ALIGN_LAYOUT)
1258 align = textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).align;
1261 if (align & textclasslist
1262 .Style(parameters->textclass,
1263 cursor.par->GetLayout()).alignpossible) {
1264 if (align == textclasslist
1265 .Style(parameters->textclass,
1266 cursor.par->GetLayout()).align)
1267 cursor.par->align = LYX_ALIGN_LAYOUT;
1269 cursor.par->align = align;
1271 cursor.par->SetLabelWidthString(labelwidthstring);
1272 cursor.par->noindent = noindent;
1275 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1278 RedoParagraphs(sel_start_cursor, endpar);
1281 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1282 sel_cursor = cursor;
1283 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1285 SetCursor(tmpcursor.par, tmpcursor.pos);
1289 void LyXText::SetParagraphExtraOpt(int type,
1291 char const * widthp,
1292 int alignment, bool hfill,
1293 bool start_minipage)
1295 LyXCursor tmpcursor = cursor;
1296 LyXParagraph * tmppar;
1298 sel_start_cursor = cursor;
1299 sel_end_cursor = cursor;
1302 // make sure that the depth behind the selection are restored, too
1303 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1304 LyXParagraph * undoendpar = endpar;
1306 if (endpar && endpar->GetDepth()) {
1307 while (endpar && endpar->GetDepth()) {
1308 endpar = endpar->LastPhysicalPar()->Next();
1309 undoendpar = endpar;
1313 endpar = endpar->Next(); // because of parindents etc.
1318 .par->ParFromPos(sel_start_cursor.pos)->previous,
1321 tmppar = sel_end_cursor.par;
1322 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1323 SetCursor(tmppar->FirstPhysicalPar(), 0);
1324 status = LyXText::NEED_MORE_REFRESH;
1325 refresh_row = cursor.row;
1326 refresh_y = cursor.y - cursor.row->baseline;
1327 if (cursor.par->footnoteflag ==
1328 sel_start_cursor.par->footnoteflag) {
1329 if (type == LyXParagraph::PEXTRA_NONE) {
1330 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1331 cursor.par->UnsetPExtraType();
1332 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1335 cursor.par->SetPExtraType(type, width, widthp);
1336 cursor.par->pextra_hfill = hfill;
1337 cursor.par->pextra_start_minipage = start_minipage;
1338 cursor.par->pextra_alignment = alignment;
1341 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1343 RedoParagraphs(sel_start_cursor, endpar);
1345 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1346 sel_cursor = cursor;
1347 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1349 SetCursor(tmpcursor.par, tmpcursor.pos);
1353 static char const * alphaCounter(int n) {
1354 static char result[2];
1359 result[0] = 'A' + n;
1367 // set the counter of a paragraph. This includes the labels
1368 void LyXText::SetCounter(LyXParagraph * par) const
1370 // this is only relevant for the beginning of paragraph
1371 par = par->FirstPhysicalPar();
1373 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1376 LyXTextClass const & textclass =
1377 textclasslist.TextClass(parameters->textclass);
1379 /* copy the prev-counters to this one, unless this is the start of a
1380 footnote or of a bibliography or the very first paragraph */
1382 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1383 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1384 && par->footnotekind == LyXParagraph::FOOTNOTE)
1385 && !(textclasslist.Style(parameters->textclass,
1386 par->Previous()->GetLayout()
1387 ).labeltype != LABEL_BIBLIO
1388 && layout.labeltype == LABEL_BIBLIO)) {
1389 for (int i = 0; i < 10; ++i) {
1390 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1392 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1393 if (!par->appendix && par->start_of_appendix){
1394 par->appendix = true;
1395 for (int i = 0; i < 10; ++i) {
1396 par->setCounter(i, 0);
1399 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1400 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1403 for (int i = 0; i < 10; ++i) {
1404 par->setCounter(i, 0);
1406 par->appendix = par->start_of_appendix;
1411 // if this is an open marginnote and this is the first
1412 // entry in the marginnote and the enclosing
1413 // environment is an enum/item then correct for the
1414 // LaTeX behaviour (ARRae)
1415 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1416 && par->footnotekind == LyXParagraph::MARGIN
1418 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1419 && (par->PreviousBeforeFootnote()
1420 && textclasslist.Style(parameters->textclass,
1421 par->PreviousBeforeFootnote()->GetLayout()
1422 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1423 // Any itemize or enumerate environment in a marginnote
1424 // that is embedded in an itemize or enumerate
1425 // paragraph is seen by LaTeX as being at a deeper
1426 // level within that enclosing itemization/enumeration
1427 // even if there is a "standard" layout at the start of
1433 /* Maybe we have to increment the enumeration depth.
1434 * BUT, enumeration in a footnote is considered in isolation from its
1435 * surrounding paragraph so don't increment if this is the
1436 * first line of the footnote
1437 * AND, bibliographies can't have their depth changed ie. they
1438 * are always of depth 0
1441 && par->Previous()->GetDepth() < par->GetDepth()
1442 && textclasslist.Style(parameters->textclass,
1443 par->Previous()->GetLayout()
1444 ).labeltype == LABEL_COUNTER_ENUMI
1445 && par->enumdepth < 3
1446 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1447 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1448 && par->footnotekind == LyXParagraph::FOOTNOTE)
1449 && layout.labeltype != LABEL_BIBLIO) {
1453 /* Maybe we have to decrement the enumeration depth, see note above */
1455 && par->Previous()->GetDepth() > par->GetDepth()
1456 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1457 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1458 && par->footnotekind == LyXParagraph::FOOTNOTE)
1459 && layout.labeltype != LABEL_BIBLIO) {
1460 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1461 par->setCounter(6 + par->enumdepth,
1462 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1463 /* reset the counters.
1464 * A depth change is like a breaking layout
1466 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1467 par->setCounter(i, 0);
1470 if (!par->labelstring.empty()) {
1471 par->labelstring.clear();
1474 if (layout.margintype == MARGIN_MANUAL) {
1475 if (par->labelwidthstring.empty()) {
1476 par->SetLabelWidthString(layout.labelstring());
1480 par->SetLabelWidthString(string());
1483 /* is it a layout that has an automatic label ? */
1484 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1486 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1487 if (i >= 0 && i<= parameters->secnumdepth) {
1488 par->incCounter(i); // increment the counter
1490 char * s = new char[50];
1492 // Is there a label? Useful for Chapter layout
1493 if (!par->appendix){
1494 if (!layout.labelstring().empty())
1495 par->labelstring = layout.labelstring();
1497 par->labelstring.clear();
1499 if (!layout.labelstring_appendix().empty())
1500 par->labelstring = layout.labelstring_appendix();
1502 par->labelstring.clear();
1505 if (!par->appendix){
1506 switch (2 * LABEL_FIRST_COUNTER -
1507 textclass.maxcounter() + i) {
1508 case LABEL_COUNTER_CHAPTER:
1510 par->getCounter(i));
1512 case LABEL_COUNTER_SECTION:
1514 par->getCounter(i - 1),
1515 par->getCounter(i));
1517 case LABEL_COUNTER_SUBSECTION:
1518 sprintf(s, "%d.%d.%d",
1519 par->getCounter(i-2),
1520 par->getCounter(i-1),
1521 par->getCounter(i));
1523 case LABEL_COUNTER_SUBSUBSECTION:
1524 sprintf(s, "%d.%d.%d.%d",
1525 par->getCounter(i-3),
1526 par->getCounter(i-2),
1527 par->getCounter(i-1),
1528 par->getCounter(i));
1530 case LABEL_COUNTER_PARAGRAPH:
1531 sprintf(s, "%d.%d.%d.%d.%d",
1532 par->getCounter(i-4),
1533 par->getCounter(i-3),
1534 par->getCounter(i-2),
1535 par->getCounter(i-1),
1536 par->getCounter(i));
1538 case LABEL_COUNTER_SUBPARAGRAPH:
1539 sprintf(s, "%d.%d.%d.%d.%d.%d",
1540 par->getCounter(i-5),
1541 par->getCounter(i-4),
1542 par->getCounter(i-3),
1543 par->getCounter(i-2),
1544 par->getCounter(i-1),
1545 par->getCounter(i));
1548 sprintf(s, "%d.", par->getCounter(i));
1552 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1553 case LABEL_COUNTER_CHAPTER:
1555 alphaCounter(par->getCounter(i)));
1557 case LABEL_COUNTER_SECTION:
1559 alphaCounter(par->getCounter(i - 1)),
1560 par->getCounter(i));
1562 case LABEL_COUNTER_SUBSECTION:
1563 sprintf(s, "%s.%d.%d",
1564 alphaCounter(par->getCounter(i-2)),
1565 par->getCounter(i-1),
1566 par->getCounter(i));
1568 case LABEL_COUNTER_SUBSUBSECTION:
1569 sprintf(s, "%s.%d.%d.%d",
1570 alphaCounter(par->getCounter(i-3)),
1571 par->getCounter(i-2),
1572 par->getCounter(i-1),
1573 par->getCounter(i));
1575 case LABEL_COUNTER_PARAGRAPH:
1576 sprintf(s, "%s.%d.%d.%d.%d",
1577 alphaCounter(par->getCounter(i-4)),
1578 par->getCounter(i-3),
1579 par->getCounter(i-2),
1580 par->getCounter(i-1),
1581 par->getCounter(i));
1583 case LABEL_COUNTER_SUBPARAGRAPH:
1584 sprintf(s, "%s.%d.%d.%d.%d.%d",
1585 alphaCounter(par->getCounter(i-5)),
1586 par->getCounter(i-4),
1587 par->getCounter(i-3),
1588 par->getCounter(i-2),
1589 par->getCounter(i-1),
1590 par->getCounter(i));
1593 sprintf(s, "%c.", par->getCounter(i));
1598 par->labelstring += s;
1601 for (i++; i < 10; ++i) {
1602 // reset the following counters
1603 par->setCounter(i, 0);
1605 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1606 for (i++; i < 10; ++i) {
1607 // reset the following counters
1608 par->setCounter(i, 0);
1610 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1611 par->incCounter(i + par->enumdepth);
1612 char * s = new char[25];
1613 int number = par->getCounter(i + par->enumdepth);
1615 static const char *roman[20] = {
1616 "i", "ii", "iii", "iv", "v",
1617 "vi", "vii", "viii", "ix", "x",
1618 "xi", "xii", "xiii", "xiv", "xv",
1619 "xvi", "xvii", "xviii", "xix", "xx"
1621 static const char hebrew[22] = {
1622 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1623 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1624 '÷', 'ø', 'ù', 'ú'
1627 switch (par->enumdepth) {
1629 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1630 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1632 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1635 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1636 sprintf(s, "%s.", roman[(number-1) % 20]);
1638 sprintf(s, ".%s", roman[(number-1) % 20]);
1641 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1642 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1644 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1647 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1648 sprintf(s, "%d.", number);
1650 sprintf(s, ".%d", number);
1653 par->labelstring = s;
1656 for (i += par->enumdepth + 1; i < 10; ++i)
1657 par->setCounter(i, 0); /* reset the following counters */
1660 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1661 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1663 int number = par->getCounter(i);
1665 par->bibkey = new InsetBibKey();
1666 par->bibkey->setCounter(number);
1667 par->labelstring = layout.labelstring();
1669 // In biblio should't be following counters but...
1671 string s = layout.labelstring();
1673 // the caption hack:
1675 if (layout.labeltype == LABEL_SENSITIVE) {
1676 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1677 && (par->footnotekind == LyXParagraph::FIG
1678 || par->footnotekind == LyXParagraph::WIDE_FIG))
1679 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1683 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1684 && (par->footnotekind == LyXParagraph::TAB
1685 || par->footnotekind == LyXParagraph::WIDE_TAB))
1686 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1690 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1691 && par->footnotekind == LyXParagraph::ALGORITHM)
1692 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1695 s = ":Ãúéøåâìà ";
1697 /* par->SetLayout(0);
1698 s = layout->labelstring; */
1699 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1702 s = " :úåòîùî øñç";
1706 par->labelstring = s;
1708 /* reset the enumeration counter. They are always resetted
1709 * when there is any other layout between */
1710 for (int i = 6 + par->enumdepth; i < 10; ++i)
1711 par->setCounter(i, 0);
1716 /* Updates all counters BEHIND the row. Changed paragraphs
1717 * with a dynamic left margin will be rebroken. */
1718 void LyXText::UpdateCounters(Row * row) const
1727 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1728 par = row->par->LastPhysicalPar()->Next();
1730 par = row->par->next;
1735 while (row->par != par)
1740 /* now check for the headline layouts. remember that they
1741 * have a dynamic left margin */
1743 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1744 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1747 /* Rebreak the paragraph */
1748 RemoveParagraph(row);
1749 AppendParagraph(row);
1751 /* think about the damned open footnotes! */
1752 while (par->Next() &&
1753 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1754 || par->Next()->IsDummy())){
1756 if (par->IsDummy()) {
1757 while (row->par != par)
1759 RemoveParagraph(row);
1760 AppendParagraph(row);
1765 par = par->LastPhysicalPar()->Next();
1771 /* insets an inset. */
1772 void LyXText::InsertInset(Inset *inset)
1774 SetUndo(Undo::INSERT,
1775 cursor.par->ParFromPos(cursor.pos)->previous,
1776 cursor.par->ParFromPos(cursor.pos)->next);
1777 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1778 cursor.par->InsertInset(cursor.pos, inset);
1779 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1780 * The character will not be inserted a
1785 // this is for the simple cut and paste mechanism
1786 static LyXParagraph * simple_cut_buffer = 0;
1787 static char simple_cut_buffer_textclass = 0;
1789 void DeleteSimpleCutBuffer()
1791 if (!simple_cut_buffer)
1793 LyXParagraph * tmppar;
1795 while (simple_cut_buffer) {
1796 tmppar = simple_cut_buffer;
1797 simple_cut_buffer = simple_cut_buffer->next;
1800 simple_cut_buffer = 0;
1804 void LyXText::copyEnvironmentType()
1806 copylayouttype = cursor.par->GetLayout();
1810 void LyXText::pasteEnvironmentType()
1812 SetLayout(copylayouttype);
1816 void LyXText::CutSelection(bool doclear)
1818 // This doesn't make sense, if there is no selection
1822 // OK, we have a selection. This is always between sel_start_cursor
1823 // and sel_end cursor
1824 LyXParagraph * tmppar;
1826 // Check whether there are half footnotes in the selection
1827 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1828 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1829 tmppar = sel_start_cursor.par;
1830 while (tmppar != sel_end_cursor.par){
1831 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1832 WriteAlert(_("Impossible operation"),
1833 _("Don't know what to do with half floats."),
1837 tmppar = tmppar->Next();
1841 /* table stuff -- begin */
1842 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1843 if ( sel_start_cursor.par != sel_end_cursor.par) {
1844 WriteAlert(_("Impossible operation"),
1845 _("Don't know what to do with half tables."),
1849 sel_start_cursor.par->table->Reinit();
1851 /* table stuff -- end */
1853 // make sure that the depth behind the selection are restored, too
1854 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1855 LyXParagraph * undoendpar = endpar;
1857 if (endpar && endpar->GetDepth()) {
1858 while (endpar && endpar->GetDepth()) {
1859 endpar = endpar->LastPhysicalPar()->Next();
1860 undoendpar = endpar;
1862 } else if (endpar) {
1863 endpar = endpar->Next(); // because of parindents etc.
1866 SetUndo(Undo::DELETE,
1868 .par->ParFromPos(sel_start_cursor.pos)->previous,
1871 // clear the simple_cut_buffer
1872 DeleteSimpleCutBuffer();
1874 // set the textclass
1875 simple_cut_buffer_textclass = parameters->textclass;
1877 #ifdef WITH_WARNINGS
1878 #warning Asger: Make cut more intelligent here.
1881 White paper for "intelligent" cutting:
1883 Example: "This is our text."
1884 Using " our " as selection, cutting will give "This istext.".
1885 Using "our" as selection, cutting will give "This is text.".
1886 Using " our" as selection, cutting will give "This is text.".
1887 Using "our " as selection, cutting will give "This is text.".
1889 All those four selections will (however) paste identically:
1890 Pasting with the cursor right after the "is" will give the
1891 original text with all four selections.
1893 The rationale is to be intelligent such that words are copied,
1894 cut and pasted in a functional manner.
1896 This is not implemented yet. (Asger)
1898 The changes below sees to do a lot of what you want. However
1899 I have not verified that all cases work as they should:
1901 - cut in multiple row
1903 - cut across footnotes and paragraph
1904 My simplistic tests show that the idea are basically sound but
1905 there are some items to fix up...we only need to find them
1908 As do redo Asger's example above (with | beeing the cursor in the
1909 result after cutting.):
1911 Example: "This is our text."
1912 Using " our " as selection, cutting will give "This is|text.".
1913 Using "our" as selection, cutting will give "This is | text.".
1914 Using " our" as selection, cutting will give "This is| text.".
1915 Using "our " as selection, cutting will give "This is |text.".
1920 #ifndef FIX_DOUBLE_SPACE
1921 bool space_wrapped =
1922 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1923 if (sel_end_cursor.pos > 0
1924 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1925 // please break before a space at the end
1926 sel_end_cursor.pos--;
1927 space_wrapped = true;
1929 // cut behind a space if there is one
1930 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1931 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1932 && (sel_start_cursor.par != sel_end_cursor.par
1933 || sel_start_cursor.pos < sel_end_cursor.pos))
1934 sel_start_cursor.pos++;
1936 // there are two cases: cut only within one paragraph or
1937 // more than one paragraph
1939 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1940 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1941 // only within one paragraph
1942 simple_cut_buffer = new LyXParagraph;
1943 LyXParagraph::size_type i =
1944 sel_start_cursor.pos;
1945 for (; i < sel_end_cursor.pos; ++i) {
1946 /* table stuff -- begin */
1947 if (sel_start_cursor.par->table
1948 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1949 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1950 sel_start_cursor.pos++;
1952 /* table stuff -- end */
1953 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1954 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1956 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1958 #ifndef FIX_DOUBLE_SPACE
1959 // check for double spaces
1960 if (sel_start_cursor.pos &&
1961 sel_start_cursor.par->Last() > sel_start_cursor.pos
1962 && sel_start_cursor.par
1963 ->IsLineSeparator(sel_start_cursor.pos - 1)
1964 && sel_start_cursor.par
1965 ->IsLineSeparator(sel_start_cursor.pos)) {
1966 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1969 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1972 endpar = sel_end_cursor.par->Next();
1974 // cut more than one paragraph
1977 ->BreakParagraphConservative(sel_end_cursor.pos);
1978 #ifndef FIX_DOUBLE_SPACE
1979 // insert a space at the end if there was one
1982 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1984 sel_end_cursor.par = sel_end_cursor.par->Next();
1985 sel_end_cursor.pos = 0;
1987 cursor = sel_end_cursor;
1989 #ifndef FIX_DOUBLE_SPACE
1990 // please break behind a space, if there is one.
1991 // The space should be copied too
1992 if (sel_start_cursor.par
1993 ->IsLineSeparator(sel_start_cursor.pos))
1994 sel_start_cursor.pos++;
1996 sel_start_cursor.par
1997 ->BreakParagraphConservative(sel_start_cursor.pos);
1998 #ifndef FIX_DOUBLE_SPACE
1999 if (!sel_start_cursor.pos
2000 || sel_start_cursor.par
2001 ->IsLineSeparator(sel_start_cursor.pos - 1)
2002 || sel_start_cursor.par
2003 ->IsNewline(sel_start_cursor.pos - 1)) {
2004 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2007 // store the endparagraph for redoing later
2008 endpar = sel_end_cursor.par->Next(); /* needed because
2013 // store the selection
2014 simple_cut_buffer = sel_start_cursor.par
2015 ->ParFromPos(sel_start_cursor.pos)->next;
2016 simple_cut_buffer->previous = 0;
2017 sel_end_cursor.par->previous->next = 0;
2019 // cut the selection
2020 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2021 = sel_end_cursor.par;
2023 sel_end_cursor.par->previous
2024 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2026 // care about footnotes
2027 if (simple_cut_buffer->footnoteflag) {
2028 LyXParagraph * tmppar = simple_cut_buffer;
2030 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2031 tmppar = tmppar->next;
2035 // the cut selection should begin with standard layout
2036 simple_cut_buffer->Clear();
2038 // paste the paragraphs again, if possible
2040 sel_start_cursor.par->Next()->ClearParagraph();
2041 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2043 !sel_start_cursor.par->Next()->Last())
2044 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2046 #ifndef FIX_DOUBLE_SPACE
2047 // maybe a forgotten blank
2048 if (sel_start_cursor.pos
2049 && sel_start_cursor.par
2050 ->IsLineSeparator(sel_start_cursor.pos)
2051 && sel_start_cursor.par
2052 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2053 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2058 // sometimes necessary
2060 sel_start_cursor.par->ClearParagraph();
2062 RedoParagraphs(sel_start_cursor, endpar);
2065 cursor = sel_start_cursor;
2066 SetCursor(cursor.par, cursor.pos);
2067 sel_cursor = cursor;
2068 UpdateCounters(cursor.row);
2072 void LyXText::CopySelection()
2074 // this doesnt make sense, if there is no selection
2078 // ok we have a selection. This is always between sel_start_cursor
2079 // and sel_end cursor
2080 LyXParagraph * tmppar;
2082 /* check wether there are half footnotes in the selection */
2083 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2084 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2085 tmppar = sel_start_cursor.par;
2086 while (tmppar != sel_end_cursor.par) {
2087 if (tmppar->footnoteflag !=
2088 sel_end_cursor.par->footnoteflag) {
2089 WriteAlert(_("Impossible operation"),
2090 _("Don't know what to do"
2091 " with half floats."),
2095 tmppar = tmppar->Next();
2099 /* table stuff -- begin */
2100 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2101 if ( sel_start_cursor.par != sel_end_cursor.par){
2102 WriteAlert(_("Impossible operation"),
2103 _("Don't know what to do with half tables."),
2108 /* table stuff -- end */
2110 // delete the simple_cut_buffer
2111 DeleteSimpleCutBuffer();
2113 // set the textclass
2114 simple_cut_buffer_textclass = parameters->textclass;
2116 #ifdef FIX_DOUBLE_SPACE
2117 // copy behind a space if there is one
2118 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2119 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2120 && (sel_start_cursor.par != sel_end_cursor.par
2121 || sel_start_cursor.pos < sel_end_cursor.pos))
2122 sel_start_cursor.pos++;
2124 // there are two cases: copy only within one paragraph
2125 // or more than one paragraph
2126 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2127 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2128 // only within one paragraph
2129 simple_cut_buffer = new LyXParagraph;
2130 LyXParagraph::size_type i = 0;
2131 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2132 sel_start_cursor.par->CopyIntoMinibuffer(i);
2133 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2136 // copy more than one paragraph
2137 // clone the paragraphs within the selection
2139 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2140 simple_cut_buffer = tmppar->Clone();
2141 LyXParagraph *tmppar2 = simple_cut_buffer;
2143 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2145 tmppar = tmppar->next;
2146 tmppar2->next = tmppar->Clone();
2147 tmppar2->next->previous = tmppar2;
2148 tmppar2 = tmppar2->next;
2152 // care about footnotes
2153 if (simple_cut_buffer->footnoteflag) {
2154 tmppar = simple_cut_buffer;
2156 tmppar->footnoteflag =
2157 LyXParagraph::NO_FOOTNOTE;
2158 tmppar = tmppar->next;
2162 // the simple_cut_buffer paragraph is too big
2163 LyXParagraph::size_type tmpi2 =
2164 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2165 for (; tmpi2; --tmpi2)
2166 simple_cut_buffer->Erase(0);
2168 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2170 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2171 while (tmppar2->size() > tmpi2) {
2172 tmppar2->Erase(tmppar2->text.size() - 1);
2178 void LyXText::PasteSelection()
2180 // this does not make sense, if there is nothing to paste
2181 if (!simple_cut_buffer)
2184 LyXParagraph * tmppar;
2185 LyXParagraph * endpar;
2187 LyXCursor tmpcursor;
2189 // be carefull with footnotes in footnotes
2190 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2192 // check whether the cut_buffer includes a footnote
2193 tmppar = simple_cut_buffer;
2195 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2196 tmppar = tmppar->next;
2199 WriteAlert(_("Impossible operation"),
2200 _("Can't paste float into float!"),
2206 /* table stuff -- begin */
2207 if (cursor.par->table) {
2208 if (simple_cut_buffer->next) {
2209 WriteAlert(_("Impossible operation"),
2210 _("Table cell cannot include more than one paragraph!"),
2215 /* table stuff -- end */
2217 SetUndo(Undo::INSERT,
2218 cursor.par->ParFromPos(cursor.pos)->previous,
2219 cursor.par->ParFromPos(cursor.pos)->next);
2223 // There are two cases: cutbuffer only one paragraph or many
2224 if (!simple_cut_buffer->next) {
2225 // only within a paragraph
2227 #ifndef FIX_DOUBLE_SPACE
2228 // please break behind a space, if there is one
2229 while (tmpcursor.par->Last() > tmpcursor.pos
2230 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2233 tmppar = simple_cut_buffer->Clone();
2234 /* table stuff -- begin */
2235 bool table_too_small = false;
2236 if (tmpcursor.par->table) {
2237 while (simple_cut_buffer->text.size()
2238 && !table_too_small) {
2239 if (simple_cut_buffer->IsNewline(0)){
2240 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2242 simple_cut_buffer->Erase(0);
2243 if (tmpcursor.pos < tmpcursor.par->Last())
2246 table_too_small = true;
2248 #ifdef FIX_DOUBLE_SPACE
2249 // This is an attempt to fix the
2250 // "never insert a space at the
2251 // beginning of a paragraph" problem.
2252 if (tmpcursor.pos == 0
2253 && simple_cut_buffer->IsLineSeparator(0)) {
2254 simple_cut_buffer->Erase(0);
2256 simple_cut_buffer->CutIntoMinibuffer(0);
2257 simple_cut_buffer->Erase(0);
2258 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2262 simple_cut_buffer->CutIntoMinibuffer(0);
2263 simple_cut_buffer->Erase(0);
2264 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2270 /* table stuff -- end */
2271 // Some provisions should be done here for checking
2272 // if we are inserting at the beginning of a
2273 // paragraph. If there are a space at the beginning
2274 // of the text to insert and we are inserting at
2275 // the beginning of the paragraph the space should
2277 while (simple_cut_buffer->text.size()) {
2278 #ifdef FIX_DOUBLE_SPACE
2279 // This is an attempt to fix the
2280 // "never insert a space at the
2281 // beginning of a paragraph" problem.
2282 if (tmpcursor.pos == 0
2283 && simple_cut_buffer->IsLineSeparator(0)) {
2284 simple_cut_buffer->Erase(0);
2286 simple_cut_buffer->CutIntoMinibuffer(0);
2287 simple_cut_buffer->Erase(0);
2288 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2292 simple_cut_buffer->CutIntoMinibuffer(0);
2293 simple_cut_buffer->Erase(0);
2294 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2299 delete simple_cut_buffer;
2300 simple_cut_buffer = tmppar;
2301 endpar = tmpcursor.par->Next();
2305 // make a copy of the simple cut_buffer
2306 tmppar = simple_cut_buffer;
2307 LyXParagraph * simple_cut_clone = tmppar->Clone();
2308 LyXParagraph * tmppar2 = simple_cut_clone;
2309 if (cursor.par->footnoteflag){
2310 tmppar->footnoteflag = cursor.par->footnoteflag;
2311 tmppar->footnotekind = cursor.par->footnotekind;
2313 while (tmppar->next) {
2314 tmppar = tmppar->next;
2315 tmppar2->next = tmppar->Clone();
2316 tmppar2->next->previous = tmppar2;
2317 tmppar2 = tmppar2->next;
2318 if (cursor.par->footnoteflag){
2319 tmppar->footnoteflag = cursor.par->footnoteflag;
2320 tmppar->footnotekind = cursor.par->footnotekind;
2324 // make sure there is no class difference
2325 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2326 parameters->textclass,
2329 // make the simple_cut_buffer exactly the same layout than
2330 // the cursor paragraph
2331 simple_cut_buffer->MakeSameLayout(cursor.par);
2333 // find the end of the buffer
2334 LyXParagraph * lastbuffer = simple_cut_buffer;
2335 while (lastbuffer->Next())
2336 lastbuffer = lastbuffer->Next();
2338 #ifndef FIX_DOUBLE_SPACE
2339 // Please break behind a space, if there is one. The space
2340 // should be copied too.
2341 if (cursor.par->Last() > cursor.pos
2342 && cursor.par->IsLineSeparator(cursor.pos))
2345 bool paste_the_end = false;
2347 // open the paragraph for inserting the simple_cut_buffer
2349 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2350 cursor.par->BreakParagraphConservative(cursor.pos);
2351 paste_the_end = true;
2354 #ifndef FIX_DOUBLE_SPACE
2355 // be careful with double spaces
2356 if ((!cursor.par->Last()
2357 || cursor.par->IsLineSeparator(cursor.pos - 1)
2358 || cursor.par->IsNewline(cursor.pos - 1))
2359 && simple_cut_buffer->text.size()
2360 && simple_cut_buffer->IsLineSeparator(0))
2361 simple_cut_buffer->Erase(0);
2363 // set the end for redoing later
2364 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2367 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2368 cursor.par->ParFromPos(cursor.pos)->next;
2369 cursor.par->ParFromPos(cursor.pos)->next->previous =
2370 lastbuffer->ParFromPos(lastbuffer->Last());
2372 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2373 simple_cut_buffer->previous =
2374 cursor.par->ParFromPos(cursor.pos);
2376 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2377 lastbuffer = cursor.par;
2379 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2381 // store the new cursor position
2382 tmpcursor.par = lastbuffer;
2383 tmpcursor.pos = lastbuffer->Last();
2385 // maybe some pasting
2386 if (lastbuffer->Next() && paste_the_end) {
2387 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2388 #ifndef FIX_DOUBLE_SPACE
2389 // be careful with double spaces
2390 if ((!lastbuffer->Last()
2391 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2392 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2393 && lastbuffer->Next()->Last()
2394 && lastbuffer->Next()->IsLineSeparator(0))
2395 lastbuffer->Next()->Erase(0);
2397 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2399 } else if (!lastbuffer->Next()->Last()) {
2400 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2401 #ifndef FIX_DOUBLE_SPACE
2402 // be careful witth double spaces
2403 if ((!lastbuffer->Last()
2404 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2405 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2406 && lastbuffer->Next()->Last()
2407 && lastbuffer->Next()->IsLineSeparator(0))
2408 lastbuffer->Next()->Erase(0);
2410 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2412 } else if (!lastbuffer->Last()) {
2413 lastbuffer->MakeSameLayout(lastbuffer->next);
2414 #ifndef FIX_DOUBLE_SPACE
2415 // be careful witth double spaces
2416 if ((!lastbuffer->Last()
2417 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2418 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2419 && lastbuffer->Next()->Last()
2420 && lastbuffer->Next()->IsLineSeparator(0))
2421 lastbuffer->Next()->Erase(0);
2423 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2426 lastbuffer->Next()->ClearParagraph();
2429 // restore the simple cut buffer
2430 simple_cut_buffer = simple_cut_clone;
2433 RedoParagraphs(cursor, endpar);
2435 SetCursor(cursor.par, cursor.pos);
2438 sel_cursor = cursor;
2439 SetCursor(tmpcursor.par, tmpcursor.pos);
2441 UpdateCounters(cursor.row);
2445 // returns a pointer to the very first LyXParagraph
2446 LyXParagraph * LyXText::FirstParagraph() const
2448 return params->paragraph;
2452 // returns true if the specified string is at the specified position
2453 bool LyXText::IsStringInText(LyXParagraph * par,
2454 LyXParagraph::size_type pos,
2455 char const * str) const
2459 while (pos + i < par->Last() && str[i] &&
2460 str[i] == par->GetChar(pos + i)) {
2470 // sets the selection over the number of characters of string, no check!!
2471 void LyXText::SetSelectionOverString(char const * string)
2473 sel_cursor = cursor;
2474 for (int i = 0; string[i]; ++i)
2480 // simple replacing. The font of the first selected character is used
2481 void LyXText::ReplaceSelectionWithString(char const * str)
2486 if (!selection) { // create a dummy selection
2487 sel_end_cursor = cursor;
2488 sel_start_cursor = cursor;
2491 // Get font setting before we cut
2492 LyXParagraph::size_type pos = sel_end_cursor.pos;
2493 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2495 // Insert the new string
2496 for (int i = 0; str[i]; ++i) {
2497 sel_end_cursor.par->InsertChar(pos, str[i]);
2498 sel_end_cursor.par->SetFont(pos, font);
2502 // Cut the selection
2509 // if the string can be found: return true and set the cursor to
2511 bool LyXText::SearchForward(char const * str) const
2513 LyXParagraph * par = cursor.par;
2514 LyXParagraph::size_type pos = cursor.pos;
2515 while (par && !IsStringInText(par, pos, str)) {
2516 if (pos < par->Last() - 1)
2524 SetCursor(par, pos);
2532 bool LyXText::SearchBackward(char const * string) const
2534 LyXParagraph * par = cursor.par;
2535 int pos = cursor.pos;
2541 // We skip empty paragraphs (Asger)
2543 par = par->Previous();
2545 pos = par->Last() - 1;
2546 } while (par && pos < 0);
2548 } while (par && !IsStringInText(par, pos, string));
2551 SetCursor(par, pos);
2558 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2560 char * str = new char[text.size() + 1];
2561 copy(text.begin(), text.end(), str);
2562 str[text.size()] = '\0';
2568 // needed to insert the selection
2569 void LyXText::InsertStringA(char const * s)
2572 LyXParagraph * par = cursor.par;
2573 LyXParagraph::size_type pos = cursor.pos;
2574 LyXParagraph::size_type a = 0;
2576 LyXParagraph * endpar = cursor.par->Next();
2581 textclasslist.Style(parameters->textclass,
2582 cursor.par->GetLayout()).isEnvironment();
2583 // only to be sure, should not be neccessary
2586 // insert the string, don't insert doublespace
2587 string::size_type i = 0;
2588 while (i < str.length()) {
2589 if (str[i] != '\n') {
2591 && i + 1 < str.length() && str[i + 1] != ' '
2592 && pos && par->GetChar(pos - 1)!= ' ') {
2593 par->InsertChar(pos,' ');
2595 } else if (par->table) {
2596 if (str[i] == '\t') {
2597 while((pos < par->size()) &&
2598 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2600 if (pos < par->size())
2602 else // no more fields to fill skip the rest
2604 } else if ((str[i] != 13) &&
2605 ((str[i] & 127) >= ' ')) {
2606 par->InsertChar(pos, str[i]);
2609 } else if (str[i] == ' ') {
2610 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2612 } else if (str[i] == '\t') {
2613 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2614 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2617 } else if (str[i]!= 13 &&
2618 // Ignore unprintables
2619 (str[i] & 127) >= ' ') {
2620 par->InsertChar(pos, str[i]);
2625 if (i + 1 >= str.length()) {
2629 while((pos < par->size()) &&
2630 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2633 cell = NumberOfCell(par, pos);
2634 while((pos < par->size()) &&
2635 !(par->table->IsFirstCell(cell))) {
2636 while((pos < par->size()) &&
2637 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2640 cell = NumberOfCell(par, pos);
2642 if (pos >= par->size())
2643 // no more fields to fill skip the rest
2646 if (!par->text.size()) {
2647 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2650 par->BreakParagraph(pos, flag);
2658 RedoParagraphs(cursor, endpar);
2659 SetCursor(cursor.par, cursor.pos);
2660 sel_cursor = cursor;
2661 SetCursor(par, pos);
2666 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2668 char * str = new char[text.size() + 1];
2669 copy(text.begin(), text.end(), str);
2670 str[text.size()] = '\0';
2676 /* turns double-CR to single CR, others where converted into one blank and 13s
2677 * that are ignored .Double spaces are also converted into one. Spaces at
2678 * the beginning of a paragraph are forbidden. tabs are converted into one
2679 * space. then InsertStringA is called */
2680 void LyXText::InsertStringB(char const * s)
2683 LyXParagraph * par = cursor.par;
2684 string::size_type i = 1;
2685 while (i < str.length()) {
2686 if (str[i] == '\t' && !par->table)
2688 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2690 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2691 if (str[i + 1] != '\n') {
2692 if (str[i - 1] != ' ')
2697 while (i + 1 < str.length()
2698 && (str[i + 1] == ' '
2699 || str[i + 1] == '\t'
2700 || str[i + 1] == '\n'
2701 || str[i + 1] == 13)) {
2708 InsertStringA(str.c_str());
2712 bool LyXText::GotoNextError() const
2714 LyXCursor res = cursor;
2716 if (res.pos < res.par->Last() - 1) {
2720 res.par = res.par->Next();
2725 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2726 && res.par->GetInset(res.pos)->AutoDelete()));
2729 SetCursor(res.par, res.pos);
2736 bool LyXText::GotoNextNote() const
2738 LyXCursor res = cursor;
2740 if (res.pos < res.par->Last() - 1) {
2743 res.par = res.par->Next();
2748 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2749 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2752 SetCursor(res.par, res.pos);
2759 int LyXText::SwitchLayoutsBetweenClasses(char class1, char class2,
2763 if (!par || class1 == class2)
2765 par = par->FirstPhysicalPar();
2767 string name = textclasslist.NameOfLayout(class1, par->layout);
2769 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2770 textclasslist.NumberOfLayout(class2, name);
2773 } else { // layout not found
2774 // use default layout "Standard" (0)
2779 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2781 string s = "Layout had to be changed from\n"
2782 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2783 + "\nbecause of class conversion from\n"
2784 + textclasslist.NameOfClass(class1) + " to "
2785 + textclasslist.NameOfClass(class2);
2786 InsetError * new_inset = new InsetError(s);
2787 par->InsertChar(0, LyXParagraph::META_INSET);
2788 par->InsertInset(0, new_inset);
2797 void LyXText::CheckParagraph(LyXParagraph * par,
2798 LyXParagraph::size_type pos)
2801 LyXCursor tmpcursor;
2803 /* table stuff -- begin*/
2806 CheckParagraphInTable(par, pos);
2809 /* table stuff -- end*/
2812 LyXParagraph::size_type z;
2813 Row * row = GetRow(par, pos, y);
2815 // is there a break one row above
2816 if (row->previous && row->previous->par == row->par) {
2817 z = NextBreakPoint(row->previous, paperwidth);
2818 if ( z >= row->pos) {
2819 // set the dimensions of the row above
2820 y -= row->previous->height;
2822 refresh_row = row->previous;
2823 status = LyXText::NEED_MORE_REFRESH;
2825 BreakAgain(row->previous);
2827 // set the cursor again. Otherwise
2828 // dangling pointers are possible
2829 SetCursor(cursor.par, cursor.pos);
2830 sel_cursor = cursor;
2835 int tmpheight = row->height;
2836 LyXParagraph::size_type tmplast = RowLast(row);
2841 if (row->height == tmpheight && RowLast(row) == tmplast)
2842 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2844 status = LyXText::NEED_MORE_REFRESH;
2846 // check the special right address boxes
2847 if (textclasslist.Style(parameters->textclass,
2848 par->GetLayout()).margintype
2849 == MARGIN_RIGHT_ADDRESS_BOX) {
2850 tmpcursor.par = par;
2851 tmpcursor.row = row;
2854 tmpcursor.x_fix = 0;
2855 tmpcursor.pos = pos;
2856 RedoDrawingOfParagraph(tmpcursor);
2861 // set the cursor again. Otherwise dangling pointers are possible
2862 // also set the selection
2866 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2867 sel_cursor = cursor;
2868 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2869 sel_start_cursor = cursor;
2870 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2871 sel_end_cursor = cursor;
2872 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2873 last_sel_cursor = cursor;
2876 SetCursorIntern(cursor.par, cursor.pos);
2880 // returns 0 if inset wasn't found
2881 int LyXText::UpdateInset(Inset * inset)
2883 // first check the current paragraph
2884 int pos = cursor.par->GetPositionOfInset(inset);
2886 CheckParagraph(cursor.par, pos);
2890 // check every paragraph
2892 LyXParagraph * par = FirstParagraph();
2894 // make sure the paragraph is open
2895 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2896 pos = par->GetPositionOfInset(inset);
2898 CheckParagraph(par, pos);
2909 void LyXText::SetCursor(LyXParagraph * par,
2910 LyXParagraph::size_type pos, bool setfont) const
2912 LyXCursor old_cursor = cursor;
2913 SetCursorIntern(par, pos, setfont);
2914 DeleteEmptyParagraphMechanism(old_cursor);
2918 void LyXText::SetCursorIntern(LyXParagraph * par,
2919 LyXParagraph::size_type pos, bool setfont) const
2923 LyXParagraph * tmppar;
2924 LyXParagraph::size_type vpos,cursor_vpos;
2926 // correct the cursor position if impossible
2927 if (pos > par->Last()){
2928 tmppar = par->ParFromPos(pos);
2929 pos = par->PositionInParFromPos(pos);
2932 if (par->IsDummy() && par->previous &&
2933 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2934 while (par->previous &&
2935 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2936 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2937 par = par->previous ;
2938 if (par->IsDummy() &&
2939 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2940 pos += par->text.size() + 1;
2942 if (par->previous) {
2943 par = par->previous;
2945 pos += par->text.size() + 1;
2953 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2954 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2955 && !cursor.par->IsSeparator(cursor.pos))
2956 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2958 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2959 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2961 current_font = cursor.par->GetFontSettings(cursor.pos);
2962 real_current_font = GetFont(cursor.par, cursor.pos);
2963 if (pos == 0 && par->size() == 0
2964 && owner_->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2965 current_font.setDirection(LyXFont::RTL_DIR);
2966 real_current_font.setDirection(LyXFont::RTL_DIR);
2970 /* get the cursor y position in text */
2971 row = GetRow(par, pos, y);
2972 /* y is now the beginning of the cursor row */
2974 /* y is now the cursor baseline */
2977 /* now get the cursors x position */
2979 float fill_separator, fill_hfill, fill_label_hfill;
2980 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2982 LyXParagraph::size_type last = RowLast(row);
2983 if (row->pos > last)
2985 else if (pos <= last ) {
2986 LyXDirection letter_direction =
2987 row->par->getLetterDirection(pos);
2988 LyXDirection font_direction =
2989 real_current_font.getFontDirection();
2990 if (letter_direction == font_direction || pos == 0)
2991 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
2992 ? log2vis(pos) : log2vis(pos)+1;
2994 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
2995 ? log2vis(pos-1)+1 : log2vis(pos-1);
2997 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
2998 ? log2vis(last)+1 : log2vis(last);
3000 /* table stuff -- begin*/
3001 if (row->par->table) {
3002 int cell = NumberOfCell(row->par, row->pos);
3004 x += row->par->table->GetBeginningOfTextInCell(cell);
3005 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3006 pos = vis2log(vpos);
3007 if (row->par->IsNewline(pos)) {
3008 x = x_old + row->par->table->WidthOfColumn(cell);
3011 x += row->par->table->GetBeginningOfTextInCell(cell);
3013 x += SingleWidth(row->par, pos);
3017 /* table stuff -- end*/
3018 LyXParagraph::size_type main_body =
3019 BeginningOfMainBody(row->par);
3020 if (main_body > 0 &&
3021 (main_body-1 > last ||
3022 !row->par->IsLineSeparator(main_body-1)))
3025 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3026 pos = vis2log(vpos);
3027 if (main_body > 0 && pos == main_body-1) {
3028 x += fill_label_hfill +
3029 GetFont(row->par, -2).stringWidth(
3030 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3031 if (row->par->IsLineSeparator(main_body-1))
3032 x -= SingleWidth(row->par, main_body-1);
3035 x += SingleWidth(row->par, pos);
3036 if (HfillExpansion(row, pos)) {
3037 if (pos >= main_body)
3040 x += fill_label_hfill;
3042 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3050 cursor.x_fix = cursor.x;
3055 void LyXText::SetCursorFromCoordinates(int x, long y) const
3057 LyXCursor old_cursor = cursor;
3059 /* get the row first */
3061 Row * row = GetRowNearY(y);
3063 cursor.par = row->par;
3065 int column = GetColumnNearX(row, x);
3066 cursor.pos = row->pos + column;
3068 cursor.y = y + row->baseline;
3073 (cursor.pos == cursor.par->Last()
3074 || cursor.par->IsSeparator(cursor.pos)
3075 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3076 && !cursor.par->IsSeparator(cursor.pos))
3077 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3079 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3080 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3082 current_font = cursor.par->GetFontSettings(cursor.pos);
3083 real_current_font = GetFont(cursor.par, cursor.pos);
3085 DeleteEmptyParagraphMechanism(old_cursor);
3089 void LyXText::CursorLeft() const
3092 if (cursor.par->table) {
3093 int cell = NumberOfCell(cursor.par, cursor.pos);
3094 if (cursor.par->table->IsContRow(cell) &&
3095 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3102 void LyXText::CursorLeftIntern() const
3104 if (cursor.pos > 0) {
3105 SetCursor(cursor.par, cursor.pos - 1);
3107 else if (cursor.par->Previous()) {
3108 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3113 void LyXText::CursorRight() const
3115 CursorRightIntern();
3116 if (cursor.par->table) {
3117 int cell = NumberOfCell(cursor.par, cursor.pos);
3118 if (cursor.par->table->IsContRow(cell) &&
3119 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3126 void LyXText::CursorRightIntern() const
3128 if (cursor.pos < cursor.par->Last()) {
3129 SetCursor(cursor.par, cursor.pos + 1);
3131 else if (cursor.par->Next()) {
3132 SetCursor(cursor.par->Next(), 0);
3137 void LyXText::CursorUp() const
3139 SetCursorFromCoordinates(cursor.x_fix,
3140 cursor.y - cursor.row->baseline - 1);
3141 if (cursor.par->table) {
3142 int cell = NumberOfCell(cursor.par, cursor.pos);
3143 if (cursor.par->table->IsContRow(cell) &&
3144 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3151 void LyXText::CursorDown() const
3153 if (cursor.par->table &&
3154 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3157 SetCursorFromCoordinates(cursor.x_fix,
3158 cursor.y - cursor.row->baseline
3159 + cursor.row->height + 1);
3160 if (cursor.par->table) {
3161 int cell = NumberOfCell(cursor.par, cursor.pos);
3162 int cell_above = cursor.par->table->GetCellAbove(cell);
3163 while(cursor.par->table &&
3164 cursor.par->table->IsContRow(cell) &&
3165 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3166 SetCursorFromCoordinates(cursor.x_fix,
3167 cursor.y - cursor.row->baseline
3168 + cursor.row->height + 1);
3169 if (cursor.par->table) {
3170 cell = NumberOfCell(cursor.par, cursor.pos);
3171 cell_above = cursor.par->table->GetCellAbove(cell);
3178 void LyXText::CursorUpParagraph() const
3180 if (cursor.pos > 0) {
3181 SetCursor(cursor.par, 0);
3183 else if (cursor.par->Previous()) {
3184 SetCursor(cursor.par->Previous(), 0);
3189 void LyXText::CursorDownParagraph() const
3191 if (cursor.par->Next()) {
3192 SetCursor(cursor.par->Next(), 0);
3194 SetCursor(cursor.par, cursor.par->Last());
3200 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3202 bool deleted = false;
3204 // this is the delete-empty-paragraph-mechanism.
3205 if (selection) return;
3207 #ifdef FIX_DOUBLE_SPACE
3208 /* Ok I'll put some comments here about what is missing.
3209 I have fixed BackSpace (and thus Delete) to not delete
3210 double-spaces automagically. I have also changed Cut,
3211 Copy and Paste to hopefully do some sensible things.
3212 There are still some small problems that can lead to
3213 double spaces stored in the document file or space at
3214 the beginning of paragraphs. This happens if you have
3215 the cursor betwenn to spaces and then save. Or if you
3216 cut and paste and the selection have a space at the
3217 beginning and then save right after the paste. I am
3218 sure none of these are very hard to fix, but I will
3219 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3220 that I can get some feedback. (Lgb)
3223 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3224 // delete the LineSeparator.
3227 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3228 // delete the LineSeparator.
3231 // If the pos around the old_cursor were spaces, delete one of them.
3232 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3233 && old_cursor.pos > 0
3234 && old_cursor.pos < old_cursor.par->Last()
3235 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3236 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3237 old_cursor.par->Erase(old_cursor.pos - 1);
3238 RedoParagraphs(old_cursor, old_cursor.par->Next());
3239 // or RedoDrawingOfParagraph(old_cursor);
3241 if (old_cursor.par == cursor.par &&
3242 cursor.pos > old_cursor.pos)
3243 SetCursor(cursor.par, cursor.pos - 1);
3245 SetCursor(cursor.par, cursor.pos);
3250 // Paragraph should not be deleted if empty
3251 if ((textclasslist.Style(parameters->textclass,
3252 old_cursor.par->GetLayout())).keepempty)
3255 LyXCursor tmpcursor;
3257 if (old_cursor.par != cursor.par) {
3258 if ( (old_cursor.par->Last() == 0
3259 || (old_cursor.par->Last() == 1
3260 && (old_cursor.par->IsLineSeparator(0))))
3261 && old_cursor.par->FirstPhysicalPar()
3262 == old_cursor.par->LastPhysicalPar()) {
3264 // ok, we will delete anything
3266 // make sure that you do not delete any environments
3267 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3268 !(old_cursor.row->previous
3269 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3270 && !(old_cursor.row->next
3271 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3273 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3274 ((old_cursor.row->previous
3275 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3277 (old_cursor.row->next
3278 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3280 status = LyXText::NEED_MORE_REFRESH;
3283 if (old_cursor.row->previous) {
3284 refresh_row = old_cursor.row->previous;
3285 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3287 cursor = old_cursor; // that undo can restore the right cursor position
3288 LyXParagraph * endpar = old_cursor.par->next;
3289 if (endpar && endpar->GetDepth()) {
3290 while (endpar && endpar->GetDepth()) {
3291 endpar = endpar->LastPhysicalPar()->Next();
3294 SetUndo(Undo::DELETE,
3295 old_cursor.par->previous,
3300 RemoveRow(old_cursor.row);
3301 if (params->paragraph == old_cursor.par) {
3302 params->paragraph = params->paragraph->next;
3305 delete old_cursor.par;
3307 /* Breakagain the next par. Needed
3308 * because of the parindent that
3309 * can occur or dissappear. The
3310 * next row can change its height,
3311 * if there is another layout before */
3312 if (refresh_row->next) {
3313 BreakAgain(refresh_row->next);
3314 UpdateCounters(refresh_row);
3316 SetHeightOfRow(refresh_row);
3318 refresh_row = old_cursor.row->next;
3319 refresh_y = old_cursor.y - old_cursor.row->baseline;
3322 cursor = old_cursor; // that undo can restore the right cursor position
3323 LyXParagraph *endpar = old_cursor.par->next;
3324 if (endpar && endpar->GetDepth()) {
3325 while (endpar && endpar->GetDepth()) {
3326 endpar = endpar->LastPhysicalPar()->Next();
3329 SetUndo(Undo::DELETE,
3330 old_cursor.par->previous,
3335 RemoveRow(old_cursor.row);
3337 if (params->paragraph == old_cursor.par) {
3338 params->paragraph = params->paragraph->next;
3340 delete old_cursor.par;
3342 /* Breakagain the next par. Needed
3343 because of the parindent that can
3344 occur or dissappear.
3345 The next row can change its height,
3346 if there is another layout before
3349 BreakAgain(refresh_row);
3350 UpdateCounters(refresh_row->previous);
3355 SetCursor(cursor.par, cursor.pos);
3357 /* if (cursor.y > old_cursor.y)
3358 cursor.y -= old_cursor.row->height; */
3360 if (sel_cursor.par == old_cursor.par
3361 && sel_cursor.pos == sel_cursor.pos) {
3362 // correct selection
3363 sel_cursor = cursor;
3368 if (old_cursor.par->ClearParagraph()){
3369 RedoParagraphs(old_cursor, old_cursor.par->Next());
3371 SetCursor(cursor.par, cursor.pos);
3372 sel_cursor = cursor;
3379 LyXParagraph * LyXText::GetParFromID(int id)
3381 LyXParagraph * result = FirstParagraph();
3382 while (result && result->id() != id)
3383 result = result->next;
3389 bool LyXText::TextUndo()
3391 // returns false if no undo possible
3392 Undo * undo = params->undostack.pop();
3397 .push(CreateUndo(undo->kind,
3398 GetParFromID(undo->number_of_before_par),
3399 GetParFromID(undo->number_of_behind_par)));
3401 return TextHandleUndo(undo);
3405 bool LyXText::TextRedo()
3407 // returns false if no redo possible
3408 Undo * undo = params->redostack.pop();
3413 .push(CreateUndo(undo->kind,
3414 GetParFromID(undo->number_of_before_par),
3415 GetParFromID(undo->number_of_behind_par)));
3417 return TextHandleUndo(undo);
3421 bool LyXText::TextHandleUndo(Undo * undo)
3423 // returns false if no undo possible
3424 bool result = false;
3426 LyXParagraph * before =
3427 GetParFromID(undo->number_of_before_par);
3428 LyXParagraph * behind =
3429 GetParFromID(undo->number_of_behind_par);
3430 LyXParagraph * tmppar;
3431 LyXParagraph * tmppar2;
3432 LyXParagraph * tmppar3;
3433 LyXParagraph * tmppar4;
3434 LyXParagraph * endpar;
3435 LyXParagraph * tmppar5;
3437 // if there's no before take the beginning
3438 // of the document for redoing
3440 SetCursorIntern(FirstParagraph(), 0);
3442 // replace the paragraphs with the undo informations
3444 tmppar3 = undo->par;
3445 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3448 while (tmppar4->next)
3449 tmppar4 = tmppar4->next;
3450 } // get last undo par
3452 // now remove the old text if there is any
3453 if (before != behind || (!behind && !before)){
3455 tmppar5 = before->next;
3457 tmppar5 = params->paragraph;
3459 while (tmppar5 && tmppar5 != behind){
3461 tmppar5 = tmppar5->next;
3462 // a memory optimization for edit: Only layout information
3463 // is stored in the undo. So restore the text informations.
3464 if (undo->kind == Undo::EDIT){
3465 tmppar2->text = tmppar->text;
3466 tmppar->text.clear();
3467 tmppar2 = tmppar2->next;
3469 if ( currentrow && currentrow->par == tmppar )
3470 currentrow = currentrow -> previous;
3471 // Commenting out this might remove the error
3472 // reported by Purify, but it might also
3473 // introduce a memory leak. We need to
3479 // put the new stuff in the list if there is one
3482 before->next = tmppar3;
3484 params->paragraph = tmppar3;
3485 tmppar3->previous = before;
3489 params->paragraph = behind;
3492 tmppar4->next = behind;
3494 behind->previous = tmppar4;
3498 // Set the cursor for redoing
3500 SetCursorIntern(before->FirstSelfrowPar(), 0);
3501 // check wether before points to a closed float and open it if necessary
3502 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3503 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3505 while (tmppar4->previous &&
3506 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3507 tmppar4 = tmppar4->previous;
3508 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3509 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3510 tmppar4 = tmppar4->next;
3515 // open a cosed footnote at the end if necessary
3516 if (behind && behind->previous &&
3517 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3518 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3519 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3520 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3521 behind = behind->next;
3525 // calculate the endpar for redoing the paragraphs.
3527 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3528 endpar = behind->LastPhysicalPar()->Next();
3530 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3535 tmppar = GetParFromID(undo->number_of_cursor_par);
3536 RedoParagraphs(cursor, endpar);
3538 SetCursorIntern(tmppar, undo->cursor_pos);
3539 UpdateCounters(cursor.row);
3549 void LyXText::FinishUndo()
3551 // makes sure the next operation will be stored
3552 undo_finished = True;
3556 void LyXText::FreezeUndo()
3558 // this is dangerous and for internal use only
3563 void LyXText::UnFreezeUndo()
3565 // this is dangerous and for internal use only
3566 undo_frozen = false;
3570 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3571 LyXParagraph const * behind) const
3574 params->undostack.push(CreateUndo(kind, before, behind));
3575 params->redostack.clear();
3579 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3580 LyXParagraph const * behind)
3582 params->redostack.push(CreateUndo(kind, before, behind));
3586 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3587 LyXParagraph const * behind) const
3589 int before_number = -1;
3590 int behind_number = -1;
3592 before_number = before->id();
3594 behind_number = behind->id();
3595 // Undo::EDIT and Undo::FINISH are
3596 // always finished. (no overlapping there)
3597 // overlapping only with insert and delete inside one paragraph:
3598 // Nobody wants all removed character
3599 // appear one by one when undoing.
3600 // EDIT is special since only layout information, not the
3601 // contents of a paragaph are stored.
3602 if (!undo_finished && kind != Undo::EDIT &&
3603 kind != Undo::FINISH){
3604 // check wether storing is needed
3605 if (!params->undostack.empty() &&
3606 params->undostack.top()->kind == kind &&
3607 params->undostack.top()->number_of_before_par == before_number &&
3608 params->undostack.top()->number_of_behind_par == behind_number ){
3613 // create a new Undo
3614 LyXParagraph * undopar;
3615 LyXParagraph * tmppar;
3616 LyXParagraph * tmppar2;
3618 LyXParagraph * start = 0;
3619 LyXParagraph * end = 0;
3622 start = before->next;
3624 start = FirstParagraph();
3626 end = behind->previous;
3628 end = FirstParagraph();
3634 && start != end->next
3635 && (before != behind || (!before && !behind))) {
3637 tmppar2 = tmppar->Clone();
3638 tmppar2->id(tmppar->id());
3640 // a memory optimization: Just store the layout information
3642 if (kind == Undo::EDIT){
3643 tmppar2->text.clear();
3648 while (tmppar != end && tmppar->next) {
3649 tmppar = tmppar->next;
3650 tmppar2->next = tmppar->Clone();
3651 tmppar2->next->id(tmppar->id());
3652 // a memory optimization: Just store the layout
3653 // information when only edit
3654 if (kind == Undo::EDIT){
3655 tmppar2->next->text.clear();
3657 tmppar2->next->previous = tmppar2;
3658 tmppar2 = tmppar2->next;
3662 undopar = 0; // nothing to replace (undo of delete maybe)
3664 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3665 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3667 Undo * undo = new Undo(kind,
3668 before_number, behind_number,
3669 cursor_par, cursor_pos,
3672 undo_finished = false;
3677 void LyXText::SetCursorParUndo()
3679 SetUndo(Undo::FINISH,
3680 cursor.par->ParFromPos(cursor.pos)->previous,
3681 cursor.par->ParFromPos(cursor.pos)->next);
3685 void LyXText::RemoveTableRow(LyXCursor * cur) const
3691 // move to the previous row
3692 int cell_act = NumberOfCell(cur->par, cur->pos);
3695 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3698 !cur->par->table->IsFirstCell(cell_act)) {
3700 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3705 // now we have to pay attention if the actual table is the
3706 // main row of TableContRows and if yes to delete all of them
3711 // delete up to the next row
3712 while (cur->pos < cur->par->Last() &&
3714 || !cur->par->table->IsFirstCell(cell_act))) {
3715 while (cur->pos < cur->par->Last() &&
3716 !cur->par->IsNewline(cur->pos))
3717 cur->par->Erase(cur->pos);
3720 if (cur->pos < cur->par->Last())
3721 cur->par->Erase(cur->pos);
3723 if (cur->pos && cur->pos == cur->par->Last()) {
3725 cur->par->Erase(cur->pos); // no newline at very end!
3727 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3728 !cur->par->table->IsContRow(cell_org) &&
3729 cur->par->table->IsContRow(cell));
3730 cur->par->table->DeleteRow(cell_org);
3735 bool LyXText::IsEmptyTableCell() const
3737 LyXParagraph::size_type pos = cursor.pos - 1;
3738 while (pos >= 0 && pos < cursor.par->Last()
3739 && !cursor.par->IsNewline(pos))
3741 return cursor.par->IsNewline(pos + 1);
3745 void LyXText::toggleAppendix(){
3746 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3747 bool start = !par->start_of_appendix;
3749 // ensure that we have only one start_of_appendix in this document
3750 LyXParagraph * tmp = FirstParagraph();
3751 for (; tmp; tmp = tmp->next)
3752 tmp->start_of_appendix = 0;
3753 par->start_of_appendix = start;
3755 // we can set the refreshing parameters now
3756 status = LyXText::NEED_MORE_REFRESH;
3758 refresh_row = 0; // not needed for full update
3760 SetCursor(cursor.par, cursor.pos);