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 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
39 #define FIX_DOUBLE_SPACE 1
43 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
51 parameters = &p->params;
55 status = LyXText::UNCHANGED;
56 LyXParagraph * par = p->paragraph;
57 current_font = GetFont(par, 0);
62 InsertParagraph(par, lastrow);
66 // set cursor at the very top position
67 selection = true; /* these setting is necessary
68 because of the delete-empty-
69 paragraph mechanism in
71 SetCursor(firstrow->par, 0);
76 // no rebreak necessary
82 // Default layouttype for copy environment type
89 // Delete all rows, this does not touch the paragraphs!
90 Row * tmprow = firstrow;
92 tmprow = firstrow->next;
99 void LyXText::owner(BufferView * bv)
101 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 // If position is -1, we get the layout font of the paragraph.
111 // If position is -2, we get the font of the manual label of the paragraph.
112 LyXFont LyXText::GetFont(LyXParagraph * par,
113 LyXParagraph::size_type pos) const
115 LyXLayout const & layout =
116 textclasslist.Style(parameters->textclass, par->GetLayout());
118 char par_depth = par->GetDepth();
119 // We specialize the 95% common case:
120 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
123 if (layout.labeltype == LABEL_MANUAL
124 && pos < BeginningOfMainBody(par)) {
126 return par->GetFontSettings(pos).
127 realize(layout.reslabelfont);
129 return par->GetFontSettings(pos).
130 realize(layout.resfont);
133 // process layoutfont for pos == -1 and labelfont for pos < -1
135 return layout.resfont;
137 return layout.reslabelfont;
141 // The uncommon case need not be optimized as much
143 LyXFont layoutfont, tmpfont;
147 if (pos < BeginningOfMainBody(par)) {
149 layoutfont = layout.labelfont;
152 layoutfont = layout.font;
154 tmpfont = par->GetFontSettings(pos);
155 tmpfont.realize(layoutfont);
158 // process layoutfont for pos == -1 and labelfont for pos < -1
160 tmpfont = layout.font;
162 tmpfont = layout.labelfont;
165 // Resolve against environment font information
166 while (par && par_depth && !tmpfont.resolved()) {
167 par = par->DepthHook(par_depth - 1);
169 tmpfont.realize(textclasslist.
170 Style(parameters->textclass,
171 par->GetLayout()).font);
172 par_depth = par->GetDepth();
176 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
178 // Cosmetic improvement: If this is an open footnote, make the font
180 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
181 && par->footnotekind == LyXParagraph::FOOTNOTE) {
189 void LyXText::SetCharFont(LyXParagraph * par,
190 LyXParagraph::size_type pos,
194 // Let the insets convert their font
195 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
196 if (par->GetInset(pos))
197 font = par->GetInset(pos)->ConvertFont(font);
200 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
203 // Get concrete layout font to reduce against
206 if (pos < BeginningOfMainBody(par))
207 layoutfont = layout.labelfont;
209 layoutfont = layout.font;
211 // Realize against environment font information
212 if (par->GetDepth()){
213 LyXParagraph * tp = par;
214 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
215 tp = tp->DepthHook(tp->GetDepth()-1);
217 layoutfont.realize(textclasslist.
218 Style(parameters->textclass,
219 tp->GetLayout()).font);
223 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
225 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
226 && par->footnotekind == LyXParagraph::FOOTNOTE) {
227 layoutfont.decSize();
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 par->SetFont(pos, font);
237 /* inserts a new row behind the specified row, increments
238 * the touched counters */
239 void LyXText::InsertRow(Row * row, LyXParagraph * par,
240 LyXParagraph::size_type pos) const
242 Row * tmprow = new Row;
244 tmprow->previous = 0;
245 tmprow->next = firstrow;
248 tmprow->previous = row;
249 tmprow->next = row->next;
254 tmprow->next->previous = tmprow;
256 if (tmprow->previous)
257 tmprow->previous->next = tmprow;
265 ++number_of_rows; // one more row
269 // removes the row and reset the touched counters
270 void LyXText::RemoveRow(Row * row) const
272 /* this must not happen before the currentrow for clear reasons.
273 so the trick is just to set the current row onto the previous
276 GetRow(row->par, row->pos, unused_y);
277 currentrow = currentrow->previous;
279 currentrow_y -= currentrow->height;
284 row->next->previous = row->previous;
285 if (!row->previous) {
286 firstrow = row->next;
288 row->previous->next = row->next;
291 lastrow = row->previous;
293 height -= row->height; // the text becomes smaller
296 --number_of_rows; // one row less
300 // remove all following rows of the paragraph of the specified row.
301 void LyXText::RemoveParagraph(Row * row) const
303 LyXParagraph * tmppar = row->par;
307 while (row && row->par == tmppar) {
315 // insert the specified paragraph behind the specified row
316 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
318 InsertRow(row, par, 0); /* insert a new row, starting
321 SetCounter(par); // set the counters
323 // and now append the whole paragraph behind the new row
325 firstrow->height = 0;
326 AppendParagraph(firstrow);
328 row->next->height = 0;
329 AppendParagraph(row->next);
334 void LyXText::ToggleFootnote()
336 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
338 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
340 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
342 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
348 void LyXText::OpenStuff()
350 if (cursor.pos == 0 && cursor.par->bibkey){
351 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
353 else if (cursor.pos < cursor.par->Last()
354 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
355 && cursor.par->GetInset(cursor.pos)->Editable()) {
356 owner_->owner()->getMiniBuffer()
357 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
358 if (cursor.par->GetInset(cursor.pos)->Editable() != 2)
360 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
367 void LyXText::CloseFootnote()
369 LyXParagraph * tmppar;
370 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
372 // if the cursor is not in an open footnote, or
373 // there is no open footnote in this paragraph, just return.
374 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
377 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
378 owner_->owner()->getMiniBuffer()
379 ->Set(_("Nothing to do"));
383 // ok, move the cursor right before the footnote
384 // just a little faster than using CursorRight()
386 cursor.par->ParFromPos(cursor.pos) != par;
390 // now the cursor is at the beginning of the physical par
391 SetCursor(cursor.par,
393 cursor.par->ParFromPos(cursor.pos)->text.size());
395 /* we are in a footnote, so let us move at the beginning */
396 /* this is just faster than using just CursorLeft() */
399 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
400 // just a little bit faster than movin the cursor
401 tmppar = tmppar->Previous();
403 SetCursor(tmppar, tmppar->Last());
406 // the cursor must be exactly before the footnote
407 par = cursor.par->ParFromPos(cursor.pos);
409 status = LyXText::NEED_MORE_REFRESH;
410 refresh_row = cursor.row;
411 refresh_y = cursor.y - cursor.row->baseline;
414 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
415 Row * row = cursor.row;
417 tmppar->CloseFootnote(cursor.pos);
419 while (tmppar != endpar) {
420 RemoveRow(row->next);
422 tmppar = row->next->par;
427 AppendParagraph(cursor.row);
429 SetCursor(cursor.par, cursor.pos);
433 if (cursor.row->next)
434 SetHeightOfRow(cursor.row->next);
438 /* used in setlayout */
439 // Asger is not sure we want to do this...
440 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
443 LyXLayout const & layout =
444 textclasslist.Style(parameters->textclass, par->GetLayout());
446 LyXFont layoutfont, tmpfont;
447 for (LyXParagraph::size_type pos = 0;
448 pos < par->Last(); ++pos) {
449 if (pos < BeginningOfMainBody(par))
450 layoutfont = layout.labelfont;
452 layoutfont = layout.font;
454 tmpfont = par->GetFontSettings(pos);
455 tmpfont.reduce(layoutfont);
456 par->SetFont(pos, tmpfont);
461 // set layout over selection and make a total rebreak of those paragraphs
462 void LyXText::SetLayout(LyXTextClass::size_type layout)
466 // if there is no selection just set the layout
467 // of the current paragraph */
469 sel_start_cursor = cursor; // dummy selection
470 sel_end_cursor = cursor;
473 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
474 LyXParagraph * undoendpar = endpar;
476 if (endpar && endpar->GetDepth()) {
477 while (endpar && endpar->GetDepth()) {
478 endpar = endpar->LastPhysicalPar()->Next();
483 endpar = endpar->Next(); // because of parindents etc.
487 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
490 tmpcursor = cursor; /* store the current cursor */
492 /* ok we have a selection. This is always between sel_start_cursor
493 * and sel_end cursor */
494 cursor = sel_start_cursor;
496 LyXLayout const & lyxlayout =
497 textclasslist.Style(parameters->textclass, layout);
499 while (cursor.par != sel_end_cursor.par) {
500 if (cursor.par->footnoteflag ==
501 sel_start_cursor.par->footnoteflag) {
502 cursor.par->SetLayout(layout);
503 MakeFontEntriesLayoutSpecific(cursor.par);
504 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
505 fppar->added_space_top = lyxlayout.fill_top ?
506 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
507 fppar->added_space_bottom = lyxlayout.fill_bottom ?
508 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
509 if (lyxlayout.margintype == MARGIN_MANUAL)
510 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
511 if (lyxlayout.labeltype != LABEL_BIBLIO
513 delete fppar->bibkey;
517 cursor.par = cursor.par->Next();
519 if (cursor.par->footnoteflag ==
520 sel_start_cursor.par->footnoteflag) {
521 cursor.par->SetLayout(layout);
522 MakeFontEntriesLayoutSpecific(cursor.par);
523 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
524 fppar->added_space_top = lyxlayout.fill_top ?
525 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
526 fppar->added_space_bottom = lyxlayout.fill_bottom ?
527 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
528 if (lyxlayout.margintype == MARGIN_MANUAL)
529 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
530 if (lyxlayout.labeltype != LABEL_BIBLIO
532 delete fppar->bibkey;
537 RedoParagraphs(sel_start_cursor, endpar);
539 // we have to reset the selection, because the
540 // geometry could have changed */
541 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
543 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
544 UpdateCounters(cursor.row);
547 SetCursor(tmpcursor.par, tmpcursor.pos);
551 // increment depth over selection and
552 // make a total rebreak of those paragraphs
553 void LyXText::IncDepth()
555 // If there is no selection, just use the current paragraph
557 sel_start_cursor = cursor; // dummy selection
558 sel_end_cursor = cursor;
561 // We end at the next paragraph with depth 0
562 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
563 LyXParagraph * undoendpar = endpar;
565 if (endpar && endpar->GetDepth()) {
566 while (endpar && endpar->GetDepth()) {
567 endpar = endpar->LastPhysicalPar()->Next();
572 endpar = endpar->Next(); // because of parindents etc.
577 .par->ParFromPos(sel_start_cursor.pos)->previous,
580 LyXCursor tmpcursor = cursor; // store the current cursor
582 // ok we have a selection. This is always between sel_start_cursor
583 // and sel_end cursor
584 cursor = sel_start_cursor;
586 bool anything_changed = false;
589 // NOTE: you can't change the depth of a bibliography entry
590 if (cursor.par->footnoteflag ==
591 sel_start_cursor.par->footnoteflag
592 && textclasslist.Style(parameters->textclass,
593 cursor.par->GetLayout()
594 ).labeltype != LABEL_BIBLIO) {
595 LyXParagraph * prev =
596 cursor.par->FirstPhysicalPar()->Previous();
598 && (prev->GetDepth() - cursor.par->GetDepth() > 0
599 || (prev->GetDepth() == cursor.par->GetDepth()
600 && textclasslist.Style(parameters->textclass,
601 prev->GetLayout()).isEnvironment()))) {
602 cursor.par->FirstPhysicalPar()->depth++;
603 anything_changed = true;
606 if (cursor.par == sel_end_cursor.par)
608 cursor.par = cursor.par->Next();
611 // if nothing changed set all depth to 0
612 if (!anything_changed) {
613 cursor = sel_start_cursor;
614 while (cursor.par != sel_end_cursor.par) {
615 cursor.par->FirstPhysicalPar()->depth = 0;
616 cursor.par = cursor.par->Next();
618 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
619 cursor.par->FirstPhysicalPar()->depth = 0;
622 RedoParagraphs(sel_start_cursor, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
628 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
629 UpdateCounters(cursor.row);
632 SetCursor(tmpcursor.par, tmpcursor.pos);
636 // decrement depth over selection and
637 // make a total rebreak of those paragraphs
638 void LyXText::DecDepth()
640 // if there is no selection just set the layout
641 // of the current paragraph
643 sel_start_cursor = cursor; // dummy selection
644 sel_end_cursor = cursor;
647 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
648 LyXParagraph * undoendpar = endpar;
650 if (endpar && endpar->GetDepth()) {
651 while (endpar && endpar->GetDepth()) {
652 endpar = endpar->LastPhysicalPar()->Next();
657 endpar = endpar->Next(); // because of parindents etc.
662 .par->ParFromPos(sel_start_cursor.pos)->previous,
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
669 cursor = sel_start_cursor;
672 if (cursor.par->footnoteflag ==
673 sel_start_cursor.par->footnoteflag) {
674 if (cursor.par->FirstPhysicalPar()->depth)
675 cursor.par->FirstPhysicalPar()->depth--;
677 if (cursor.par == sel_end_cursor.par)
679 cursor.par = cursor.par->Next();
682 RedoParagraphs(sel_start_cursor, endpar);
684 // we have to reset the selection, because the
685 // geometry could have changed
686 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
688 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
689 UpdateCounters(cursor.row);
692 SetCursor(tmpcursor.par, tmpcursor.pos);
696 // set font over selection and make a total rebreak of those paragraphs
697 void LyXText::SetFont(LyXFont const & font, bool toggleall)
699 // if there is no selection just set the current_font
701 // Determine basis font
703 if (cursor.pos < BeginningOfMainBody(cursor.par))
704 layoutfont = GetFont(cursor.par, -2);
706 layoutfont = GetFont(cursor.par, -1);
707 // Update current font
708 real_current_font.update(font, toggleall);
710 // Reduce to implicit settings
711 current_font = real_current_font;
712 current_font.reduce(layoutfont);
713 // And resolve it completely
714 real_current_font.realize(layoutfont);
718 LyXCursor tmpcursor = cursor; // store the current cursor
720 // ok we have a selection. This is always between sel_start_cursor
721 // and sel_end cursor
724 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
725 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
726 cursor = sel_start_cursor;
727 while (cursor.par != sel_end_cursor.par ||
728 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
729 && cursor.pos < sel_end_cursor.pos))
731 if (cursor.pos < cursor.par->Last()
732 && cursor.par->footnoteflag
733 == sel_start_cursor.par->footnoteflag) {
734 // an open footnote should behave
736 LyXFont newfont = GetFont(cursor.par, cursor.pos);
737 newfont.update(font, toggleall);
738 SetCharFont(cursor.par, cursor.pos, newfont);
742 cursor.par = cursor.par->Next();
746 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
748 // we have to reset the selection, because the
749 // geometry could have changed
750 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
752 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
755 SetCursor(tmpcursor.par, tmpcursor.pos);
759 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
761 Row * tmprow = cur.row;
762 long y = cur.y - tmprow->baseline;
764 SetHeightOfRow(tmprow);
765 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
766 // find the first row of the paragraph
767 if (first_phys_par != tmprow->par)
768 while (tmprow->previous
769 && tmprow->previous->par != first_phys_par) {
770 tmprow = tmprow->previous;
772 SetHeightOfRow(tmprow);
774 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
775 tmprow = tmprow->previous;
777 SetHeightOfRow(tmprow);
780 // we can set the refreshing parameters now
781 status = LyXText::NEED_MORE_REFRESH;
783 refresh_row = tmprow;
784 SetCursor(cur.par, cur.pos);
788 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
790 Row * tmprow = cur.row;
792 long y = cur.y - tmprow->baseline;
793 SetHeightOfRow(tmprow);
794 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
795 // find the first row of the paragraph
796 if (first_phys_par != tmprow->par)
797 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
798 tmprow = tmprow->previous;
801 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
802 tmprow = tmprow->previous;
806 // we can set the refreshing parameters now
807 if (status == LyXText::UNCHANGED || y < refresh_y) {
809 refresh_row = tmprow;
811 status = LyXText::NEED_MORE_REFRESH;
812 SetCursor(cur.par, cur.pos);
816 /* deletes and inserts again all paragaphs between the cursor
817 * and the specified par
818 * This function is needed after SetLayout and SetFont etc. */
819 void LyXText::RedoParagraphs(LyXCursor const & cur,
820 LyXParagraph const * endpar) const
823 LyXParagraph * tmppar, * first_phys_par;
825 Row * tmprow = cur.row;
827 long y = cur.y - tmprow->baseline;
829 if (!tmprow->previous){
830 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
832 first_phys_par = tmprow->par->FirstPhysicalPar();
833 // find the first row of the paragraph
834 if (first_phys_par != tmprow->par)
835 while (tmprow->previous
836 && tmprow->previous->par != first_phys_par) {
837 tmprow = tmprow->previous;
840 while (tmprow->previous
841 && tmprow->previous->par == first_phys_par) {
842 tmprow = tmprow->previous;
847 // we can set the refreshing parameters now
848 status = LyXText::NEED_MORE_REFRESH;
850 refresh_row = tmprow->previous; /* the real refresh row will
851 be deleted, so I store
855 tmppar = tmprow->next->par;
858 while (tmppar != endpar) {
859 RemoveRow(tmprow->next);
861 tmppar = tmprow->next->par;
866 // remove the first one
867 tmprow2 = tmprow; /* this is because tmprow->previous
869 tmprow = tmprow->previous;
872 tmppar = first_phys_par;
876 InsertParagraph(tmppar, tmprow);
879 while (tmprow->next && tmprow->next->par == tmppar)
880 tmprow = tmprow->next;
881 tmppar = tmppar->Next();
883 } while (tmppar != endpar);
885 // this is because of layout changes
887 refresh_y -= refresh_row->height;
888 SetHeightOfRow(refresh_row);
890 refresh_row = firstrow;
892 SetHeightOfRow(refresh_row);
895 if (tmprow && tmprow->next)
896 SetHeightOfRow(tmprow->next);
900 int LyXText::FullRebreak()
902 if (need_break_row) {
903 BreakAgain(need_break_row);
911 /* important for the screen */
914 /* the cursor set functions have a special mechanism. When they
915 * realize, that you left an empty paragraph, they will delete it.
916 * They also delet the corresponding row */
918 // need the selection cursor:
919 void LyXText::SetSelection()
922 last_sel_cursor = sel_cursor;
923 sel_start_cursor = sel_cursor;
924 sel_end_cursor = sel_cursor;
929 // first the toggling area
930 if (cursor.y < last_sel_cursor.y ||
931 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
932 toggle_end_cursor = last_sel_cursor;
933 toggle_cursor = cursor;
936 toggle_end_cursor = cursor;
937 toggle_cursor = last_sel_cursor;
940 last_sel_cursor = cursor;
942 // and now the whole selection
944 if (sel_cursor.par == cursor.par)
945 if (sel_cursor.pos < cursor.pos) {
946 sel_end_cursor = cursor;
947 sel_start_cursor = sel_cursor;
949 sel_end_cursor = sel_cursor;
950 sel_start_cursor = cursor;
952 else if (sel_cursor.y < cursor.y ||
953 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
954 sel_end_cursor = cursor;
955 sel_start_cursor = sel_cursor;
958 sel_end_cursor = sel_cursor;
959 sel_start_cursor = cursor;
962 // a selection with no contents is not a selection
963 if (sel_start_cursor.x == sel_end_cursor.x &&
964 sel_start_cursor.y == sel_end_cursor.y)
969 void LyXText::ClearSelection() const
976 void LyXText::CursorHome() const
978 SetCursor(cursor.par, cursor.row->pos);
982 void LyXText::CursorEnd() const
984 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
985 SetCursor(cursor.par, RowLast(cursor.row) + 1);
987 if (cursor.par->Last() &&
988 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
989 || cursor.par->IsNewline(RowLast(cursor.row))))
990 SetCursor(cursor.par, RowLast(cursor.row));
992 SetCursor(cursor.par, RowLast(cursor.row) + 1);
994 if (cursor.par->table) {
995 int cell = NumberOfCell(cursor.par, cursor.pos);
996 if (cursor.par->table->RowHasContRow(cell) &&
997 cursor.par->table->CellHasContRow(cell)<0) {
998 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
999 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1001 if (cursor.par->Last() &&
1002 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1003 || cursor.par->IsNewline(RowLast(cursor.row))))
1004 SetCursor(cursor.par, RowLast(cursor.row));
1006 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1013 void LyXText::CursorTop() const
1015 while (cursor.par->Previous())
1016 cursor.par = cursor.par->Previous();
1017 SetCursor(cursor.par, 0);
1021 void LyXText::CursorBottom() const
1023 while (cursor.par->Next())
1024 cursor.par = cursor.par->Next();
1025 SetCursor(cursor.par, cursor.par->Last());
1029 /* returns a pointer to the row near the specified y-coordinate
1030 * (relative to the whole text). y is set to the real beginning
1032 Row * LyXText::GetRowNearY(long & y) const
1038 tmprow = currentrow;
1039 tmpy = currentrow_y;
1046 while (tmprow->next && tmpy + tmprow->height <= y) {
1047 tmpy += tmprow->height;
1048 tmprow = tmprow->next;
1051 while (tmprow->previous && tmpy > y) {
1052 tmprow = tmprow->previous;
1053 tmpy -= tmprow->height;
1056 currentrow = tmprow;
1057 currentrow_y = tmpy;
1059 y = tmpy; // return the real y
1064 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1066 // If the mask is completely neutral, tell user
1067 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1068 // Could only happen with user style
1069 owner_->owner()->getMiniBuffer()
1070 ->Set(_("No font change defined. Use Character under"
1071 " the Layout menu to define font change."));
1075 // Try implicit word selection
1076 LyXCursor resetCursor = cursor;
1077 int implicitSelection = SelectWordWhenUnderCursor();
1080 SetFont(font, toggleall);
1082 /* Implicit selections are cleared afterwards and cursor is set to the
1083 original position. */
1084 if (implicitSelection) {
1086 cursor = resetCursor;
1087 SetCursor( cursor.par, cursor.pos );
1088 sel_cursor = cursor;
1093 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1095 if (textclasslist.Style(parameters->textclass,
1096 par->GetLayout()).labeltype != LABEL_MANUAL)
1099 return par->BeginningOfMainBody();
1103 /* if there is a selection, reset every environment you can find
1104 * in the selection, otherwise just the environment you are in */
1105 void LyXText::MeltFootnoteEnvironment()
1107 LyXParagraph * tmppar, * firsttmppar;
1111 /* is is only allowed, if the cursor is IN an open footnote.
1112 * Otherwise it is too dangerous */
1113 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1116 SetUndo(Undo::FINISH,
1117 cursor.par->PreviousBeforeFootnote()->previous,
1118 cursor.par->NextAfterFootnote()->next);
1120 /* ok, move to the beginning of the footnote. */
1121 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1122 cursor.par = cursor.par->Previous();
1124 SetCursor(cursor.par, cursor.par->Last());
1125 /* this is just faster than using CursorLeft(); */
1127 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1128 tmppar = firsttmppar;
1129 /* tmppar is now the paragraph right before the footnote */
1131 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1134 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1135 tmppar = tmppar->next; /* I use next instead of Next(),
1136 * because there cannot be any
1137 * footnotes in a footnote
1139 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1141 /* remember the captions and empty paragraphs */
1142 if ((textclasslist.Style(parameters->textclass,
1143 tmppar->GetLayout())
1144 .labeltype == LABEL_SENSITIVE)
1146 tmppar->SetLayout(0);
1149 // now we will paste the ex-footnote, if the layouts allow it
1150 // first restore the layout of the paragraph right behind
1153 tmppar->next->MakeSameLayout(cursor.par);
1156 if ((!tmppar->GetLayout() && !tmppar->table)
1158 && (!tmppar->Next()->Last()
1159 || tmppar->Next()->HasSameLayout(tmppar)))) {
1160 if (tmppar->Next()->Last()
1161 && tmppar->Next()->IsLineSeparator(0))
1162 tmppar->Next()->Erase(0);
1163 tmppar->PasteParagraph();
1166 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1167 * by the pasting of the beginning */
1169 /* then the beginning */
1170 /* if there is no space between the text and the footnote, so we insert
1172 * (only if the previous par and the footnotepar are not empty!) */
1173 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1174 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1175 if (firsttmppar->text.size()
1176 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1177 && first_footnote_par_is_not_empty) {
1178 firsttmppar->next->InsertChar(0, ' ');
1180 firsttmppar->PasteParagraph();
1183 /* now redo the paragaphs */
1184 RedoParagraphs(cursor, tmppar);
1186 SetCursor(cursor.par, cursor.pos);
1188 /* sometimes it can happen, that there is a counter change */
1189 Row * row = cursor.row;
1190 while (row->next && row->par != tmppar && row->next->par != tmppar)
1192 UpdateCounters(row);
1199 /* the DTP switches for paragraphs. LyX will store them in the
1200 * first physicla paragraph. When a paragraph is broken, the top settings
1201 * rest, the bottom settings are given to the new one. So I can make shure,
1202 * they do not duplicate themself and you cannnot make dirty things with
1205 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1206 bool pagebreak_top, bool pagebreak_bottom,
1207 VSpace const & space_top,
1208 VSpace const & space_bottom,
1210 string labelwidthstring,
1213 LyXCursor tmpcursor = cursor;
1215 sel_start_cursor = cursor;
1216 sel_end_cursor = cursor;
1219 // make sure that the depth behind the selection are restored, too
1220 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1221 LyXParagraph * undoendpar = endpar;
1223 if (endpar && endpar->GetDepth()) {
1224 while (endpar && endpar->GetDepth()) {
1225 endpar = endpar->LastPhysicalPar()->Next();
1226 undoendpar = endpar;
1230 endpar = endpar->Next(); // because of parindents etc.
1235 .par->ParFromPos(sel_start_cursor.pos)->previous,
1239 LyXParagraph * tmppar = sel_end_cursor.par;
1240 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1241 SetCursor(tmppar->FirstPhysicalPar(), 0);
1242 status = LyXText::NEED_MORE_REFRESH;
1243 refresh_row = cursor.row;
1244 refresh_y = cursor.y - cursor.row->baseline;
1245 if (cursor.par->footnoteflag ==
1246 sel_start_cursor.par->footnoteflag) {
1247 cursor.par->line_top = line_top;
1248 cursor.par->line_bottom = line_bottom;
1249 cursor.par->pagebreak_top = pagebreak_top;
1250 cursor.par->pagebreak_bottom = pagebreak_bottom;
1251 cursor.par->added_space_top = space_top;
1252 cursor.par->added_space_bottom = space_bottom;
1253 // does the layout allow the new alignment?
1254 if (align == LYX_ALIGN_LAYOUT)
1255 align = textclasslist
1256 .Style(parameters->textclass,
1257 cursor.par->GetLayout()).align;
1258 if (align & textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).alignpossible) {
1261 if (align == textclasslist
1262 .Style(parameters->textclass,
1263 cursor.par->GetLayout()).align)
1264 cursor.par->align = LYX_ALIGN_LAYOUT;
1266 cursor.par->align = align;
1268 cursor.par->SetLabelWidthString(labelwidthstring);
1269 cursor.par->noindent = noindent;
1272 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1275 RedoParagraphs(sel_start_cursor, endpar);
1278 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1279 sel_cursor = cursor;
1280 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1282 SetCursor(tmpcursor.par, tmpcursor.pos);
1286 void LyXText::SetParagraphExtraOpt(int type,
1288 char const * widthp,
1289 int alignment, bool hfill,
1290 bool start_minipage)
1292 LyXCursor tmpcursor = cursor;
1293 LyXParagraph * tmppar;
1295 sel_start_cursor = cursor;
1296 sel_end_cursor = cursor;
1299 // make sure that the depth behind the selection are restored, too
1300 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1301 LyXParagraph * undoendpar = endpar;
1303 if (endpar && endpar->GetDepth()) {
1304 while (endpar && endpar->GetDepth()) {
1305 endpar = endpar->LastPhysicalPar()->Next();
1306 undoendpar = endpar;
1310 endpar = endpar->Next(); // because of parindents etc.
1315 .par->ParFromPos(sel_start_cursor.pos)->previous,
1318 tmppar = sel_end_cursor.par;
1319 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1320 SetCursor(tmppar->FirstPhysicalPar(), 0);
1321 status = LyXText::NEED_MORE_REFRESH;
1322 refresh_row = cursor.row;
1323 refresh_y = cursor.y - cursor.row->baseline;
1324 if (cursor.par->footnoteflag ==
1325 sel_start_cursor.par->footnoteflag) {
1326 if (type == LyXParagraph::PEXTRA_NONE) {
1327 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1328 cursor.par->UnsetPExtraType();
1329 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1332 cursor.par->SetPExtraType(type, width, widthp);
1333 cursor.par->pextra_hfill = hfill;
1334 cursor.par->pextra_start_minipage = start_minipage;
1335 cursor.par->pextra_alignment = alignment;
1338 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1340 RedoParagraphs(sel_start_cursor, endpar);
1342 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1343 sel_cursor = cursor;
1344 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1346 SetCursor(tmpcursor.par, tmpcursor.pos);
1351 string alphaCounter(int n) {
1355 char result[2] = { 'A' + n - 1, 0 };
1362 // set the counter of a paragraph. This includes the labels
1363 void LyXText::SetCounter(LyXParagraph * par) const
1365 // this is only relevant for the beginning of paragraph
1366 par = par->FirstPhysicalPar();
1368 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1371 LyXTextClass const & textclass =
1372 textclasslist.TextClass(parameters->textclass);
1374 /* copy the prev-counters to this one, unless this is the start of a
1375 footnote or of a bibliography or the very first paragraph */
1377 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1378 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1379 && par->footnotekind == LyXParagraph::FOOTNOTE)
1380 && !(textclasslist.Style(parameters->textclass,
1381 par->Previous()->GetLayout()
1382 ).labeltype != LABEL_BIBLIO
1383 && layout.labeltype == LABEL_BIBLIO)) {
1384 for (int i = 0; i < 10; ++i) {
1385 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1387 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1388 if (!par->appendix && par->start_of_appendix){
1389 par->appendix = true;
1390 for (int i = 0; i < 10; ++i) {
1391 par->setCounter(i, 0);
1394 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1395 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1398 for (int i = 0; i < 10; ++i) {
1399 par->setCounter(i, 0);
1401 par->appendix = par->start_of_appendix;
1406 // if this is an open marginnote and this is the first
1407 // entry in the marginnote and the enclosing
1408 // environment is an enum/item then correct for the
1409 // LaTeX behaviour (ARRae)
1410 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1411 && par->footnotekind == LyXParagraph::MARGIN
1413 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1414 && (par->PreviousBeforeFootnote()
1415 && textclasslist.Style(parameters->textclass,
1416 par->PreviousBeforeFootnote()->GetLayout()
1417 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1418 // Any itemize or enumerate environment in a marginnote
1419 // that is embedded in an itemize or enumerate
1420 // paragraph is seen by LaTeX as being at a deeper
1421 // level within that enclosing itemization/enumeration
1422 // even if there is a "standard" layout at the start of
1428 /* Maybe we have to increment the enumeration depth.
1429 * BUT, enumeration in a footnote is considered in isolation from its
1430 * surrounding paragraph so don't increment if this is the
1431 * first line of the footnote
1432 * AND, bibliographies can't have their depth changed ie. they
1433 * are always of depth 0
1436 && par->Previous()->GetDepth() < par->GetDepth()
1437 && textclasslist.Style(parameters->textclass,
1438 par->Previous()->GetLayout()
1439 ).labeltype == LABEL_COUNTER_ENUMI
1440 && par->enumdepth < 3
1441 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1442 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1443 && par->footnotekind == LyXParagraph::FOOTNOTE)
1444 && layout.labeltype != LABEL_BIBLIO) {
1448 /* Maybe we have to decrement the enumeration depth, see note above */
1450 && par->Previous()->GetDepth() > par->GetDepth()
1451 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1452 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1453 && par->footnotekind == LyXParagraph::FOOTNOTE)
1454 && layout.labeltype != LABEL_BIBLIO) {
1455 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1456 par->setCounter(6 + par->enumdepth,
1457 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1458 /* reset the counters.
1459 * A depth change is like a breaking layout
1461 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1462 par->setCounter(i, 0);
1465 if (!par->labelstring.empty()) {
1466 par->labelstring.clear();
1469 if (layout.margintype == MARGIN_MANUAL) {
1470 if (par->labelwidthstring.empty()) {
1471 par->SetLabelWidthString(layout.labelstring());
1474 par->SetLabelWidthString(string());
1477 /* is it a layout that has an automatic label ? */
1478 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1480 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1481 if (i >= 0 && i<= parameters->secnumdepth) {
1482 par->incCounter(i); // increment the counter
1484 // Is there a label? Useful for Chapter layout
1485 if (!par->appendix){
1486 if (!layout.labelstring().empty())
1487 par->labelstring = layout.labelstring();
1489 par->labelstring.clear();
1491 if (!layout.labelstring_appendix().empty())
1492 par->labelstring = layout.labelstring_appendix();
1494 par->labelstring.clear();
1502 if (!par->appendix) {
1503 switch (2 * LABEL_FIRST_COUNTER -
1504 textclass.maxcounter() + i) {
1505 case LABEL_COUNTER_CHAPTER:
1506 s << par->getCounter(i);
1508 case LABEL_COUNTER_SECTION:
1509 s << par->getCounter(i - 1) << '.'
1510 << par->getCounter(i);
1512 case LABEL_COUNTER_SUBSECTION:
1513 s << par->getCounter(i - 2) << '.'
1514 << par->getCounter(i - 1) << '.'
1515 << par->getCounter(i);
1517 case LABEL_COUNTER_SUBSUBSECTION:
1518 s << par->getCounter(i - 3) << '.'
1519 << par->getCounter(i - 2) << '.'
1520 << par->getCounter(i - 1) << '.'
1521 << par->getCounter(i);
1524 case LABEL_COUNTER_PARAGRAPH:
1525 s << par->getCounter(i - 4) << '.'
1526 << par->getCounter(i - 3) << '.'
1527 << par->getCounter(i - 2) << '.'
1528 << par->getCounter(i - 1) << '.'
1529 << par->getCounter(i);
1531 case LABEL_COUNTER_SUBPARAGRAPH:
1532 s << par->getCounter(i - 5) << '.'
1533 << par->getCounter(i - 4) << '.'
1534 << par->getCounter(i - 3) << '.'
1535 << par->getCounter(i - 2) << '.'
1536 << par->getCounter(i - 1) << '.'
1537 << par->getCounter(i);
1541 s << par->getCounter(i) << '.';
1544 } else { // appendix
1545 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1546 case LABEL_COUNTER_CHAPTER:
1547 s << alphaCounter(par->getCounter(i));
1549 case LABEL_COUNTER_SECTION:
1550 s << alphaCounter(par->getCounter(i - 1)) << '.'
1551 << par->getCounter(i);
1554 case LABEL_COUNTER_SUBSECTION:
1555 s << alphaCounter(par->getCounter(i - 2)) << '.'
1556 << par->getCounter(i-1) << '.'
1557 << par->getCounter(i);
1560 case LABEL_COUNTER_SUBSUBSECTION:
1561 s << alphaCounter(par->getCounter(i-3)) << '.'
1562 << par->getCounter(i-2) << '.'
1563 << par->getCounter(i-1) << '.'
1564 << par->getCounter(i);
1567 case LABEL_COUNTER_PARAGRAPH:
1568 s << alphaCounter(par->getCounter(i-4)) << '.'
1569 << par->getCounter(i-3) << '.'
1570 << par->getCounter(i-2) << '.'
1571 << par->getCounter(i-1) << '.'
1572 << par->getCounter(i);
1575 case LABEL_COUNTER_SUBPARAGRAPH:
1576 s << alphaCounter(par->getCounter(i-5)) << '.'
1577 << par->getCounter(i-4) << '.'
1578 << par->getCounter(i-3) << '.'
1579 << par->getCounter(i-2) << '.'
1580 << par->getCounter(i-1) << '.'
1581 << par->getCounter(i);
1585 // Can this ever be reached? And in the
1586 // case it is, how can this be correct?
1588 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1594 par->labelstring += s.str().c_str();
1595 // We really want to remove the c_str as soon as
1599 char * tmps = s.str();
1600 par->labelstring += tmps;
1604 for (i++; i < 10; ++i) {
1605 // reset the following counters
1606 par->setCounter(i, 0);
1608 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1609 for (i++; i < 10; ++i) {
1610 // reset the following counters
1611 par->setCounter(i, 0);
1613 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1614 par->incCounter(i + par->enumdepth);
1615 int number = par->getCounter(i + par->enumdepth);
1617 static const char *roman[20] = {
1618 "i", "ii", "iii", "iv", "v",
1619 "vi", "vii", "viii", "ix", "x",
1620 "xi", "xii", "xiii", "xiv", "xv",
1621 "xvi", "xvii", "xviii", "xix", "xx"
1623 static const char hebrew[22] = {
1624 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1625 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1626 '÷', 'ø', 'ù', 'ú'
1633 switch (par->enumdepth) {
1635 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1637 << static_cast<unsigned char>
1638 (((number - 1) % 26) + 'a')
1643 << static_cast<unsigned char>
1644 (hebrew[(number - 1) % 22])
1648 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1649 s << roman[(number - 1) % 20] << '.';
1651 s << '.' << roman[(number - 1) % 20];
1654 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1655 s << static_cast<unsigned char>
1656 (((number - 1) % 26) + 'A')
1660 << static_cast<unsigned char>
1661 (((number - 1) % 26) + 'A');
1664 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1671 par->labelstring = s.str().c_str();
1672 // we really want to get rid of that c_str()
1675 char * tmps = s.str();
1676 par->labelstring = tmps;
1680 for (i += par->enumdepth + 1; i < 10; ++i)
1681 par->setCounter(i, 0); /* reset the following counters */
1684 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1685 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1687 int number = par->getCounter(i);
1689 par->bibkey = new InsetBibKey();
1690 par->bibkey->setCounter(number);
1691 par->labelstring = layout.labelstring();
1693 // In biblio should't be following counters but...
1695 string s = layout.labelstring();
1697 // the caption hack:
1699 if (layout.labeltype == LABEL_SENSITIVE) {
1700 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1701 && (par->footnotekind == LyXParagraph::FIG
1702 || par->footnotekind == LyXParagraph::WIDE_FIG))
1703 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1707 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1708 && (par->footnotekind == LyXParagraph::TAB
1709 || par->footnotekind == LyXParagraph::WIDE_TAB))
1710 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1714 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1715 && par->footnotekind == LyXParagraph::ALGORITHM)
1716 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1719 s = ":Ãúéøåâìà ";
1721 /* par->SetLayout(0);
1722 s = layout->labelstring; */
1723 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1726 s = " :úåòîùî øñç";
1730 par->labelstring = s;
1732 /* reset the enumeration counter. They are always resetted
1733 * when there is any other layout between */
1734 for (int i = 6 + par->enumdepth; i < 10; ++i)
1735 par->setCounter(i, 0);
1740 /* Updates all counters BEHIND the row. Changed paragraphs
1741 * with a dynamic left margin will be rebroken. */
1742 void LyXText::UpdateCounters(Row * row) const
1751 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1752 par = row->par->LastPhysicalPar()->Next();
1754 par = row->par->next;
1759 while (row->par != par)
1764 /* now check for the headline layouts. remember that they
1765 * have a dynamic left margin */
1767 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1768 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1771 /* Rebreak the paragraph */
1772 RemoveParagraph(row);
1773 AppendParagraph(row);
1775 /* think about the damned open footnotes! */
1776 while (par->Next() &&
1777 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1778 || par->Next()->IsDummy())){
1780 if (par->IsDummy()) {
1781 while (row->par != par)
1783 RemoveParagraph(row);
1784 AppendParagraph(row);
1789 par = par->LastPhysicalPar()->Next();
1795 /* insets an inset. */
1796 void LyXText::InsertInset(Inset *inset)
1798 SetUndo(Undo::INSERT,
1799 cursor.par->ParFromPos(cursor.pos)->previous,
1800 cursor.par->ParFromPos(cursor.pos)->next);
1801 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1802 cursor.par->InsertInset(cursor.pos, inset);
1803 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1804 * The character will not be inserted a
1809 // this is for the simple cut and paste mechanism
1810 static LyXParagraph * simple_cut_buffer = 0;
1811 static char simple_cut_buffer_textclass = 0;
1813 void DeleteSimpleCutBuffer()
1815 if (!simple_cut_buffer)
1817 LyXParagraph * tmppar;
1819 while (simple_cut_buffer) {
1820 tmppar = simple_cut_buffer;
1821 simple_cut_buffer = simple_cut_buffer->next;
1824 simple_cut_buffer = 0;
1828 void LyXText::copyEnvironmentType()
1830 copylayouttype = cursor.par->GetLayout();
1834 void LyXText::pasteEnvironmentType()
1836 SetLayout(copylayouttype);
1840 void LyXText::CutSelection(bool doclear)
1842 // This doesn't make sense, if there is no selection
1846 // OK, we have a selection. This is always between sel_start_cursor
1847 // and sel_end cursor
1848 LyXParagraph * tmppar;
1850 // Check whether there are half footnotes in the selection
1851 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1852 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1853 tmppar = sel_start_cursor.par;
1854 while (tmppar != sel_end_cursor.par){
1855 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1856 WriteAlert(_("Impossible operation"),
1857 _("Don't know what to do with half floats."),
1861 tmppar = tmppar->Next();
1865 /* table stuff -- begin */
1866 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1867 if ( sel_start_cursor.par != sel_end_cursor.par) {
1868 WriteAlert(_("Impossible operation"),
1869 _("Don't know what to do with half tables."),
1873 sel_start_cursor.par->table->Reinit();
1875 /* table stuff -- end */
1877 // make sure that the depth behind the selection are restored, too
1878 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1879 LyXParagraph * undoendpar = endpar;
1881 if (endpar && endpar->GetDepth()) {
1882 while (endpar && endpar->GetDepth()) {
1883 endpar = endpar->LastPhysicalPar()->Next();
1884 undoendpar = endpar;
1886 } else if (endpar) {
1887 endpar = endpar->Next(); // because of parindents etc.
1890 SetUndo(Undo::DELETE,
1892 .par->ParFromPos(sel_start_cursor.pos)->previous,
1895 // clear the simple_cut_buffer
1896 DeleteSimpleCutBuffer();
1898 // set the textclass
1899 simple_cut_buffer_textclass = parameters->textclass;
1901 #ifdef WITH_WARNINGS
1902 #warning Asger: Make cut more intelligent here.
1905 White paper for "intelligent" cutting:
1907 Example: "This is our text."
1908 Using " our " as selection, cutting will give "This istext.".
1909 Using "our" as selection, cutting will give "This is text.".
1910 Using " our" as selection, cutting will give "This is text.".
1911 Using "our " as selection, cutting will give "This is text.".
1913 All those four selections will (however) paste identically:
1914 Pasting with the cursor right after the "is" will give the
1915 original text with all four selections.
1917 The rationale is to be intelligent such that words are copied,
1918 cut and pasted in a functional manner.
1920 This is not implemented yet. (Asger)
1922 The changes below sees to do a lot of what you want. However
1923 I have not verified that all cases work as they should:
1925 - cut in multiple row
1927 - cut across footnotes and paragraph
1928 My simplistic tests show that the idea are basically sound but
1929 there are some items to fix up...we only need to find them
1932 As do redo Asger's example above (with | beeing the cursor in the
1933 result after cutting.):
1935 Example: "This is our text."
1936 Using " our " as selection, cutting will give "This is|text.".
1937 Using "our" as selection, cutting will give "This is | text.".
1938 Using " our" as selection, cutting will give "This is| text.".
1939 Using "our " as selection, cutting will give "This is |text.".
1944 #ifndef FIX_DOUBLE_SPACE
1945 bool space_wrapped =
1946 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1947 if (sel_end_cursor.pos > 0
1948 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1949 // please break before a space at the end
1950 sel_end_cursor.pos--;
1951 space_wrapped = true;
1953 // cut behind a space if there is one
1954 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1955 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1956 && (sel_start_cursor.par != sel_end_cursor.par
1957 || sel_start_cursor.pos < sel_end_cursor.pos))
1958 sel_start_cursor.pos++;
1960 // there are two cases: cut only within one paragraph or
1961 // more than one paragraph
1963 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1964 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1965 // only within one paragraph
1966 simple_cut_buffer = new LyXParagraph;
1967 LyXParagraph::size_type i =
1968 sel_start_cursor.pos;
1969 for (; i < sel_end_cursor.pos; ++i) {
1970 /* table stuff -- begin */
1971 if (sel_start_cursor.par->table
1972 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1973 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1974 sel_start_cursor.pos++;
1976 /* table stuff -- end */
1977 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1978 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1980 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1982 #ifndef FIX_DOUBLE_SPACE
1983 // check for double spaces
1984 if (sel_start_cursor.pos &&
1985 sel_start_cursor.par->Last() > sel_start_cursor.pos
1986 && sel_start_cursor.par
1987 ->IsLineSeparator(sel_start_cursor.pos - 1)
1988 && sel_start_cursor.par
1989 ->IsLineSeparator(sel_start_cursor.pos)) {
1990 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1993 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1996 endpar = sel_end_cursor.par->Next();
1998 // cut more than one paragraph
2001 ->BreakParagraphConservative(sel_end_cursor.pos);
2002 #ifndef FIX_DOUBLE_SPACE
2003 // insert a space at the end if there was one
2006 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2008 sel_end_cursor.par = sel_end_cursor.par->Next();
2009 sel_end_cursor.pos = 0;
2011 cursor = sel_end_cursor;
2013 #ifndef FIX_DOUBLE_SPACE
2014 // please break behind a space, if there is one.
2015 // The space should be copied too
2016 if (sel_start_cursor.par
2017 ->IsLineSeparator(sel_start_cursor.pos))
2018 sel_start_cursor.pos++;
2020 sel_start_cursor.par
2021 ->BreakParagraphConservative(sel_start_cursor.pos);
2022 #ifndef FIX_DOUBLE_SPACE
2023 if (!sel_start_cursor.pos
2024 || sel_start_cursor.par
2025 ->IsLineSeparator(sel_start_cursor.pos - 1)
2026 || sel_start_cursor.par
2027 ->IsNewline(sel_start_cursor.pos - 1)) {
2028 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2031 // store the endparagraph for redoing later
2032 endpar = sel_end_cursor.par->Next(); /* needed because
2037 // store the selection
2038 simple_cut_buffer = sel_start_cursor.par
2039 ->ParFromPos(sel_start_cursor.pos)->next;
2040 simple_cut_buffer->previous = 0;
2041 sel_end_cursor.par->previous->next = 0;
2043 // cut the selection
2044 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2045 = sel_end_cursor.par;
2047 sel_end_cursor.par->previous
2048 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2050 // care about footnotes
2051 if (simple_cut_buffer->footnoteflag) {
2052 LyXParagraph * tmppar = simple_cut_buffer;
2054 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2055 tmppar = tmppar->next;
2059 // the cut selection should begin with standard layout
2060 simple_cut_buffer->Clear();
2062 // paste the paragraphs again, if possible
2064 sel_start_cursor.par->Next()->ClearParagraph();
2065 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2067 !sel_start_cursor.par->Next()->Last())
2068 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2070 #ifndef FIX_DOUBLE_SPACE
2071 // maybe a forgotten blank
2072 if (sel_start_cursor.pos
2073 && sel_start_cursor.par
2074 ->IsLineSeparator(sel_start_cursor.pos)
2075 && sel_start_cursor.par
2076 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2077 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2082 // sometimes necessary
2084 sel_start_cursor.par->ClearParagraph();
2086 RedoParagraphs(sel_start_cursor, endpar);
2089 cursor = sel_start_cursor;
2090 SetCursor(cursor.par, cursor.pos);
2091 sel_cursor = cursor;
2092 UpdateCounters(cursor.row);
2096 void LyXText::CopySelection()
2098 // this doesnt make sense, if there is no selection
2102 // ok we have a selection. This is always between sel_start_cursor
2103 // and sel_end cursor
2104 LyXParagraph * tmppar;
2106 /* check wether there are half footnotes in the selection */
2107 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2108 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2109 tmppar = sel_start_cursor.par;
2110 while (tmppar != sel_end_cursor.par) {
2111 if (tmppar->footnoteflag !=
2112 sel_end_cursor.par->footnoteflag) {
2113 WriteAlert(_("Impossible operation"),
2114 _("Don't know what to do"
2115 " with half floats."),
2119 tmppar = tmppar->Next();
2123 /* table stuff -- begin */
2124 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2125 if ( sel_start_cursor.par != sel_end_cursor.par){
2126 WriteAlert(_("Impossible operation"),
2127 _("Don't know what to do with half tables."),
2132 /* table stuff -- end */
2134 // delete the simple_cut_buffer
2135 DeleteSimpleCutBuffer();
2137 // set the textclass
2138 simple_cut_buffer_textclass = parameters->textclass;
2140 #ifdef FIX_DOUBLE_SPACE
2141 // copy behind a space if there is one
2142 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2143 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2144 && (sel_start_cursor.par != sel_end_cursor.par
2145 || sel_start_cursor.pos < sel_end_cursor.pos))
2146 sel_start_cursor.pos++;
2148 // there are two cases: copy only within one paragraph
2149 // or more than one paragraph
2150 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2151 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2152 // only within one paragraph
2153 simple_cut_buffer = new LyXParagraph;
2154 LyXParagraph::size_type i = 0;
2155 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2156 sel_start_cursor.par->CopyIntoMinibuffer(i);
2157 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2160 // copy more than one paragraph
2161 // clone the paragraphs within the selection
2163 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2164 simple_cut_buffer = tmppar->Clone();
2165 LyXParagraph *tmppar2 = simple_cut_buffer;
2167 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2169 tmppar = tmppar->next;
2170 tmppar2->next = tmppar->Clone();
2171 tmppar2->next->previous = tmppar2;
2172 tmppar2 = tmppar2->next;
2176 // care about footnotes
2177 if (simple_cut_buffer->footnoteflag) {
2178 tmppar = simple_cut_buffer;
2180 tmppar->footnoteflag =
2181 LyXParagraph::NO_FOOTNOTE;
2182 tmppar = tmppar->next;
2186 // the simple_cut_buffer paragraph is too big
2187 LyXParagraph::size_type tmpi2 =
2188 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2189 for (; tmpi2; --tmpi2)
2190 simple_cut_buffer->Erase(0);
2192 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2194 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2195 while (tmppar2->size() > tmpi2) {
2196 tmppar2->Erase(tmppar2->text.size() - 1);
2202 void LyXText::PasteSelection()
2204 // this does not make sense, if there is nothing to paste
2205 if (!simple_cut_buffer)
2208 LyXParagraph * tmppar;
2209 LyXParagraph * endpar;
2211 LyXCursor tmpcursor;
2213 // be carefull with footnotes in footnotes
2214 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2216 // check whether the cut_buffer includes a footnote
2217 tmppar = simple_cut_buffer;
2219 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2220 tmppar = tmppar->next;
2223 WriteAlert(_("Impossible operation"),
2224 _("Can't paste float into float!"),
2230 /* table stuff -- begin */
2231 if (cursor.par->table) {
2232 if (simple_cut_buffer->next) {
2233 WriteAlert(_("Impossible operation"),
2234 _("Table cell cannot include more than one paragraph!"),
2239 /* table stuff -- end */
2241 SetUndo(Undo::INSERT,
2242 cursor.par->ParFromPos(cursor.pos)->previous,
2243 cursor.par->ParFromPos(cursor.pos)->next);
2247 // There are two cases: cutbuffer only one paragraph or many
2248 if (!simple_cut_buffer->next) {
2249 // only within a paragraph
2251 #ifndef FIX_DOUBLE_SPACE
2252 // please break behind a space, if there is one
2253 while (tmpcursor.par->Last() > tmpcursor.pos
2254 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2257 tmppar = simple_cut_buffer->Clone();
2258 /* table stuff -- begin */
2259 bool table_too_small = false;
2260 if (tmpcursor.par->table) {
2261 while (simple_cut_buffer->text.size()
2262 && !table_too_small) {
2263 if (simple_cut_buffer->IsNewline(0)){
2264 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2266 simple_cut_buffer->Erase(0);
2267 if (tmpcursor.pos < tmpcursor.par->Last())
2270 table_too_small = true;
2272 #ifdef FIX_DOUBLE_SPACE
2273 // This is an attempt to fix the
2274 // "never insert a space at the
2275 // beginning of a paragraph" problem.
2276 if (tmpcursor.pos == 0
2277 && simple_cut_buffer->IsLineSeparator(0)) {
2278 simple_cut_buffer->Erase(0);
2280 simple_cut_buffer->CutIntoMinibuffer(0);
2281 simple_cut_buffer->Erase(0);
2282 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2286 simple_cut_buffer->CutIntoMinibuffer(0);
2287 simple_cut_buffer->Erase(0);
2288 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2294 /* table stuff -- end */
2295 // Some provisions should be done here for checking
2296 // if we are inserting at the beginning of a
2297 // paragraph. If there are a space at the beginning
2298 // of the text to insert and we are inserting at
2299 // the beginning of the paragraph the space should
2301 while (simple_cut_buffer->text.size()) {
2302 #ifdef FIX_DOUBLE_SPACE
2303 // This is an attempt to fix the
2304 // "never insert a space at the
2305 // beginning of a paragraph" problem.
2306 if (tmpcursor.pos == 0
2307 && simple_cut_buffer->IsLineSeparator(0)) {
2308 simple_cut_buffer->Erase(0);
2310 simple_cut_buffer->CutIntoMinibuffer(0);
2311 simple_cut_buffer->Erase(0);
2312 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2316 simple_cut_buffer->CutIntoMinibuffer(0);
2317 simple_cut_buffer->Erase(0);
2318 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2323 delete simple_cut_buffer;
2324 simple_cut_buffer = tmppar;
2325 endpar = tmpcursor.par->Next();
2329 // make a copy of the simple cut_buffer
2330 tmppar = simple_cut_buffer;
2331 LyXParagraph * simple_cut_clone = tmppar->Clone();
2332 LyXParagraph * tmppar2 = simple_cut_clone;
2333 if (cursor.par->footnoteflag){
2334 tmppar->footnoteflag = cursor.par->footnoteflag;
2335 tmppar->footnotekind = cursor.par->footnotekind;
2337 while (tmppar->next) {
2338 tmppar = tmppar->next;
2339 tmppar2->next = tmppar->Clone();
2340 tmppar2->next->previous = tmppar2;
2341 tmppar2 = tmppar2->next;
2342 if (cursor.par->footnoteflag){
2343 tmppar->footnoteflag = cursor.par->footnoteflag;
2344 tmppar->footnotekind = cursor.par->footnotekind;
2348 // make sure there is no class difference
2349 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2350 parameters->textclass,
2353 // make the simple_cut_buffer exactly the same layout than
2354 // the cursor paragraph
2355 simple_cut_buffer->MakeSameLayout(cursor.par);
2357 // find the end of the buffer
2358 LyXParagraph * lastbuffer = simple_cut_buffer;
2359 while (lastbuffer->Next())
2360 lastbuffer = lastbuffer->Next();
2362 #ifndef FIX_DOUBLE_SPACE
2363 // Please break behind a space, if there is one. The space
2364 // should be copied too.
2365 if (cursor.par->Last() > cursor.pos
2366 && cursor.par->IsLineSeparator(cursor.pos))
2369 bool paste_the_end = false;
2371 // open the paragraph for inserting the simple_cut_buffer
2373 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2374 cursor.par->BreakParagraphConservative(cursor.pos);
2375 paste_the_end = true;
2378 #ifndef FIX_DOUBLE_SPACE
2379 // be careful with double spaces
2380 if ((!cursor.par->Last()
2381 || cursor.par->IsLineSeparator(cursor.pos - 1)
2382 || cursor.par->IsNewline(cursor.pos - 1))
2383 && simple_cut_buffer->text.size()
2384 && simple_cut_buffer->IsLineSeparator(0))
2385 simple_cut_buffer->Erase(0);
2387 // set the end for redoing later
2388 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2391 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2392 cursor.par->ParFromPos(cursor.pos)->next;
2393 cursor.par->ParFromPos(cursor.pos)->next->previous =
2394 lastbuffer->ParFromPos(lastbuffer->Last());
2396 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2397 simple_cut_buffer->previous =
2398 cursor.par->ParFromPos(cursor.pos);
2400 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2401 lastbuffer = cursor.par;
2403 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2405 // store the new cursor position
2406 tmpcursor.par = lastbuffer;
2407 tmpcursor.pos = lastbuffer->Last();
2409 // maybe some pasting
2410 if (lastbuffer->Next() && paste_the_end) {
2411 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2412 #ifndef FIX_DOUBLE_SPACE
2413 // be careful with double spaces
2414 if ((!lastbuffer->Last()
2415 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2416 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2417 && lastbuffer->Next()->Last()
2418 && lastbuffer->Next()->IsLineSeparator(0))
2419 lastbuffer->Next()->Erase(0);
2421 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2423 } else if (!lastbuffer->Next()->Last()) {
2424 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2425 #ifndef FIX_DOUBLE_SPACE
2426 // be careful witth double spaces
2427 if ((!lastbuffer->Last()
2428 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2429 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2430 && lastbuffer->Next()->Last()
2431 && lastbuffer->Next()->IsLineSeparator(0))
2432 lastbuffer->Next()->Erase(0);
2434 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2436 } else if (!lastbuffer->Last()) {
2437 lastbuffer->MakeSameLayout(lastbuffer->next);
2438 #ifndef FIX_DOUBLE_SPACE
2439 // be careful witth double spaces
2440 if ((!lastbuffer->Last()
2441 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2442 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2443 && lastbuffer->Next()->Last()
2444 && lastbuffer->Next()->IsLineSeparator(0))
2445 lastbuffer->Next()->Erase(0);
2447 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2450 lastbuffer->Next()->ClearParagraph();
2453 // restore the simple cut buffer
2454 simple_cut_buffer = simple_cut_clone;
2457 RedoParagraphs(cursor, endpar);
2459 SetCursor(cursor.par, cursor.pos);
2462 sel_cursor = cursor;
2463 SetCursor(tmpcursor.par, tmpcursor.pos);
2465 UpdateCounters(cursor.row);
2469 // returns a pointer to the very first LyXParagraph
2470 LyXParagraph * LyXText::FirstParagraph() const
2472 return params->paragraph;
2476 // returns true if the specified string is at the specified position
2477 bool LyXText::IsStringInText(LyXParagraph * par,
2478 LyXParagraph::size_type pos,
2479 char const * str) const
2483 while (pos + i < par->Last() && str[i] &&
2484 str[i] == par->GetChar(pos + i)) {
2494 // sets the selection over the number of characters of string, no check!!
2495 void LyXText::SetSelectionOverString(char const * string)
2497 sel_cursor = cursor;
2498 for (int i = 0; string[i]; ++i)
2504 // simple replacing. The font of the first selected character is used
2505 void LyXText::ReplaceSelectionWithString(char const * str)
2510 if (!selection) { // create a dummy selection
2511 sel_end_cursor = cursor;
2512 sel_start_cursor = cursor;
2515 // Get font setting before we cut
2516 LyXParagraph::size_type pos = sel_end_cursor.pos;
2517 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2519 // Insert the new string
2520 for (int i = 0; str[i]; ++i) {
2521 sel_end_cursor.par->InsertChar(pos, str[i]);
2522 sel_end_cursor.par->SetFont(pos, font);
2526 // Cut the selection
2533 // if the string can be found: return true and set the cursor to
2535 bool LyXText::SearchForward(char const * str) const
2537 LyXParagraph * par = cursor.par;
2538 LyXParagraph::size_type pos = cursor.pos;
2539 while (par && !IsStringInText(par, pos, str)) {
2540 if (pos < par->Last() - 1)
2548 SetCursor(par, pos);
2556 bool LyXText::SearchBackward(char const * string) const
2558 LyXParagraph * par = cursor.par;
2559 int pos = cursor.pos;
2565 // We skip empty paragraphs (Asger)
2567 par = par->Previous();
2569 pos = par->Last() - 1;
2570 } while (par && pos < 0);
2572 } while (par && !IsStringInText(par, pos, string));
2575 SetCursor(par, pos);
2582 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2584 char * str = new char[text.size() + 1];
2585 copy(text.begin(), text.end(), str);
2586 str[text.size()] = '\0';
2592 // needed to insert the selection
2593 void LyXText::InsertStringA(char const * s)
2596 LyXParagraph * par = cursor.par;
2597 LyXParagraph::size_type pos = cursor.pos;
2598 LyXParagraph::size_type a = 0;
2600 LyXParagraph * endpar = cursor.par->Next();
2605 textclasslist.Style(parameters->textclass,
2606 cursor.par->GetLayout()).isEnvironment();
2607 // only to be sure, should not be neccessary
2610 // insert the string, don't insert doublespace
2611 string::size_type i = 0;
2612 while (i < str.length()) {
2613 if (str[i] != '\n') {
2615 && i + 1 < str.length() && str[i + 1] != ' '
2616 && pos && par->GetChar(pos - 1)!= ' ') {
2617 par->InsertChar(pos,' ');
2619 } else if (par->table) {
2620 if (str[i] == '\t') {
2621 while((pos < par->size()) &&
2622 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2624 if (pos < par->size())
2626 else // no more fields to fill skip the rest
2628 } else if ((str[i] != 13) &&
2629 ((str[i] & 127) >= ' ')) {
2630 par->InsertChar(pos, str[i]);
2633 } else if (str[i] == ' ') {
2635 InsetSpecialChar * new_inset =
2636 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2637 par->InsertInset(pos, new_inset);
2639 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2642 } else if (str[i] == '\t') {
2643 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2645 InsetSpecialChar * new_inset =
2646 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2647 par->InsertInset(pos, new_inset);
2649 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2653 } else if (str[i]!= 13 &&
2654 // Ignore unprintables
2655 (str[i] & 127) >= ' ') {
2656 par->InsertChar(pos, str[i]);
2661 if (i + 1 >= str.length()) {
2665 while((pos < par->size()) &&
2666 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2669 cell = NumberOfCell(par, pos);
2670 while((pos < par->size()) &&
2671 !(par->table->IsFirstCell(cell))) {
2673 while((pos < par->size()) &&
2674 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2677 cell = NumberOfCell(par, pos);
2679 if (pos >= par->size())
2680 // no more fields to fill skip the rest
2683 if (!par->text.size()) {
2685 InsetSpecialChar * new_inset =
2686 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2687 par->InsertInset(pos, new_inset);
2689 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2693 par->BreakParagraph(pos, flag);
2701 RedoParagraphs(cursor, endpar);
2702 SetCursor(cursor.par, cursor.pos);
2703 sel_cursor = cursor;
2704 SetCursor(par, pos);
2709 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2711 char * str = new char[text.size() + 1];
2712 copy(text.begin(), text.end(), str);
2713 str[text.size()] = '\0';
2719 /* turns double-CR to single CR, others where converted into one blank and 13s
2720 * that are ignored .Double spaces are also converted into one. Spaces at
2721 * the beginning of a paragraph are forbidden. tabs are converted into one
2722 * space. then InsertStringA is called */
2723 void LyXText::InsertStringB(char const * s)
2726 LyXParagraph * par = cursor.par;
2727 string::size_type i = 1;
2728 while (i < str.length()) {
2729 if (str[i] == '\t' && !par->table)
2731 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2733 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2734 if (str[i + 1] != '\n') {
2735 if (str[i - 1] != ' ')
2740 while (i + 1 < str.length()
2741 && (str[i + 1] == ' '
2742 || str[i + 1] == '\t'
2743 || str[i + 1] == '\n'
2744 || str[i + 1] == 13)) {
2751 InsertStringA(str.c_str());
2755 bool LyXText::GotoNextError() const
2757 LyXCursor res = cursor;
2759 if (res.pos < res.par->Last() - 1) {
2763 res.par = res.par->Next();
2768 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2769 && res.par->GetInset(res.pos)->AutoDelete()));
2772 SetCursor(res.par, res.pos);
2779 bool LyXText::GotoNextNote() const
2781 LyXCursor res = cursor;
2783 if (res.pos < res.par->Last() - 1) {
2786 res.par = res.par->Next();
2791 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2792 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2795 SetCursor(res.par, res.pos);
2802 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2803 LyXTextClassList::size_type class2,
2807 if (!par || class1 == class2)
2809 par = par->FirstPhysicalPar();
2811 string name = textclasslist.NameOfLayout(class1, par->layout);
2813 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2814 textclasslist.NumberOfLayout(class2, name);
2817 } else { // layout not found
2818 // use default layout "Standard" (0)
2823 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2825 string s = "Layout had to be changed from\n"
2826 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2827 + "\nbecause of class conversion from\n"
2828 + textclasslist.NameOfClass(class1) + " to "
2829 + textclasslist.NameOfClass(class2);
2830 InsetError * new_inset = new InsetError(s);
2831 par->InsertChar(0, LyXParagraph::META_INSET);
2832 par->InsertInset(0, new_inset);
2841 void LyXText::CheckParagraph(LyXParagraph * par,
2842 LyXParagraph::size_type pos)
2845 LyXCursor tmpcursor;
2847 /* table stuff -- begin*/
2850 CheckParagraphInTable(par, pos);
2853 /* table stuff -- end*/
2856 LyXParagraph::size_type z;
2857 Row * row = GetRow(par, pos, y);
2859 // is there a break one row above
2860 if (row->previous && row->previous->par == row->par) {
2861 z = NextBreakPoint(row->previous, paperwidth);
2862 if ( z >= row->pos) {
2863 // set the dimensions of the row above
2864 y -= row->previous->height;
2866 refresh_row = row->previous;
2867 status = LyXText::NEED_MORE_REFRESH;
2869 BreakAgain(row->previous);
2871 // set the cursor again. Otherwise
2872 // dangling pointers are possible
2873 SetCursor(cursor.par, cursor.pos);
2874 sel_cursor = cursor;
2879 int tmpheight = row->height;
2880 LyXParagraph::size_type tmplast = RowLast(row);
2885 if (row->height == tmpheight && RowLast(row) == tmplast)
2886 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2888 status = LyXText::NEED_MORE_REFRESH;
2890 // check the special right address boxes
2891 if (textclasslist.Style(parameters->textclass,
2892 par->GetLayout()).margintype
2893 == MARGIN_RIGHT_ADDRESS_BOX) {
2894 tmpcursor.par = par;
2895 tmpcursor.row = row;
2898 tmpcursor.x_fix = 0;
2899 tmpcursor.pos = pos;
2900 RedoDrawingOfParagraph(tmpcursor);
2905 // set the cursor again. Otherwise dangling pointers are possible
2906 // also set the selection
2910 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2911 sel_cursor = cursor;
2912 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2913 sel_start_cursor = cursor;
2914 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2915 sel_end_cursor = cursor;
2916 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2917 last_sel_cursor = cursor;
2920 SetCursorIntern(cursor.par, cursor.pos);
2924 // returns 0 if inset wasn't found
2925 int LyXText::UpdateInset(Inset * inset)
2927 // first check the current paragraph
2928 int pos = cursor.par->GetPositionOfInset(inset);
2930 CheckParagraph(cursor.par, pos);
2934 // check every paragraph
2936 LyXParagraph * par = FirstParagraph();
2938 // make sure the paragraph is open
2939 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2940 pos = par->GetPositionOfInset(inset);
2942 CheckParagraph(par, pos);
2953 void LyXText::SetCursor(LyXParagraph * par,
2954 LyXParagraph::size_type pos, bool setfont) const
2956 LyXCursor old_cursor = cursor;
2957 SetCursorIntern(par, pos, setfont);
2958 DeleteEmptyParagraphMechanism(old_cursor);
2962 void LyXText::SetCursorIntern(LyXParagraph * par,
2963 LyXParagraph::size_type pos, bool setfont) const
2967 LyXParagraph * tmppar;
2968 LyXParagraph::size_type vpos,cursor_vpos;
2970 // correct the cursor position if impossible
2971 if (pos > par->Last()){
2972 tmppar = par->ParFromPos(pos);
2973 pos = par->PositionInParFromPos(pos);
2976 if (par->IsDummy() && par->previous &&
2977 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2978 while (par->previous &&
2979 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2980 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2981 par = par->previous ;
2982 if (par->IsDummy() &&
2983 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2984 pos += par->text.size() + 1;
2986 if (par->previous) {
2987 par = par->previous;
2989 pos += par->text.size() + 1;
2997 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2998 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2999 && !cursor.par->IsSeparator(cursor.pos))
3000 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3002 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3003 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3005 current_font = cursor.par->GetFontSettings(cursor.pos);
3006 real_current_font = GetFont(cursor.par, cursor.pos);
3007 if (pos == 0 && par->size() == 0
3008 && owner_->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
3009 current_font.setDirection(LyXFont::RTL_DIR);
3010 real_current_font.setDirection(LyXFont::RTL_DIR);
3014 /* get the cursor y position in text */
3015 row = GetRow(par, pos, y);
3016 /* y is now the beginning of the cursor row */
3018 /* y is now the cursor baseline */
3021 /* now get the cursors x position */
3023 float fill_separator, fill_hfill, fill_label_hfill;
3024 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3026 LyXParagraph::size_type last = RowLast(row);
3027 if (row->pos > last)
3029 else if (pos <= last ) {
3030 LyXDirection letter_direction =
3031 row->par->getLetterDirection(pos);
3032 LyXDirection font_direction =
3033 real_current_font.getFontDirection();
3034 if (letter_direction == font_direction || pos == 0)
3035 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3036 ? log2vis(pos) : log2vis(pos)+1;
3038 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3039 ? log2vis(pos-1)+1 : log2vis(pos-1);
3041 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3042 ? log2vis(last)+1 : log2vis(last);
3044 /* table stuff -- begin*/
3045 if (row->par->table) {
3046 int cell = NumberOfCell(row->par, row->pos);
3048 x += row->par->table->GetBeginningOfTextInCell(cell);
3049 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3050 pos = vis2log(vpos);
3051 if (row->par->IsNewline(pos)) {
3052 x = x_old + row->par->table->WidthOfColumn(cell);
3055 x += row->par->table->GetBeginningOfTextInCell(cell);
3057 x += SingleWidth(row->par, pos);
3061 /* table stuff -- end*/
3062 LyXParagraph::size_type main_body =
3063 BeginningOfMainBody(row->par);
3064 if (main_body > 0 &&
3065 (main_body-1 > last ||
3066 !row->par->IsLineSeparator(main_body-1)))
3069 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3070 pos = vis2log(vpos);
3071 if (main_body > 0 && pos == main_body-1) {
3072 x += fill_label_hfill +
3073 GetFont(row->par, -2).stringWidth(
3074 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3075 if (row->par->IsLineSeparator(main_body-1))
3076 x -= SingleWidth(row->par, main_body-1);
3079 x += SingleWidth(row->par, pos);
3080 if (HfillExpansion(row, pos)) {
3081 if (pos >= main_body)
3084 x += fill_label_hfill;
3086 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3094 cursor.x_fix = cursor.x;
3099 void LyXText::SetCursorFromCoordinates(int x, long y) const
3101 LyXCursor old_cursor = cursor;
3103 /* get the row first */
3105 Row * row = GetRowNearY(y);
3107 cursor.par = row->par;
3109 int column = GetColumnNearX(row, x);
3110 cursor.pos = row->pos + column;
3112 cursor.y = y + row->baseline;
3117 (cursor.pos == cursor.par->Last()
3118 || cursor.par->IsSeparator(cursor.pos)
3119 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3120 && !cursor.par->IsSeparator(cursor.pos))
3121 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3123 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3124 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3126 current_font = cursor.par->GetFontSettings(cursor.pos);
3127 real_current_font = GetFont(cursor.par, cursor.pos);
3129 DeleteEmptyParagraphMechanism(old_cursor);
3133 void LyXText::CursorLeft() const
3136 if (cursor.par->table) {
3137 int cell = NumberOfCell(cursor.par, cursor.pos);
3138 if (cursor.par->table->IsContRow(cell) &&
3139 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3146 void LyXText::CursorLeftIntern() const
3148 if (cursor.pos > 0) {
3149 SetCursor(cursor.par, cursor.pos - 1);
3151 else if (cursor.par->Previous()) {
3152 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3157 void LyXText::CursorRight() const
3159 CursorRightIntern();
3160 if (cursor.par->table) {
3161 int cell = NumberOfCell(cursor.par, cursor.pos);
3162 if (cursor.par->table->IsContRow(cell) &&
3163 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3170 void LyXText::CursorRightIntern() const
3172 if (cursor.pos < cursor.par->Last()) {
3173 SetCursor(cursor.par, cursor.pos + 1);
3175 else if (cursor.par->Next()) {
3176 SetCursor(cursor.par->Next(), 0);
3181 void LyXText::CursorUp() const
3183 SetCursorFromCoordinates(cursor.x_fix,
3184 cursor.y - cursor.row->baseline - 1);
3185 if (cursor.par->table) {
3186 int cell = NumberOfCell(cursor.par, cursor.pos);
3187 if (cursor.par->table->IsContRow(cell) &&
3188 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3195 void LyXText::CursorDown() const
3197 if (cursor.par->table &&
3198 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3201 SetCursorFromCoordinates(cursor.x_fix,
3202 cursor.y - cursor.row->baseline
3203 + cursor.row->height + 1);
3204 if (cursor.par->table) {
3205 int cell = NumberOfCell(cursor.par, cursor.pos);
3206 int cell_above = cursor.par->table->GetCellAbove(cell);
3207 while(cursor.par->table &&
3208 cursor.par->table->IsContRow(cell) &&
3209 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3210 SetCursorFromCoordinates(cursor.x_fix,
3211 cursor.y - cursor.row->baseline
3212 + cursor.row->height + 1);
3213 if (cursor.par->table) {
3214 cell = NumberOfCell(cursor.par, cursor.pos);
3215 cell_above = cursor.par->table->GetCellAbove(cell);
3222 void LyXText::CursorUpParagraph() const
3224 if (cursor.pos > 0) {
3225 SetCursor(cursor.par, 0);
3227 else if (cursor.par->Previous()) {
3228 SetCursor(cursor.par->Previous(), 0);
3233 void LyXText::CursorDownParagraph() const
3235 if (cursor.par->Next()) {
3236 SetCursor(cursor.par->Next(), 0);
3238 SetCursor(cursor.par, cursor.par->Last());
3244 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3246 bool deleted = false;
3248 // this is the delete-empty-paragraph-mechanism.
3249 if (selection) return;
3251 #ifdef FIX_DOUBLE_SPACE
3252 /* Ok I'll put some comments here about what is missing.
3253 I have fixed BackSpace (and thus Delete) to not delete
3254 double-spaces automagically. I have also changed Cut,
3255 Copy and Paste to hopefully do some sensible things.
3256 There are still some small problems that can lead to
3257 double spaces stored in the document file or space at
3258 the beginning of paragraphs. This happens if you have
3259 the cursor betwenn to spaces and then save. Or if you
3260 cut and paste and the selection have a space at the
3261 beginning and then save right after the paste. I am
3262 sure none of these are very hard to fix, but I will
3263 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3264 that I can get some feedback. (Lgb)
3267 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3268 // delete the LineSeparator.
3271 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3272 // delete the LineSeparator.
3275 // If the pos around the old_cursor were spaces, delete one of them.
3276 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3277 && old_cursor.pos > 0
3278 && old_cursor.pos < old_cursor.par->Last()
3279 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3280 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3281 old_cursor.par->Erase(old_cursor.pos - 1);
3282 RedoParagraphs(old_cursor, old_cursor.par->Next());
3283 // or RedoDrawingOfParagraph(old_cursor);
3285 if (old_cursor.par == cursor.par &&
3286 cursor.pos > old_cursor.pos)
3287 SetCursor(cursor.par, cursor.pos - 1);
3289 SetCursor(cursor.par, cursor.pos);
3294 // Paragraph should not be deleted if empty
3295 if ((textclasslist.Style(parameters->textclass,
3296 old_cursor.par->GetLayout())).keepempty)
3299 LyXCursor tmpcursor;
3301 if (old_cursor.par != cursor.par) {
3302 if ( (old_cursor.par->Last() == 0
3303 || (old_cursor.par->Last() == 1
3304 && (old_cursor.par->IsLineSeparator(0))))
3305 && old_cursor.par->FirstPhysicalPar()
3306 == old_cursor.par->LastPhysicalPar()) {
3308 // ok, we will delete anything
3310 // make sure that you do not delete any environments
3311 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3312 !(old_cursor.row->previous
3313 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3314 && !(old_cursor.row->next
3315 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3317 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3318 ((old_cursor.row->previous
3319 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3321 (old_cursor.row->next
3322 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3324 status = LyXText::NEED_MORE_REFRESH;
3327 if (old_cursor.row->previous) {
3328 refresh_row = old_cursor.row->previous;
3329 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3331 cursor = old_cursor; // that undo can restore the right cursor position
3332 LyXParagraph * endpar = old_cursor.par->next;
3333 if (endpar && endpar->GetDepth()) {
3334 while (endpar && endpar->GetDepth()) {
3335 endpar = endpar->LastPhysicalPar()->Next();
3338 SetUndo(Undo::DELETE,
3339 old_cursor.par->previous,
3344 RemoveRow(old_cursor.row);
3345 if (params->paragraph == old_cursor.par) {
3346 params->paragraph = params->paragraph->next;
3349 delete old_cursor.par;
3351 /* Breakagain the next par. Needed
3352 * because of the parindent that
3353 * can occur or dissappear. The
3354 * next row can change its height,
3355 * if there is another layout before */
3356 if (refresh_row->next) {
3357 BreakAgain(refresh_row->next);
3358 UpdateCounters(refresh_row);
3360 SetHeightOfRow(refresh_row);
3362 refresh_row = old_cursor.row->next;
3363 refresh_y = old_cursor.y - old_cursor.row->baseline;
3366 cursor = old_cursor; // that undo can restore the right cursor position
3367 LyXParagraph *endpar = old_cursor.par->next;
3368 if (endpar && endpar->GetDepth()) {
3369 while (endpar && endpar->GetDepth()) {
3370 endpar = endpar->LastPhysicalPar()->Next();
3373 SetUndo(Undo::DELETE,
3374 old_cursor.par->previous,
3379 RemoveRow(old_cursor.row);
3381 if (params->paragraph == old_cursor.par) {
3382 params->paragraph = params->paragraph->next;
3384 delete old_cursor.par;
3386 /* Breakagain the next par. Needed
3387 because of the parindent that can
3388 occur or dissappear.
3389 The next row can change its height,
3390 if there is another layout before
3393 BreakAgain(refresh_row);
3394 UpdateCounters(refresh_row->previous);
3399 SetCursor(cursor.par, cursor.pos);
3401 /* if (cursor.y > old_cursor.y)
3402 cursor.y -= old_cursor.row->height; */
3404 if (sel_cursor.par == old_cursor.par
3405 && sel_cursor.pos == sel_cursor.pos) {
3406 // correct selection
3407 sel_cursor = cursor;
3412 if (old_cursor.par->ClearParagraph()){
3413 RedoParagraphs(old_cursor, old_cursor.par->Next());
3415 SetCursor(cursor.par, cursor.pos);
3416 sel_cursor = cursor;
3423 LyXParagraph * LyXText::GetParFromID(int id)
3425 LyXParagraph * result = FirstParagraph();
3426 while (result && result->id() != id)
3427 result = result->next;
3433 bool LyXText::TextUndo()
3435 // returns false if no undo possible
3436 Undo * undo = params->undostack.pop();
3441 .push(CreateUndo(undo->kind,
3442 GetParFromID(undo->number_of_before_par),
3443 GetParFromID(undo->number_of_behind_par)));
3445 return TextHandleUndo(undo);
3449 bool LyXText::TextRedo()
3451 // returns false if no redo possible
3452 Undo * undo = params->redostack.pop();
3457 .push(CreateUndo(undo->kind,
3458 GetParFromID(undo->number_of_before_par),
3459 GetParFromID(undo->number_of_behind_par)));
3461 return TextHandleUndo(undo);
3465 bool LyXText::TextHandleUndo(Undo * undo)
3467 // returns false if no undo possible
3468 bool result = false;
3470 LyXParagraph * before =
3471 GetParFromID(undo->number_of_before_par);
3472 LyXParagraph * behind =
3473 GetParFromID(undo->number_of_behind_par);
3474 LyXParagraph * tmppar;
3475 LyXParagraph * tmppar2;
3476 LyXParagraph * tmppar3;
3477 LyXParagraph * tmppar4;
3478 LyXParagraph * endpar;
3479 LyXParagraph * tmppar5;
3481 // if there's no before take the beginning
3482 // of the document for redoing
3484 SetCursorIntern(FirstParagraph(), 0);
3486 // replace the paragraphs with the undo informations
3488 tmppar3 = undo->par;
3489 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3492 while (tmppar4->next)
3493 tmppar4 = tmppar4->next;
3494 } // get last undo par
3496 // now remove the old text if there is any
3497 if (before != behind || (!behind && !before)){
3499 tmppar5 = before->next;
3501 tmppar5 = params->paragraph;
3503 while (tmppar5 && tmppar5 != behind){
3505 tmppar5 = tmppar5->next;
3506 // a memory optimization for edit: Only layout information
3507 // is stored in the undo. So restore the text informations.
3508 if (undo->kind == Undo::EDIT){
3509 tmppar2->text = tmppar->text;
3510 tmppar->text.clear();
3511 tmppar2 = tmppar2->next;
3513 if ( currentrow && currentrow->par == tmppar )
3514 currentrow = currentrow -> previous;
3515 // Commenting out this might remove the error
3516 // reported by Purify, but it might also
3517 // introduce a memory leak. We need to
3523 // put the new stuff in the list if there is one
3526 before->next = tmppar3;
3528 params->paragraph = tmppar3;
3529 tmppar3->previous = before;
3533 params->paragraph = behind;
3536 tmppar4->next = behind;
3538 behind->previous = tmppar4;
3542 // Set the cursor for redoing
3544 SetCursorIntern(before->FirstSelfrowPar(), 0);
3545 // check wether before points to a closed float and open it if necessary
3546 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3547 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3549 while (tmppar4->previous &&
3550 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3551 tmppar4 = tmppar4->previous;
3552 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3553 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3554 tmppar4 = tmppar4->next;
3559 // open a cosed footnote at the end if necessary
3560 if (behind && behind->previous &&
3561 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3562 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3563 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3564 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3565 behind = behind->next;
3569 // calculate the endpar for redoing the paragraphs.
3571 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3572 endpar = behind->LastPhysicalPar()->Next();
3574 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3579 tmppar = GetParFromID(undo->number_of_cursor_par);
3580 RedoParagraphs(cursor, endpar);
3582 SetCursorIntern(tmppar, undo->cursor_pos);
3583 UpdateCounters(cursor.row);
3593 void LyXText::FinishUndo()
3595 // makes sure the next operation will be stored
3596 undo_finished = True;
3600 void LyXText::FreezeUndo()
3602 // this is dangerous and for internal use only
3607 void LyXText::UnFreezeUndo()
3609 // this is dangerous and for internal use only
3610 undo_frozen = false;
3614 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3615 LyXParagraph const * behind) const
3618 params->undostack.push(CreateUndo(kind, before, behind));
3619 params->redostack.clear();
3623 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3624 LyXParagraph const * behind)
3626 params->redostack.push(CreateUndo(kind, before, behind));
3630 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3631 LyXParagraph const * behind) const
3633 int before_number = -1;
3634 int behind_number = -1;
3636 before_number = before->id();
3638 behind_number = behind->id();
3639 // Undo::EDIT and Undo::FINISH are
3640 // always finished. (no overlapping there)
3641 // overlapping only with insert and delete inside one paragraph:
3642 // Nobody wants all removed character
3643 // appear one by one when undoing.
3644 // EDIT is special since only layout information, not the
3645 // contents of a paragaph are stored.
3646 if (!undo_finished && kind != Undo::EDIT &&
3647 kind != Undo::FINISH){
3648 // check wether storing is needed
3649 if (!params->undostack.empty() &&
3650 params->undostack.top()->kind == kind &&
3651 params->undostack.top()->number_of_before_par == before_number &&
3652 params->undostack.top()->number_of_behind_par == behind_number ){
3657 // create a new Undo
3658 LyXParagraph * undopar;
3659 LyXParagraph * tmppar;
3660 LyXParagraph * tmppar2;
3662 LyXParagraph * start = 0;
3663 LyXParagraph * end = 0;
3666 start = before->next;
3668 start = FirstParagraph();
3670 end = behind->previous;
3672 end = FirstParagraph();
3678 && start != end->next
3679 && (before != behind || (!before && !behind))) {
3681 tmppar2 = tmppar->Clone();
3682 tmppar2->id(tmppar->id());
3684 // a memory optimization: Just store the layout information
3686 if (kind == Undo::EDIT){
3687 tmppar2->text.clear();
3692 while (tmppar != end && tmppar->next) {
3693 tmppar = tmppar->next;
3694 tmppar2->next = tmppar->Clone();
3695 tmppar2->next->id(tmppar->id());
3696 // a memory optimization: Just store the layout
3697 // information when only edit
3698 if (kind == Undo::EDIT){
3699 tmppar2->next->text.clear();
3701 tmppar2->next->previous = tmppar2;
3702 tmppar2 = tmppar2->next;
3706 undopar = 0; // nothing to replace (undo of delete maybe)
3708 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3709 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3711 Undo * undo = new Undo(kind,
3712 before_number, behind_number,
3713 cursor_par, cursor_pos,
3716 undo_finished = false;
3721 void LyXText::SetCursorParUndo()
3723 SetUndo(Undo::FINISH,
3724 cursor.par->ParFromPos(cursor.pos)->previous,
3725 cursor.par->ParFromPos(cursor.pos)->next);
3729 void LyXText::RemoveTableRow(LyXCursor * cur) const
3735 // move to the previous row
3736 int cell_act = NumberOfCell(cur->par, cur->pos);
3739 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3742 !cur->par->table->IsFirstCell(cell_act)) {
3744 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3749 // now we have to pay attention if the actual table is the
3750 // main row of TableContRows and if yes to delete all of them
3755 // delete up to the next row
3756 while (cur->pos < cur->par->Last() &&
3758 || !cur->par->table->IsFirstCell(cell_act))) {
3759 while (cur->pos < cur->par->Last() &&
3760 !cur->par->IsNewline(cur->pos))
3761 cur->par->Erase(cur->pos);
3764 if (cur->pos < cur->par->Last())
3765 cur->par->Erase(cur->pos);
3767 if (cur->pos && cur->pos == cur->par->Last()) {
3769 cur->par->Erase(cur->pos); // no newline at very end!
3771 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3772 !cur->par->table->IsContRow(cell_org) &&
3773 cur->par->table->IsContRow(cell));
3774 cur->par->table->DeleteRow(cell_org);
3779 bool LyXText::IsEmptyTableCell() const
3781 LyXParagraph::size_type pos = cursor.pos - 1;
3782 while (pos >= 0 && pos < cursor.par->Last()
3783 && !cursor.par->IsNewline(pos))
3785 return cursor.par->IsNewline(pos + 1);
3789 void LyXText::toggleAppendix(){
3790 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3791 bool start = !par->start_of_appendix;
3793 // ensure that we have only one start_of_appendix in this document
3794 LyXParagraph * tmp = FirstParagraph();
3795 for (; tmp; tmp = tmp->next)
3796 tmp->start_of_appendix = 0;
3797 par->start_of_appendix = start;
3799 // we can set the refreshing parameters now
3800 status = LyXText::NEED_MORE_REFRESH;
3802 refresh_row = 0; // not needed for full update
3804 SetCursor(cursor.par, cursor.pos);