1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
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() != Inset::HIGHLY_EDITABLE)
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)->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 bool first_footnote_par_is_not_empty = tmppar->next->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->size()
1176 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1177 && first_footnote_par_is_not_empty) {
1178 firsttmppar->next->InsertChar(0, ' ');
1180 firsttmppar->PasteParagraph();
1183 /* now redo the paragaphs */
1184 RedoParagraphs(cursor, tmppar);
1186 SetCursor(cursor.par, cursor.pos);
1188 /* sometimes it can happen, that there is a counter change */
1189 Row * row = cursor.row;
1190 while (row->next && row->par != tmppar && row->next->par != tmppar)
1192 UpdateCounters(row);
1199 /* the DTP switches for paragraphs. LyX will store them in the
1200 * first physicla paragraph. When a paragraph is broken, the top settings
1201 * rest, the bottom settings are given to the new one. So I can make shure,
1202 * they do not duplicate themself and you cannnot make dirty things with
1205 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1206 bool pagebreak_top, bool pagebreak_bottom,
1207 VSpace const & space_top,
1208 VSpace const & space_bottom,
1210 string labelwidthstring,
1213 LyXCursor tmpcursor = cursor;
1215 sel_start_cursor = cursor;
1216 sel_end_cursor = cursor;
1219 // make sure that the depth behind the selection are restored, too
1220 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1221 LyXParagraph * undoendpar = endpar;
1223 if (endpar && endpar->GetDepth()) {
1224 while (endpar && endpar->GetDepth()) {
1225 endpar = endpar->LastPhysicalPar()->Next();
1226 undoendpar = endpar;
1230 endpar = endpar->Next(); // because of parindents etc.
1235 .par->ParFromPos(sel_start_cursor.pos)->previous,
1239 LyXParagraph * tmppar = sel_end_cursor.par;
1240 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1241 SetCursor(tmppar->FirstPhysicalPar(), 0);
1242 status = LyXText::NEED_MORE_REFRESH;
1243 refresh_row = cursor.row;
1244 refresh_y = cursor.y - cursor.row->baseline;
1245 if (cursor.par->footnoteflag ==
1246 sel_start_cursor.par->footnoteflag) {
1247 cursor.par->line_top = line_top;
1248 cursor.par->line_bottom = line_bottom;
1249 cursor.par->pagebreak_top = pagebreak_top;
1250 cursor.par->pagebreak_bottom = pagebreak_bottom;
1251 cursor.par->added_space_top = space_top;
1252 cursor.par->added_space_bottom = space_bottom;
1253 // does the layout allow the new alignment?
1254 if (align == LYX_ALIGN_LAYOUT)
1255 align = textclasslist
1256 .Style(parameters->textclass,
1257 cursor.par->GetLayout()).align;
1258 if (align & textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).alignpossible) {
1261 if (align == textclasslist
1262 .Style(parameters->textclass,
1263 cursor.par->GetLayout()).align)
1264 cursor.par->align = LYX_ALIGN_LAYOUT;
1266 cursor.par->align = align;
1268 cursor.par->SetLabelWidthString(labelwidthstring);
1269 cursor.par->noindent = noindent;
1272 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1275 RedoParagraphs(sel_start_cursor, endpar);
1278 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1279 sel_cursor = cursor;
1280 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1282 SetCursor(tmpcursor.par, tmpcursor.pos);
1286 void LyXText::SetParagraphExtraOpt(int type,
1288 char const * widthp,
1289 int alignment, bool hfill,
1290 bool start_minipage)
1292 LyXCursor tmpcursor = cursor;
1293 LyXParagraph * tmppar;
1295 sel_start_cursor = cursor;
1296 sel_end_cursor = cursor;
1299 // make sure that the depth behind the selection are restored, too
1300 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1301 LyXParagraph * undoendpar = endpar;
1303 if (endpar && endpar->GetDepth()) {
1304 while (endpar && endpar->GetDepth()) {
1305 endpar = endpar->LastPhysicalPar()->Next();
1306 undoendpar = endpar;
1310 endpar = endpar->Next(); // because of parindents etc.
1315 .par->ParFromPos(sel_start_cursor.pos)->previous,
1318 tmppar = sel_end_cursor.par;
1319 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1320 SetCursor(tmppar->FirstPhysicalPar(), 0);
1321 status = LyXText::NEED_MORE_REFRESH;
1322 refresh_row = cursor.row;
1323 refresh_y = cursor.y - cursor.row->baseline;
1324 if (cursor.par->footnoteflag ==
1325 sel_start_cursor.par->footnoteflag) {
1326 if (type == LyXParagraph::PEXTRA_NONE) {
1327 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1328 cursor.par->UnsetPExtraType();
1329 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1332 cursor.par->SetPExtraType(type, width, widthp);
1333 cursor.par->pextra_hfill = hfill;
1334 cursor.par->pextra_start_minipage = start_minipage;
1335 cursor.par->pextra_alignment = alignment;
1338 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1340 RedoParagraphs(sel_start_cursor, endpar);
1342 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1343 sel_cursor = cursor;
1344 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1346 SetCursor(tmpcursor.par, tmpcursor.pos);
1350 char loweralphaCounter(int n)
1352 if (n < 1 || n > 26)
1358 char alphaCounter(int n)
1360 if (n < 1 || n > 26)
1366 char hebrewCounter(int n)
1368 static const char hebrew[22] = {
1369 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1370 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1371 '÷', 'ø', 'ù', 'ú'
1373 if (n < 1 || n > 22)
1379 static char const * romanCounter(int n)
1381 static char const * roman[20] = {
1382 "i", "ii", "iii", "iv", "v",
1383 "vi", "vii", "viii", "ix", "x",
1384 "xi", "xii", "xiii", "xiv", "xv",
1385 "xvi", "xvii", "xviii", "xix", "xx"
1387 if (n < 1 || n > 20)
1393 // set the counter of a paragraph. This includes the labels
1394 void LyXText::SetCounter(LyXParagraph * par) const
1396 // this is only relevant for the beginning of paragraph
1397 par = par->FirstPhysicalPar();
1399 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1402 LyXTextClass const & textclass =
1403 textclasslist.TextClass(parameters->textclass);
1405 /* copy the prev-counters to this one, unless this is the start of a
1406 footnote or of a bibliography or the very first paragraph */
1408 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1409 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1410 && par->footnotekind == LyXParagraph::FOOTNOTE)
1411 && !(textclasslist.Style(parameters->textclass,
1412 par->Previous()->GetLayout()
1413 ).labeltype != LABEL_BIBLIO
1414 && layout.labeltype == LABEL_BIBLIO)) {
1415 for (int i = 0; i < 10; ++i) {
1416 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1418 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1419 if (!par->appendix && par->start_of_appendix){
1420 par->appendix = true;
1421 for (int i = 0; i < 10; ++i) {
1422 par->setCounter(i, 0);
1425 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1426 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1429 for (int i = 0; i < 10; ++i) {
1430 par->setCounter(i, 0);
1432 par->appendix = par->start_of_appendix;
1437 // if this is an open marginnote and this is the first
1438 // entry in the marginnote and the enclosing
1439 // environment is an enum/item then correct for the
1440 // LaTeX behaviour (ARRae)
1441 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1442 && par->footnotekind == LyXParagraph::MARGIN
1444 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1445 && (par->PreviousBeforeFootnote()
1446 && textclasslist.Style(parameters->textclass,
1447 par->PreviousBeforeFootnote()->GetLayout()
1448 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1449 // Any itemize or enumerate environment in a marginnote
1450 // that is embedded in an itemize or enumerate
1451 // paragraph is seen by LaTeX as being at a deeper
1452 // level within that enclosing itemization/enumeration
1453 // even if there is a "standard" layout at the start of
1459 /* Maybe we have to increment the enumeration depth.
1460 * BUT, enumeration in a footnote is considered in isolation from its
1461 * surrounding paragraph so don't increment if this is the
1462 * first line of the footnote
1463 * AND, bibliographies can't have their depth changed ie. they
1464 * are always of depth 0
1467 && par->Previous()->GetDepth() < par->GetDepth()
1468 && textclasslist.Style(parameters->textclass,
1469 par->Previous()->GetLayout()
1470 ).labeltype == LABEL_COUNTER_ENUMI
1471 && par->enumdepth < 3
1472 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1473 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1474 && par->footnotekind == LyXParagraph::FOOTNOTE)
1475 && layout.labeltype != LABEL_BIBLIO) {
1479 /* Maybe we have to decrement the enumeration depth, see note above */
1481 && par->Previous()->GetDepth() > par->GetDepth()
1482 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1483 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1484 && par->footnotekind == LyXParagraph::FOOTNOTE)
1485 && layout.labeltype != LABEL_BIBLIO) {
1486 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1487 par->setCounter(6 + par->enumdepth,
1488 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1489 /* reset the counters.
1490 * A depth change is like a breaking layout
1492 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1493 par->setCounter(i, 0);
1496 if (!par->labelstring.empty()) {
1497 par->labelstring.clear();
1500 if (layout.margintype == MARGIN_MANUAL) {
1501 if (par->labelwidthstring.empty()) {
1502 par->SetLabelWidthString(layout.labelstring());
1505 par->SetLabelWidthString(string());
1508 /* is it a layout that has an automatic label ? */
1509 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1511 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1512 if (i >= 0 && i<= parameters->secnumdepth) {
1513 par->incCounter(i); // increment the counter
1515 // Is there a label? Useful for Chapter layout
1516 if (!par->appendix){
1517 if (!layout.labelstring().empty())
1518 par->labelstring = layout.labelstring();
1520 par->labelstring.clear();
1522 if (!layout.labelstring_appendix().empty())
1523 par->labelstring = layout.labelstring_appendix();
1525 par->labelstring.clear();
1533 if (!par->appendix) {
1534 switch (2 * LABEL_FIRST_COUNTER -
1535 textclass.maxcounter() + i) {
1536 case LABEL_COUNTER_CHAPTER:
1537 s << par->getCounter(i);
1539 case LABEL_COUNTER_SECTION:
1540 s << par->getCounter(i - 1) << '.'
1541 << par->getCounter(i);
1543 case LABEL_COUNTER_SUBSECTION:
1544 s << par->getCounter(i - 2) << '.'
1545 << par->getCounter(i - 1) << '.'
1546 << par->getCounter(i);
1548 case LABEL_COUNTER_SUBSUBSECTION:
1549 s << par->getCounter(i - 3) << '.'
1550 << par->getCounter(i - 2) << '.'
1551 << par->getCounter(i - 1) << '.'
1552 << par->getCounter(i);
1555 case LABEL_COUNTER_PARAGRAPH:
1556 s << par->getCounter(i - 4) << '.'
1557 << par->getCounter(i - 3) << '.'
1558 << par->getCounter(i - 2) << '.'
1559 << par->getCounter(i - 1) << '.'
1560 << par->getCounter(i);
1562 case LABEL_COUNTER_SUBPARAGRAPH:
1563 s << par->getCounter(i - 5) << '.'
1564 << par->getCounter(i - 4) << '.'
1565 << par->getCounter(i - 3) << '.'
1566 << par->getCounter(i - 2) << '.'
1567 << par->getCounter(i - 1) << '.'
1568 << par->getCounter(i);
1572 s << par->getCounter(i) << '.';
1575 } else { // appendix
1576 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1577 case LABEL_COUNTER_CHAPTER:
1578 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1579 s << alphaCounter(par->getCounter(i));
1581 s << hebrewCounter(par->getCounter(i));
1583 case LABEL_COUNTER_SECTION:
1584 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1585 s << alphaCounter(par->getCounter(i - 1));
1587 s << hebrewCounter(par->getCounter(i - 1));
1590 << par->getCounter(i);
1593 case LABEL_COUNTER_SUBSECTION:
1594 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1595 s << alphaCounter(par->getCounter(i - 2));
1597 s << hebrewCounter(par->getCounter(i - 2));
1600 << par->getCounter(i-1) << '.'
1601 << par->getCounter(i);
1604 case LABEL_COUNTER_SUBSUBSECTION:
1605 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1606 s << alphaCounter(par->getCounter(i-3));
1608 s << hebrewCounter(par->getCounter(i-3));
1611 << par->getCounter(i-2) << '.'
1612 << par->getCounter(i-1) << '.'
1613 << par->getCounter(i);
1616 case LABEL_COUNTER_PARAGRAPH:
1617 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1618 s << alphaCounter(par->getCounter(i-4));
1620 s << hebrewCounter(par->getCounter(i-4));
1623 << par->getCounter(i-3) << '.'
1624 << par->getCounter(i-2) << '.'
1625 << par->getCounter(i-1) << '.'
1626 << par->getCounter(i);
1629 case LABEL_COUNTER_SUBPARAGRAPH:
1630 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1631 s << alphaCounter(par->getCounter(i-5));
1633 s << hebrewCounter(par->getCounter(i-5));
1636 << par->getCounter(i-4) << '.'
1637 << par->getCounter(i-3) << '.'
1638 << par->getCounter(i-2) << '.'
1639 << par->getCounter(i-1) << '.'
1640 << par->getCounter(i);
1644 // Can this ever be reached? And in the
1645 // case it is, how can this be correct?
1647 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1653 par->labelstring += s.str().c_str();
1654 // We really want to remove the c_str as soon as
1658 char * tmps = s.str();
1659 par->labelstring += tmps;
1663 for (i++; i < 10; ++i) {
1664 // reset the following counters
1665 par->setCounter(i, 0);
1667 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1668 for (i++; i < 10; ++i) {
1669 // reset the following counters
1670 par->setCounter(i, 0);
1672 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1673 par->incCounter(i + par->enumdepth);
1674 int number = par->getCounter(i + par->enumdepth);
1681 switch (par->enumdepth) {
1683 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1685 << loweralphaCounter(number)
1689 << hebrewCounter(number)
1693 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1694 s << romanCounter(number) << '.';
1696 s << '.' << romanCounter(number);
1699 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1700 s << alphaCounter(number)
1704 << alphaCounter(number);
1707 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1714 par->labelstring = s.str().c_str();
1715 // we really want to get rid of that c_str()
1718 char * tmps = s.str();
1719 par->labelstring = tmps;
1723 for (i += par->enumdepth + 1; i < 10; ++i)
1724 par->setCounter(i, 0); /* reset the following counters */
1727 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1728 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1730 int number = par->getCounter(i);
1732 par->bibkey = new InsetBibKey();
1733 par->bibkey->setCounter(number);
1734 par->labelstring = layout.labelstring();
1736 // In biblio should't be following counters but...
1738 string s = layout.labelstring();
1740 // the caption hack:
1742 if (layout.labeltype == LABEL_SENSITIVE) {
1743 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1744 && (par->footnotekind == LyXParagraph::FIG
1745 || par->footnotekind == LyXParagraph::WIDE_FIG))
1746 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1750 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1751 && (par->footnotekind == LyXParagraph::TAB
1752 || par->footnotekind == LyXParagraph::WIDE_TAB))
1753 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1757 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1758 && par->footnotekind == LyXParagraph::ALGORITHM)
1759 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1762 s = ":Ãúéøåâìà ";
1764 /* par->SetLayout(0);
1765 s = layout->labelstring; */
1766 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1769 s = " :úåòîùî øñç";
1773 par->labelstring = s;
1775 /* reset the enumeration counter. They are always resetted
1776 * when there is any other layout between */
1777 for (int i = 6 + par->enumdepth; i < 10; ++i)
1778 par->setCounter(i, 0);
1783 /* Updates all counters BEHIND the row. Changed paragraphs
1784 * with a dynamic left margin will be rebroken. */
1785 void LyXText::UpdateCounters(Row * row) const
1794 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1795 par = row->par->LastPhysicalPar()->Next();
1797 par = row->par->next;
1802 while (row->par != par)
1807 /* now check for the headline layouts. remember that they
1808 * have a dynamic left margin */
1810 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1811 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1814 /* Rebreak the paragraph */
1815 RemoveParagraph(row);
1816 AppendParagraph(row);
1818 /* think about the damned open footnotes! */
1819 while (par->Next() &&
1820 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1821 || par->Next()->IsDummy())){
1823 if (par->IsDummy()) {
1824 while (row->par != par)
1826 RemoveParagraph(row);
1827 AppendParagraph(row);
1832 par = par->LastPhysicalPar()->Next();
1838 /* insets an inset. */
1839 void LyXText::InsertInset(Inset *inset)
1841 SetUndo(Undo::INSERT,
1842 cursor.par->ParFromPos(cursor.pos)->previous,
1843 cursor.par->ParFromPos(cursor.pos)->next);
1844 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1845 cursor.par->InsertInset(cursor.pos, inset);
1846 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1847 * The character will not be inserted a
1852 // this is for the simple cut and paste mechanism
1853 static LyXParagraph * simple_cut_buffer = 0;
1854 static char simple_cut_buffer_textclass = 0;
1856 void DeleteSimpleCutBuffer()
1858 if (!simple_cut_buffer)
1860 LyXParagraph * tmppar;
1862 while (simple_cut_buffer) {
1863 tmppar = simple_cut_buffer;
1864 simple_cut_buffer = simple_cut_buffer->next;
1867 simple_cut_buffer = 0;
1871 void LyXText::copyEnvironmentType()
1873 copylayouttype = cursor.par->GetLayout();
1877 void LyXText::pasteEnvironmentType()
1879 SetLayout(copylayouttype);
1883 void LyXText::CutSelection(bool doclear)
1885 // This doesn't make sense, if there is no selection
1889 // OK, we have a selection. This is always between sel_start_cursor
1890 // and sel_end cursor
1891 LyXParagraph * tmppar;
1893 // Check whether there are half footnotes in the selection
1894 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1895 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1896 tmppar = sel_start_cursor.par;
1897 while (tmppar != sel_end_cursor.par){
1898 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1899 WriteAlert(_("Impossible operation"),
1900 _("Don't know what to do with half floats."),
1904 tmppar = tmppar->Next();
1908 /* table stuff -- begin */
1909 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1910 if ( sel_start_cursor.par != sel_end_cursor.par) {
1911 WriteAlert(_("Impossible operation"),
1912 _("Don't know what to do with half tables."),
1916 sel_start_cursor.par->table->Reinit();
1918 /* table stuff -- end */
1920 // make sure that the depth behind the selection are restored, too
1921 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1922 LyXParagraph * undoendpar = endpar;
1924 if (endpar && endpar->GetDepth()) {
1925 while (endpar && endpar->GetDepth()) {
1926 endpar = endpar->LastPhysicalPar()->Next();
1927 undoendpar = endpar;
1929 } else if (endpar) {
1930 endpar = endpar->Next(); // because of parindents etc.
1933 SetUndo(Undo::DELETE,
1935 .par->ParFromPos(sel_start_cursor.pos)->previous,
1938 // clear the simple_cut_buffer
1939 DeleteSimpleCutBuffer();
1941 // set the textclass
1942 simple_cut_buffer_textclass = parameters->textclass;
1944 #ifdef WITH_WARNINGS
1945 #warning Asger: Make cut more intelligent here.
1948 White paper for "intelligent" cutting:
1950 Example: "This is our text."
1951 Using " our " as selection, cutting will give "This istext.".
1952 Using "our" as selection, cutting will give "This is text.".
1953 Using " our" as selection, cutting will give "This is text.".
1954 Using "our " as selection, cutting will give "This is text.".
1956 All those four selections will (however) paste identically:
1957 Pasting with the cursor right after the "is" will give the
1958 original text with all four selections.
1960 The rationale is to be intelligent such that words are copied,
1961 cut and pasted in a functional manner.
1963 This is not implemented yet. (Asger)
1965 The changes below sees to do a lot of what you want. However
1966 I have not verified that all cases work as they should:
1968 - cut in multiple row
1970 - cut across footnotes and paragraph
1971 My simplistic tests show that the idea are basically sound but
1972 there are some items to fix up...we only need to find them
1975 As do redo Asger's example above (with | beeing the cursor in the
1976 result after cutting.):
1978 Example: "This is our text."
1979 Using " our " as selection, cutting will give "This is|text.".
1980 Using "our" as selection, cutting will give "This is | text.".
1981 Using " our" as selection, cutting will give "This is| text.".
1982 Using "our " as selection, cutting will give "This is |text.".
1987 #ifndef FIX_DOUBLE_SPACE
1988 bool space_wrapped =
1989 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1990 if (sel_end_cursor.pos > 0
1991 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1992 // please break before a space at the end
1993 sel_end_cursor.pos--;
1994 space_wrapped = true;
1996 // cut behind a space if there is one
1997 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1998 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1999 && (sel_start_cursor.par != sel_end_cursor.par
2000 || sel_start_cursor.pos < sel_end_cursor.pos))
2001 sel_start_cursor.pos++;
2003 // there are two cases: cut only within one paragraph or
2004 // more than one paragraph
2006 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2007 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2008 // only within one paragraph
2009 simple_cut_buffer = new LyXParagraph;
2010 LyXParagraph::size_type i =
2011 sel_start_cursor.pos;
2012 for (; i < sel_end_cursor.pos; ++i) {
2013 /* table stuff -- begin */
2014 if (sel_start_cursor.par->table
2015 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2016 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2017 sel_start_cursor.pos++;
2019 /* table stuff -- end */
2020 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2021 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2023 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2025 #ifndef FIX_DOUBLE_SPACE
2026 // check for double spaces
2027 if (sel_start_cursor.pos &&
2028 sel_start_cursor.par->Last() > sel_start_cursor.pos
2029 && sel_start_cursor.par
2030 ->IsLineSeparator(sel_start_cursor.pos - 1)
2031 && sel_start_cursor.par
2032 ->IsLineSeparator(sel_start_cursor.pos)) {
2033 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2036 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
2039 endpar = sel_end_cursor.par->Next();
2041 // cut more than one paragraph
2044 ->BreakParagraphConservative(sel_end_cursor.pos);
2045 #ifndef FIX_DOUBLE_SPACE
2046 // insert a space at the end if there was one
2049 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2051 sel_end_cursor.par = sel_end_cursor.par->Next();
2052 sel_end_cursor.pos = 0;
2054 cursor = sel_end_cursor;
2056 #ifndef FIX_DOUBLE_SPACE
2057 // please break behind a space, if there is one.
2058 // The space should be copied too
2059 if (sel_start_cursor.par
2060 ->IsLineSeparator(sel_start_cursor.pos))
2061 sel_start_cursor.pos++;
2063 sel_start_cursor.par
2064 ->BreakParagraphConservative(sel_start_cursor.pos);
2065 #ifndef FIX_DOUBLE_SPACE
2066 if (!sel_start_cursor.pos
2067 || sel_start_cursor.par
2068 ->IsLineSeparator(sel_start_cursor.pos - 1)
2069 || sel_start_cursor.par
2070 ->IsNewline(sel_start_cursor.pos - 1)) {
2071 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2074 // store the endparagraph for redoing later
2075 endpar = sel_end_cursor.par->Next(); /* needed because
2080 // store the selection
2081 simple_cut_buffer = sel_start_cursor.par
2082 ->ParFromPos(sel_start_cursor.pos)->next;
2083 simple_cut_buffer->previous = 0;
2084 sel_end_cursor.par->previous->next = 0;
2086 // cut the selection
2087 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2088 = sel_end_cursor.par;
2090 sel_end_cursor.par->previous
2091 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2093 // care about footnotes
2094 if (simple_cut_buffer->footnoteflag) {
2095 LyXParagraph * tmppar = simple_cut_buffer;
2097 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2098 tmppar = tmppar->next;
2102 // the cut selection should begin with standard layout
2103 simple_cut_buffer->Clear();
2105 // paste the paragraphs again, if possible
2107 sel_start_cursor.par->Next()->ClearParagraph();
2108 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2110 !sel_start_cursor.par->Next()->Last())
2111 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2113 #ifndef FIX_DOUBLE_SPACE
2114 // maybe a forgotten blank
2115 if (sel_start_cursor.pos
2116 && sel_start_cursor.par
2117 ->IsLineSeparator(sel_start_cursor.pos)
2118 && sel_start_cursor.par
2119 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2120 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2125 // sometimes necessary
2127 sel_start_cursor.par->ClearParagraph();
2129 RedoParagraphs(sel_start_cursor, endpar);
2132 cursor = sel_start_cursor;
2133 SetCursor(cursor.par, cursor.pos);
2134 sel_cursor = cursor;
2135 UpdateCounters(cursor.row);
2139 void LyXText::CopySelection()
2141 // this doesnt make sense, if there is no selection
2145 // ok we have a selection. This is always between sel_start_cursor
2146 // and sel_end cursor
2147 LyXParagraph * tmppar;
2149 /* check wether there are half footnotes in the selection */
2150 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2151 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2152 tmppar = sel_start_cursor.par;
2153 while (tmppar != sel_end_cursor.par) {
2154 if (tmppar->footnoteflag !=
2155 sel_end_cursor.par->footnoteflag) {
2156 WriteAlert(_("Impossible operation"),
2157 _("Don't know what to do"
2158 " with half floats."),
2162 tmppar = tmppar->Next();
2166 /* table stuff -- begin */
2167 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2168 if ( sel_start_cursor.par != sel_end_cursor.par){
2169 WriteAlert(_("Impossible operation"),
2170 _("Don't know what to do with half tables."),
2175 /* table stuff -- end */
2177 // delete the simple_cut_buffer
2178 DeleteSimpleCutBuffer();
2180 // set the textclass
2181 simple_cut_buffer_textclass = parameters->textclass;
2183 #ifdef FIX_DOUBLE_SPACE
2184 // copy behind a space if there is one
2185 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2186 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2187 && (sel_start_cursor.par != sel_end_cursor.par
2188 || sel_start_cursor.pos < sel_end_cursor.pos))
2189 sel_start_cursor.pos++;
2191 // there are two cases: copy only within one paragraph
2192 // or more than one paragraph
2193 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2194 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2195 // only within one paragraph
2196 simple_cut_buffer = new LyXParagraph;
2197 LyXParagraph::size_type i = 0;
2198 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2199 sel_start_cursor.par->CopyIntoMinibuffer(i);
2200 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2203 // copy more than one paragraph
2204 // clone the paragraphs within the selection
2206 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2207 simple_cut_buffer = tmppar->Clone();
2208 LyXParagraph *tmppar2 = simple_cut_buffer;
2210 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2212 tmppar = tmppar->next;
2213 tmppar2->next = tmppar->Clone();
2214 tmppar2->next->previous = tmppar2;
2215 tmppar2 = tmppar2->next;
2219 // care about footnotes
2220 if (simple_cut_buffer->footnoteflag) {
2221 tmppar = simple_cut_buffer;
2223 tmppar->footnoteflag =
2224 LyXParagraph::NO_FOOTNOTE;
2225 tmppar = tmppar->next;
2229 // the simple_cut_buffer paragraph is too big
2230 LyXParagraph::size_type tmpi2 =
2231 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2232 for (; tmpi2; --tmpi2)
2233 simple_cut_buffer->Erase(0);
2235 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2237 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2238 while (tmppar2->size() > tmpi2) {
2239 tmppar2->Erase(tmppar2->size() - 1);
2245 void LyXText::PasteSelection()
2247 // this does not make sense, if there is nothing to paste
2248 if (!simple_cut_buffer)
2251 LyXParagraph * tmppar;
2252 LyXParagraph * endpar;
2254 LyXCursor tmpcursor;
2256 // be carefull with footnotes in footnotes
2257 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2259 // check whether the cut_buffer includes a footnote
2260 tmppar = simple_cut_buffer;
2262 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2263 tmppar = tmppar->next;
2266 WriteAlert(_("Impossible operation"),
2267 _("Can't paste float into float!"),
2273 /* table stuff -- begin */
2274 if (cursor.par->table) {
2275 if (simple_cut_buffer->next) {
2276 WriteAlert(_("Impossible operation"),
2277 _("Table cell cannot include more than one paragraph!"),
2282 /* table stuff -- end */
2284 SetUndo(Undo::INSERT,
2285 cursor.par->ParFromPos(cursor.pos)->previous,
2286 cursor.par->ParFromPos(cursor.pos)->next);
2290 // There are two cases: cutbuffer only one paragraph or many
2291 if (!simple_cut_buffer->next) {
2292 // only within a paragraph
2294 #ifndef FIX_DOUBLE_SPACE
2295 // please break behind a space, if there is one
2296 while (tmpcursor.par->Last() > tmpcursor.pos
2297 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2300 tmppar = simple_cut_buffer->Clone();
2301 /* table stuff -- begin */
2302 bool table_too_small = false;
2303 if (tmpcursor.par->table) {
2304 while (simple_cut_buffer->size()
2305 && !table_too_small) {
2306 if (simple_cut_buffer->IsNewline(0)){
2307 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2309 simple_cut_buffer->Erase(0);
2310 if (tmpcursor.pos < tmpcursor.par->Last())
2313 table_too_small = true;
2315 #ifdef FIX_DOUBLE_SPACE
2316 // This is an attempt to fix the
2317 // "never insert a space at the
2318 // beginning of a paragraph" problem.
2319 if (tmpcursor.pos == 0
2320 && simple_cut_buffer->IsLineSeparator(0)) {
2321 simple_cut_buffer->Erase(0);
2323 simple_cut_buffer->CutIntoMinibuffer(0);
2324 simple_cut_buffer->Erase(0);
2325 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2329 simple_cut_buffer->CutIntoMinibuffer(0);
2330 simple_cut_buffer->Erase(0);
2331 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2337 /* table stuff -- end */
2338 // Some provisions should be done here for checking
2339 // if we are inserting at the beginning of a
2340 // paragraph. If there are a space at the beginning
2341 // of the text to insert and we are inserting at
2342 // the beginning of the paragraph the space should
2344 while (simple_cut_buffer->size()) {
2345 #ifdef FIX_DOUBLE_SPACE
2346 // This is an attempt to fix the
2347 // "never insert a space at the
2348 // beginning of a paragraph" problem.
2349 if (tmpcursor.pos == 0
2350 && simple_cut_buffer->IsLineSeparator(0)) {
2351 simple_cut_buffer->Erase(0);
2353 simple_cut_buffer->CutIntoMinibuffer(0);
2354 simple_cut_buffer->Erase(0);
2355 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2359 simple_cut_buffer->CutIntoMinibuffer(0);
2360 simple_cut_buffer->Erase(0);
2361 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2366 delete simple_cut_buffer;
2367 simple_cut_buffer = tmppar;
2368 endpar = tmpcursor.par->Next();
2372 // make a copy of the simple cut_buffer
2373 tmppar = simple_cut_buffer;
2374 LyXParagraph * simple_cut_clone = tmppar->Clone();
2375 LyXParagraph * tmppar2 = simple_cut_clone;
2376 if (cursor.par->footnoteflag){
2377 tmppar->footnoteflag = cursor.par->footnoteflag;
2378 tmppar->footnotekind = cursor.par->footnotekind;
2380 while (tmppar->next) {
2381 tmppar = tmppar->next;
2382 tmppar2->next = tmppar->Clone();
2383 tmppar2->next->previous = tmppar2;
2384 tmppar2 = tmppar2->next;
2385 if (cursor.par->footnoteflag){
2386 tmppar->footnoteflag = cursor.par->footnoteflag;
2387 tmppar->footnotekind = cursor.par->footnotekind;
2391 // make sure there is no class difference
2392 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2393 parameters->textclass,
2396 // make the simple_cut_buffer exactly the same layout than
2397 // the cursor paragraph
2398 simple_cut_buffer->MakeSameLayout(cursor.par);
2400 // find the end of the buffer
2401 LyXParagraph * lastbuffer = simple_cut_buffer;
2402 while (lastbuffer->Next())
2403 lastbuffer = lastbuffer->Next();
2405 #ifndef FIX_DOUBLE_SPACE
2406 // Please break behind a space, if there is one. The space
2407 // should be copied too.
2408 if (cursor.par->Last() > cursor.pos
2409 && cursor.par->IsLineSeparator(cursor.pos))
2412 bool paste_the_end = false;
2414 // open the paragraph for inserting the simple_cut_buffer
2416 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2417 cursor.par->BreakParagraphConservative(cursor.pos);
2418 paste_the_end = true;
2421 #ifndef FIX_DOUBLE_SPACE
2422 // be careful with double spaces
2423 if ((!cursor.par->Last()
2424 || cursor.par->IsLineSeparator(cursor.pos - 1)
2425 || cursor.par->IsNewline(cursor.pos - 1))
2426 && simple_cut_buffer->text.size()
2427 && simple_cut_buffer->IsLineSeparator(0))
2428 simple_cut_buffer->Erase(0);
2430 // set the end for redoing later
2431 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2434 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2435 cursor.par->ParFromPos(cursor.pos)->next;
2436 cursor.par->ParFromPos(cursor.pos)->next->previous =
2437 lastbuffer->ParFromPos(lastbuffer->Last());
2439 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2440 simple_cut_buffer->previous =
2441 cursor.par->ParFromPos(cursor.pos);
2443 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2444 lastbuffer = cursor.par;
2446 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2448 // store the new cursor position
2449 tmpcursor.par = lastbuffer;
2450 tmpcursor.pos = lastbuffer->Last();
2452 // maybe some pasting
2453 if (lastbuffer->Next() && paste_the_end) {
2454 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2455 #ifndef FIX_DOUBLE_SPACE
2456 // be careful with double spaces
2457 if ((!lastbuffer->Last()
2458 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2459 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2460 && lastbuffer->Next()->Last()
2461 && lastbuffer->Next()->IsLineSeparator(0))
2462 lastbuffer->Next()->Erase(0);
2464 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2466 } else if (!lastbuffer->Next()->Last()) {
2467 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2468 #ifndef FIX_DOUBLE_SPACE
2469 // be careful witth double spaces
2470 if ((!lastbuffer->Last()
2471 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2472 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2473 && lastbuffer->Next()->Last()
2474 && lastbuffer->Next()->IsLineSeparator(0))
2475 lastbuffer->Next()->Erase(0);
2477 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2479 } else if (!lastbuffer->Last()) {
2480 lastbuffer->MakeSameLayout(lastbuffer->next);
2481 #ifndef FIX_DOUBLE_SPACE
2482 // be careful witth double spaces
2483 if ((!lastbuffer->Last()
2484 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2485 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2486 && lastbuffer->Next()->Last()
2487 && lastbuffer->Next()->IsLineSeparator(0))
2488 lastbuffer->Next()->Erase(0);
2490 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2493 lastbuffer->Next()->ClearParagraph();
2496 // restore the simple cut buffer
2497 simple_cut_buffer = simple_cut_clone;
2500 RedoParagraphs(cursor, endpar);
2502 SetCursor(cursor.par, cursor.pos);
2505 sel_cursor = cursor;
2506 SetCursor(tmpcursor.par, tmpcursor.pos);
2508 UpdateCounters(cursor.row);
2512 // returns a pointer to the very first LyXParagraph
2513 LyXParagraph * LyXText::FirstParagraph() const
2515 return params->paragraph;
2519 // returns true if the specified string is at the specified position
2520 bool LyXText::IsStringInText(LyXParagraph * par,
2521 LyXParagraph::size_type pos,
2522 char const * str) const
2526 while (pos + i < par->Last() && str[i] &&
2527 str[i] == par->GetChar(pos + i)) {
2537 // sets the selection over the number of characters of string, no check!!
2538 void LyXText::SetSelectionOverString(char const * string)
2540 sel_cursor = cursor;
2541 for (int i = 0; string[i]; ++i)
2547 // simple replacing. The font of the first selected character is used
2548 void LyXText::ReplaceSelectionWithString(char const * str)
2553 if (!selection) { // create a dummy selection
2554 sel_end_cursor = cursor;
2555 sel_start_cursor = cursor;
2558 // Get font setting before we cut
2559 LyXParagraph::size_type pos = sel_end_cursor.pos;
2560 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2562 // Insert the new string
2563 for (int i = 0; str[i]; ++i) {
2564 sel_end_cursor.par->InsertChar(pos, str[i]);
2565 sel_end_cursor.par->SetFont(pos, font);
2569 // Cut the selection
2576 // if the string can be found: return true and set the cursor to
2578 bool LyXText::SearchForward(char const * str) const
2580 LyXParagraph * par = cursor.par;
2581 LyXParagraph::size_type pos = cursor.pos;
2582 while (par && !IsStringInText(par, pos, str)) {
2583 if (pos < par->Last() - 1)
2591 SetCursor(par, pos);
2599 bool LyXText::SearchBackward(char const * string) const
2601 LyXParagraph * par = cursor.par;
2602 int pos = cursor.pos;
2608 // We skip empty paragraphs (Asger)
2610 par = par->Previous();
2612 pos = par->Last() - 1;
2613 } while (par && pos < 0);
2615 } while (par && !IsStringInText(par, pos, string));
2618 SetCursor(par, pos);
2625 // needed to insert the selection
2626 void LyXText::InsertStringA(string const & str)
2628 LyXParagraph * par = cursor.par;
2629 LyXParagraph::size_type pos = cursor.pos;
2630 LyXParagraph::size_type a = 0;
2632 LyXParagraph * endpar = cursor.par->Next();
2637 textclasslist.Style(parameters->textclass,
2638 cursor.par->GetLayout()).isEnvironment();
2639 // only to be sure, should not be neccessary
2642 // insert the string, don't insert doublespace
2643 string::size_type i = 0;
2644 while (i < str.length()) {
2645 if (str[i] != '\n') {
2647 && i + 1 < str.length() && str[i + 1] != ' '
2648 && pos && par->GetChar(pos - 1)!= ' ') {
2649 par->InsertChar(pos,' ');
2651 } else if (par->table) {
2652 if (str[i] == '\t') {
2653 while((pos < par->size()) &&
2654 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2656 if (pos < par->size())
2658 else // no more fields to fill skip the rest
2660 } else if ((str[i] != 13) &&
2661 ((str[i] & 127) >= ' ')) {
2662 par->InsertChar(pos, str[i]);
2665 } else if (str[i] == ' ') {
2667 InsetSpecialChar * new_inset =
2668 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2669 par->InsertChar(pos, LyXParagraph::META_INSET);
2670 par->InsertInset(pos, new_inset);
2672 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2675 } else if (str[i] == '\t') {
2676 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2678 InsetSpecialChar * new_inset =
2679 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2680 par->InsertChar(pos, LyXParagraph::META_INSET);
2681 par->InsertInset(pos, new_inset);
2683 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2687 } else if (str[i] != 13 &&
2688 // Ignore unprintables
2689 (str[i] & 127) >= ' ') {
2690 par->InsertChar(pos, str[i]);
2695 if (i + 1 >= str.length()) {
2699 while((pos < par->size()) &&
2700 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2703 cell = NumberOfCell(par, pos);
2704 while((pos < par->size()) &&
2705 !(par->table->IsFirstCell(cell))) {
2707 while((pos < par->size()) &&
2708 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2711 cell = NumberOfCell(par, pos);
2713 if (pos >= par->size())
2714 // no more fields to fill skip the rest
2717 if (!par->size()) { // par is empty
2719 InsetSpecialChar * new_inset =
2720 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2721 par->InsertChar(pos, LyXParagraph::META_INSET);
2722 par->InsertInset(pos, new_inset);
2724 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2728 par->BreakParagraph(pos, flag);
2736 RedoParagraphs(cursor, endpar);
2737 SetCursor(cursor.par, cursor.pos);
2738 sel_cursor = cursor;
2739 SetCursor(par, pos);
2744 /* turns double-CR to single CR, others where converted into one blank and 13s
2745 * that are ignored .Double spaces are also converted into one. Spaces at
2746 * the beginning of a paragraph are forbidden. tabs are converted into one
2747 * space. then InsertStringA is called */
2748 void LyXText::InsertStringB(string const & s)
2751 LyXParagraph * par = cursor.par;
2752 string::size_type i = 1;
2753 while (i < str.length()) {
2754 if (str[i] == '\t' && !par->table)
2756 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2758 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2759 if (str[i + 1] != '\n') {
2760 if (str[i - 1] != ' ')
2765 while (i + 1 < str.length()
2766 && (str[i + 1] == ' '
2767 || str[i + 1] == '\t'
2768 || str[i + 1] == '\n'
2769 || str[i + 1] == 13)) {
2780 bool LyXText::GotoNextError() const
2782 LyXCursor res = cursor;
2784 if (res.pos < res.par->Last() - 1) {
2788 res.par = res.par->Next();
2793 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2794 && res.par->GetInset(res.pos)->AutoDelete()));
2797 SetCursor(res.par, res.pos);
2804 bool LyXText::GotoNextNote() const
2806 LyXCursor res = cursor;
2808 if (res.pos < res.par->Last() - 1) {
2811 res.par = res.par->Next();
2816 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2817 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2820 SetCursor(res.par, res.pos);
2827 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2828 LyXTextClassList::size_type class2,
2832 if (!par || class1 == class2)
2834 par = par->FirstPhysicalPar();
2836 string name = textclasslist.NameOfLayout(class1, par->layout);
2838 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2839 textclasslist.NumberOfLayout(class2, name);
2842 } else { // layout not found
2843 // use default layout "Standard" (0)
2848 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2850 string s = "Layout had to be changed from\n"
2851 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2852 + "\nbecause of class conversion from\n"
2853 + textclasslist.NameOfClass(class1) + " to "
2854 + textclasslist.NameOfClass(class2);
2855 InsetError * new_inset = new InsetError(s);
2856 par->InsertChar(0, LyXParagraph::META_INSET);
2857 par->InsertInset(0, new_inset);
2866 void LyXText::CheckParagraph(LyXParagraph * par,
2867 LyXParagraph::size_type pos)
2870 LyXCursor tmpcursor;
2872 /* table stuff -- begin*/
2875 CheckParagraphInTable(par, pos);
2878 /* table stuff -- end*/
2881 LyXParagraph::size_type z;
2882 Row * row = GetRow(par, pos, y);
2884 // is there a break one row above
2885 if (row->previous && row->previous->par == row->par) {
2886 z = NextBreakPoint(row->previous, paperwidth);
2887 if ( z >= row->pos) {
2888 // set the dimensions of the row above
2889 y -= row->previous->height;
2891 refresh_row = row->previous;
2892 status = LyXText::NEED_MORE_REFRESH;
2894 BreakAgain(row->previous);
2896 // set the cursor again. Otherwise
2897 // dangling pointers are possible
2898 SetCursor(cursor.par, cursor.pos);
2899 sel_cursor = cursor;
2904 int tmpheight = row->height;
2905 LyXParagraph::size_type tmplast = RowLast(row);
2910 if (row->height == tmpheight && RowLast(row) == tmplast)
2911 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2913 status = LyXText::NEED_MORE_REFRESH;
2915 // check the special right address boxes
2916 if (textclasslist.Style(parameters->textclass,
2917 par->GetLayout()).margintype
2918 == MARGIN_RIGHT_ADDRESS_BOX) {
2919 tmpcursor.par = par;
2920 tmpcursor.row = row;
2923 tmpcursor.x_fix = 0;
2924 tmpcursor.pos = pos;
2925 RedoDrawingOfParagraph(tmpcursor);
2930 // set the cursor again. Otherwise dangling pointers are possible
2931 // also set the selection
2935 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2936 sel_cursor = cursor;
2937 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2938 sel_start_cursor = cursor;
2939 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2940 sel_end_cursor = cursor;
2941 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2942 last_sel_cursor = cursor;
2945 SetCursorIntern(cursor.par, cursor.pos);
2949 // returns 0 if inset wasn't found
2950 int LyXText::UpdateInset(Inset * inset)
2952 // first check the current paragraph
2953 int pos = cursor.par->GetPositionOfInset(inset);
2955 CheckParagraph(cursor.par, pos);
2959 // check every paragraph
2961 LyXParagraph * par = FirstParagraph();
2963 // make sure the paragraph is open
2964 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2965 pos = par->GetPositionOfInset(inset);
2967 CheckParagraph(par, pos);
2978 void LyXText::SetCursor(LyXParagraph * par,
2979 LyXParagraph::size_type pos, bool setfont) const
2981 LyXCursor old_cursor = cursor;
2982 SetCursorIntern(par, pos, setfont);
2983 DeleteEmptyParagraphMechanism(old_cursor);
2987 void LyXText::SetCursorIntern(LyXParagraph * par,
2988 LyXParagraph::size_type pos, bool setfont) const
2992 LyXParagraph * tmppar;
2993 LyXParagraph::size_type vpos,cursor_vpos;
2995 // correct the cursor position if impossible
2996 if (pos > par->Last()){
2997 tmppar = par->ParFromPos(pos);
2998 pos = par->PositionInParFromPos(pos);
3001 if (par->IsDummy() && par->previous &&
3002 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3003 while (par->previous &&
3004 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3005 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3006 par = par->previous ;
3007 if (par->IsDummy() &&
3008 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3009 pos += par->size() + 1;
3011 if (par->previous) {
3012 par = par->previous;
3014 pos += par->size() + 1;
3022 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3023 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3024 && !cursor.par->IsSeparator(cursor.pos))
3025 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3027 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3028 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3030 current_font = cursor.par->GetFontSettings(cursor.pos);
3031 real_current_font = GetFont(cursor.par, cursor.pos);
3032 if (pos == 0 && par->size() == 0
3033 && parameters->getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
3034 current_font.setDirection(LyXFont::RTL_DIR);
3035 real_current_font.setDirection(LyXFont::RTL_DIR);
3039 /* get the cursor y position in text */
3040 row = GetRow(par, pos, y);
3041 /* y is now the beginning of the cursor row */
3043 /* y is now the cursor baseline */
3046 /* now get the cursors x position */
3048 float fill_separator, fill_hfill, fill_label_hfill;
3049 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3051 LyXParagraph::size_type last = RowLast(row);
3052 if (row->pos > last)
3054 else if (pos <= last ) {
3055 LyXDirection letter_direction =
3056 row->par->getLetterDirection(pos);
3057 LyXDirection font_direction =
3058 real_current_font.getFontDirection();
3059 if (letter_direction == font_direction || pos == 0)
3060 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3061 ? log2vis(pos) : log2vis(pos)+1;
3063 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3064 ? log2vis(pos-1)+1 : log2vis(pos-1);
3066 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3067 ? log2vis(last)+1 : log2vis(last);
3069 /* table stuff -- begin*/
3070 if (row->par->table) {
3071 int cell = NumberOfCell(row->par, row->pos);
3073 x += row->par->table->GetBeginningOfTextInCell(cell);
3074 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3075 pos = vis2log(vpos);
3076 if (row->par->IsNewline(pos)) {
3077 x = x_old + row->par->table->WidthOfColumn(cell);
3080 x += row->par->table->GetBeginningOfTextInCell(cell);
3082 x += SingleWidth(row->par, pos);
3086 /* table stuff -- end*/
3087 LyXParagraph::size_type main_body =
3088 BeginningOfMainBody(row->par);
3089 if (main_body > 0 &&
3090 (main_body-1 > last ||
3091 !row->par->IsLineSeparator(main_body-1)))
3094 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3095 pos = vis2log(vpos);
3096 if (main_body > 0 && pos == main_body-1) {
3097 x += fill_label_hfill +
3098 GetFont(row->par, -2).stringWidth(
3099 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3100 if (row->par->IsLineSeparator(main_body-1))
3101 x -= SingleWidth(row->par, main_body-1);
3104 x += SingleWidth(row->par, pos);
3105 if (HfillExpansion(row, pos)) {
3106 if (pos >= main_body)
3109 x += fill_label_hfill;
3111 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3119 cursor.x_fix = cursor.x;
3124 void LyXText::SetCursorFromCoordinates(int x, long y) const
3126 LyXCursor old_cursor = cursor;
3128 /* get the row first */
3130 Row * row = GetRowNearY(y);
3132 cursor.par = row->par;
3134 int column = GetColumnNearX(row, x);
3135 cursor.pos = row->pos + column;
3137 cursor.y = y + row->baseline;
3142 (cursor.pos == cursor.par->Last()
3143 || cursor.par->IsSeparator(cursor.pos)
3144 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3145 && !cursor.par->IsSeparator(cursor.pos))
3146 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3148 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3149 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3151 current_font = cursor.par->GetFontSettings(cursor.pos);
3152 real_current_font = GetFont(cursor.par, cursor.pos);
3154 DeleteEmptyParagraphMechanism(old_cursor);
3158 void LyXText::CursorLeft() const
3161 if (cursor.par->table) {
3162 int cell = NumberOfCell(cursor.par, cursor.pos);
3163 if (cursor.par->table->IsContRow(cell) &&
3164 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3171 void LyXText::CursorLeftIntern() const
3173 if (cursor.pos > 0) {
3174 SetCursor(cursor.par, cursor.pos - 1);
3176 else if (cursor.par->Previous()) {
3177 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3182 void LyXText::CursorRight() const
3184 CursorRightIntern();
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::CursorRightIntern() const
3197 if (cursor.pos < cursor.par->Last()) {
3198 SetCursor(cursor.par, cursor.pos + 1);
3200 else if (cursor.par->Next()) {
3201 SetCursor(cursor.par->Next(), 0);
3206 void LyXText::CursorUp() const
3208 SetCursorFromCoordinates(cursor.x_fix,
3209 cursor.y - cursor.row->baseline - 1);
3210 if (cursor.par->table) {
3211 int cell = NumberOfCell(cursor.par, cursor.pos);
3212 if (cursor.par->table->IsContRow(cell) &&
3213 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3220 void LyXText::CursorDown() const
3222 if (cursor.par->table &&
3223 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3226 SetCursorFromCoordinates(cursor.x_fix,
3227 cursor.y - cursor.row->baseline
3228 + cursor.row->height + 1);
3229 if (cursor.par->table) {
3230 int cell = NumberOfCell(cursor.par, cursor.pos);
3231 int cell_above = cursor.par->table->GetCellAbove(cell);
3232 while(cursor.par->table &&
3233 cursor.par->table->IsContRow(cell) &&
3234 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3235 SetCursorFromCoordinates(cursor.x_fix,
3236 cursor.y - cursor.row->baseline
3237 + cursor.row->height + 1);
3238 if (cursor.par->table) {
3239 cell = NumberOfCell(cursor.par, cursor.pos);
3240 cell_above = cursor.par->table->GetCellAbove(cell);
3247 void LyXText::CursorUpParagraph() const
3249 if (cursor.pos > 0) {
3250 SetCursor(cursor.par, 0);
3252 else if (cursor.par->Previous()) {
3253 SetCursor(cursor.par->Previous(), 0);
3258 void LyXText::CursorDownParagraph() const
3260 if (cursor.par->Next()) {
3261 SetCursor(cursor.par->Next(), 0);
3263 SetCursor(cursor.par, cursor.par->Last());
3269 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3271 // Would be wrong to delete anything if we have a selection.
3272 if (selection) return;
3274 // We allow all kinds of "mumbo-jumbo" when freespacing.
3275 if (textclasslist.Style(parameters->textclass,
3276 old_cursor.par->GetLayout()).free_spacing)
3279 bool deleted = false;
3281 #ifdef FIX_DOUBLE_SPACE
3282 /* Ok I'll put some comments here about what is missing.
3283 I have fixed BackSpace (and thus Delete) to not delete
3284 double-spaces automagically. I have also changed Cut,
3285 Copy and Paste to hopefully do some sensible things.
3286 There are still some small problems that can lead to
3287 double spaces stored in the document file or space at
3288 the beginning of paragraphs. This happens if you have
3289 the cursor betwenn to spaces and then save. Or if you
3290 cut and paste and the selection have a space at the
3291 beginning and then save right after the paste. I am
3292 sure none of these are very hard to fix, but I will
3293 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3294 that I can get some feedback. (Lgb)
3297 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3298 // delete the LineSeparator.
3301 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3302 // delete the LineSeparator.
3305 // If the pos around the old_cursor were spaces, delete one of them.
3306 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3307 if (old_cursor.pos > 0
3308 && old_cursor.pos < old_cursor.par->Last()
3309 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3310 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3311 old_cursor.par->Erase(old_cursor.pos - 1);
3312 //RedoParagraphs(old_cursor, old_cursor.par->Next());
3313 status = LyXText::NEED_MORE_REFRESH;
3316 //if (old_cursor.par == cursor.par &&
3317 // cursor.pos > old_cursor.pos)
3318 // SetCursor(cursor.par, cursor.pos - 1);
3320 // SetCursor(cursor.par, cursor.pos);
3326 // Do not delete empty paragraphs with keepempty set.
3327 if ((textclasslist.Style(parameters->textclass,
3328 old_cursor.par->GetLayout())).keepempty)
3331 LyXCursor tmpcursor;
3333 if (old_cursor.par != cursor.par) {
3334 if ( (old_cursor.par->Last() == 0
3335 || (old_cursor.par->Last() == 1
3336 && old_cursor.par->IsLineSeparator(0)))
3337 && old_cursor.par->FirstPhysicalPar()
3338 == old_cursor.par->LastPhysicalPar()) {
3340 // ok, we will delete anything
3342 // make sure that you do not delete any environments
3343 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3344 !(old_cursor.row->previous
3345 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3346 && !(old_cursor.row->next
3347 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3348 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3349 ((old_cursor.row->previous
3350 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3351 || (old_cursor.row->next
3352 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3354 status = LyXText::NEED_MORE_REFRESH;
3357 if (old_cursor.row->previous) {
3358 refresh_row = old_cursor.row->previous;
3359 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3361 cursor = old_cursor; // that undo can restore the right cursor position
3362 LyXParagraph * endpar = old_cursor.par->next;
3363 if (endpar && endpar->GetDepth()) {
3364 while (endpar && endpar->GetDepth()) {
3365 endpar = endpar->LastPhysicalPar()->Next();
3368 SetUndo(Undo::DELETE,
3369 old_cursor.par->previous,
3374 RemoveRow(old_cursor.row);
3375 if (params->paragraph == old_cursor.par) {
3376 params->paragraph = params->paragraph->next;
3379 delete old_cursor.par;
3381 /* Breakagain the next par. Needed
3382 * because of the parindent that
3383 * can occur or dissappear. The
3384 * next row can change its height,
3385 * if there is another layout before */
3386 if (refresh_row->next) {
3387 BreakAgain(refresh_row->next);
3388 UpdateCounters(refresh_row);
3390 SetHeightOfRow(refresh_row);
3392 refresh_row = old_cursor.row->next;
3393 refresh_y = old_cursor.y - old_cursor.row->baseline;
3396 cursor = old_cursor; // that undo can restore the right cursor position
3397 LyXParagraph *endpar = old_cursor.par->next;
3398 if (endpar && endpar->GetDepth()) {
3399 while (endpar && endpar->GetDepth()) {
3400 endpar = endpar->LastPhysicalPar()->Next();
3403 SetUndo(Undo::DELETE,
3404 old_cursor.par->previous,
3409 RemoveRow(old_cursor.row);
3411 if (params->paragraph == old_cursor.par) {
3412 params->paragraph = params->paragraph->next;
3414 delete old_cursor.par;
3416 /* Breakagain the next par. Needed
3417 because of the parindent that can
3418 occur or dissappear.
3419 The next row can change its height,
3420 if there is another layout before
3423 BreakAgain(refresh_row);
3424 UpdateCounters(refresh_row->previous);
3429 SetCursor(cursor.par, cursor.pos);
3431 /* if (cursor.y > old_cursor.y)
3432 cursor.y -= old_cursor.row->height; */
3434 if (sel_cursor.par == old_cursor.par
3435 && sel_cursor.pos == sel_cursor.pos) {
3436 // correct selection
3437 sel_cursor = cursor;
3442 if (old_cursor.par->ClearParagraph()){
3443 RedoParagraphs(old_cursor, old_cursor.par->Next());
3445 SetCursor(cursor.par, cursor.pos);
3446 sel_cursor = cursor;
3454 LyXParagraph * LyXText::GetParFromID(int id)
3456 LyXParagraph * result = FirstParagraph();
3457 while (result && result->id() != id)
3458 result = result->next;
3464 bool LyXText::TextUndo()
3466 // returns false if no undo possible
3467 Undo * undo = params->undostack.pop();
3472 .push(CreateUndo(undo->kind,
3473 GetParFromID(undo->number_of_before_par),
3474 GetParFromID(undo->number_of_behind_par)));
3476 return TextHandleUndo(undo);
3480 bool LyXText::TextRedo()
3482 // returns false if no redo possible
3483 Undo * undo = params->redostack.pop();
3488 .push(CreateUndo(undo->kind,
3489 GetParFromID(undo->number_of_before_par),
3490 GetParFromID(undo->number_of_behind_par)));
3492 return TextHandleUndo(undo);
3496 bool LyXText::TextHandleUndo(Undo * undo)
3498 // returns false if no undo possible
3499 bool result = false;
3501 LyXParagraph * before =
3502 GetParFromID(undo->number_of_before_par);
3503 LyXParagraph * behind =
3504 GetParFromID(undo->number_of_behind_par);
3505 LyXParagraph * tmppar;
3506 LyXParagraph * tmppar2;
3507 LyXParagraph * endpar;
3508 LyXParagraph * tmppar5;
3510 // if there's no before take the beginning
3511 // of the document for redoing
3513 SetCursorIntern(FirstParagraph(), 0);
3515 // replace the paragraphs with the undo informations
3517 LyXParagraph * tmppar3 = undo->par;
3518 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3519 LyXParagraph * tmppar4 = tmppar3;
3521 while (tmppar4->next)
3522 tmppar4 = tmppar4->next;
3523 } // get last undo par
3525 // now remove the old text if there is any
3526 if (before != behind || (!behind && !before)){
3528 tmppar5 = before->next;
3530 tmppar5 = params->paragraph;
3532 while (tmppar5 && tmppar5 != behind){
3534 tmppar5 = tmppar5->next;
3535 // a memory optimization for edit: Only layout information
3536 // is stored in the undo. So restore the text informations.
3537 if (undo->kind == Undo::EDIT) {
3538 tmppar2->setContentsFromPar(tmppar);
3539 tmppar->clearContents();
3540 //tmppar2->text = tmppar->text;
3541 //tmppar->text.clear();
3542 tmppar2 = tmppar2->next;
3544 if ( currentrow && currentrow->par == tmppar )
3545 currentrow = currentrow -> previous;
3546 // Commenting out this might remove the error
3547 // reported by Purify, but it might also
3548 // introduce a memory leak. We need to
3554 // put the new stuff in the list if there is one
3557 before->next = tmppar3;
3559 params->paragraph = tmppar3;
3560 tmppar3->previous = before;
3564 params->paragraph = behind;
3567 tmppar4->next = behind;
3569 behind->previous = tmppar4;
3573 // Set the cursor for redoing
3575 SetCursorIntern(before->FirstSelfrowPar(), 0);
3576 // check wether before points to a closed float and open it if necessary
3577 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3578 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3580 while (tmppar4->previous &&
3581 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3582 tmppar4 = tmppar4->previous;
3583 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3584 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3585 tmppar4 = tmppar4->next;
3590 // open a cosed footnote at the end if necessary
3591 if (behind && behind->previous &&
3592 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3593 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3594 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3595 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3596 behind = behind->next;
3600 // calculate the endpar for redoing the paragraphs.
3602 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3603 endpar = behind->LastPhysicalPar()->Next();
3605 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3610 tmppar = GetParFromID(undo->number_of_cursor_par);
3611 RedoParagraphs(cursor, endpar);
3613 SetCursorIntern(tmppar, undo->cursor_pos);
3614 UpdateCounters(cursor.row);
3624 void LyXText::FinishUndo()
3626 // makes sure the next operation will be stored
3627 undo_finished = True;
3631 void LyXText::FreezeUndo()
3633 // this is dangerous and for internal use only
3638 void LyXText::UnFreezeUndo()
3640 // this is dangerous and for internal use only
3641 undo_frozen = false;
3645 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3646 LyXParagraph const * behind) const
3649 params->undostack.push(CreateUndo(kind, before, behind));
3650 params->redostack.clear();
3654 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3655 LyXParagraph const * behind)
3657 params->redostack.push(CreateUndo(kind, before, behind));
3661 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3662 LyXParagraph const * behind) const
3664 int before_number = -1;
3665 int behind_number = -1;
3667 before_number = before->id();
3669 behind_number = behind->id();
3670 // Undo::EDIT and Undo::FINISH are
3671 // always finished. (no overlapping there)
3672 // overlapping only with insert and delete inside one paragraph:
3673 // Nobody wants all removed character
3674 // appear one by one when undoing.
3675 // EDIT is special since only layout information, not the
3676 // contents of a paragaph are stored.
3677 if (!undo_finished && kind != Undo::EDIT &&
3678 kind != Undo::FINISH){
3679 // check wether storing is needed
3680 if (!params->undostack.empty() &&
3681 params->undostack.top()->kind == kind &&
3682 params->undostack.top()->number_of_before_par == before_number &&
3683 params->undostack.top()->number_of_behind_par == behind_number ){
3688 // create a new Undo
3689 LyXParagraph * undopar;
3690 LyXParagraph * tmppar;
3691 LyXParagraph * tmppar2;
3693 LyXParagraph * start = 0;
3694 LyXParagraph * end = 0;
3697 start = before->next;
3699 start = FirstParagraph();
3701 end = behind->previous;
3703 end = FirstParagraph();
3709 && start != end->next
3710 && (before != behind || (!before && !behind))) {
3712 tmppar2 = tmppar->Clone();
3713 tmppar2->id(tmppar->id());
3715 // a memory optimization: Just store the layout information
3717 if (kind == Undo::EDIT){
3718 //tmppar2->text.clear();
3719 tmppar2->clearContents();
3724 while (tmppar != end && tmppar->next) {
3725 tmppar = tmppar->next;
3726 tmppar2->next = tmppar->Clone();
3727 tmppar2->next->id(tmppar->id());
3728 // a memory optimization: Just store the layout
3729 // information when only edit
3730 if (kind == Undo::EDIT){
3731 //tmppar2->next->text.clear();
3732 tmppar2->clearContents();
3734 tmppar2->next->previous = tmppar2;
3735 tmppar2 = tmppar2->next;
3739 undopar = 0; // nothing to replace (undo of delete maybe)
3741 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3742 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3744 Undo * undo = new Undo(kind,
3745 before_number, behind_number,
3746 cursor_par, cursor_pos,
3749 undo_finished = false;
3754 void LyXText::SetCursorParUndo()
3756 SetUndo(Undo::FINISH,
3757 cursor.par->ParFromPos(cursor.pos)->previous,
3758 cursor.par->ParFromPos(cursor.pos)->next);
3762 void LyXText::RemoveTableRow(LyXCursor * cur) const
3768 // move to the previous row
3769 int cell_act = NumberOfCell(cur->par, cur->pos);
3772 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3775 !cur->par->table->IsFirstCell(cell_act)) {
3777 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3782 // now we have to pay attention if the actual table is the
3783 // main row of TableContRows and if yes to delete all of them
3788 // delete up to the next row
3789 while (cur->pos < cur->par->Last() &&
3791 || !cur->par->table->IsFirstCell(cell_act))) {
3792 while (cur->pos < cur->par->Last() &&
3793 !cur->par->IsNewline(cur->pos))
3794 cur->par->Erase(cur->pos);
3797 if (cur->pos < cur->par->Last())
3798 cur->par->Erase(cur->pos);
3800 if (cur->pos && cur->pos == cur->par->Last()) {
3802 cur->par->Erase(cur->pos); // no newline at very end!
3804 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3805 !cur->par->table->IsContRow(cell_org) &&
3806 cur->par->table->IsContRow(cell));
3807 cur->par->table->DeleteRow(cell_org);
3812 bool LyXText::IsEmptyTableCell() const
3814 LyXParagraph::size_type pos = cursor.pos - 1;
3815 while (pos >= 0 && pos < cursor.par->Last()
3816 && !cursor.par->IsNewline(pos))
3818 return cursor.par->IsNewline(pos + 1);
3822 void LyXText::toggleAppendix(){
3823 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3824 bool start = !par->start_of_appendix;
3826 // ensure that we have only one start_of_appendix in this document
3827 LyXParagraph * tmp = FirstParagraph();
3828 for (; tmp; tmp = tmp->next)
3829 tmp->start_of_appendix = 0;
3830 par->start_of_appendix = start;
3832 // we can set the refreshing parameters now
3833 status = LyXText::NEED_MORE_REFRESH;
3835 refresh_row = 0; // not needed for full update
3837 SetCursor(cursor.par, cursor.pos);