1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
39 #define FIX_DOUBLE_SPACE 1
43 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
51 parameters = &p->params;
55 status = LyXText::UNCHANGED;
56 LyXParagraph * par = p->paragraph;
57 current_font = GetFont(par, 0);
62 InsertParagraph(par, lastrow);
66 // set cursor at the very top position
67 selection = true; /* these setting is necessary
68 because of the delete-empty-
69 paragraph mechanism in
71 SetCursor(firstrow->par, 0);
76 // no rebreak necessary
82 // Default layouttype for copy environment type
89 // Delete all rows, this does not touch the paragraphs!
90 Row * tmprow = firstrow;
92 tmprow = firstrow->next;
99 void LyXText::owner(BufferView * bv)
101 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
105 // Gets the fully instantiated font at a given position in a paragraph
106 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
107 // The difference is that this one is used for displaying, and thus we
108 // are allowed to make cosmetic improvements. For instance make footnotes
110 // If position is -1, we get the layout font of the paragraph.
111 // If position is -2, we get the font of the manual label of the paragraph.
112 LyXFont LyXText::GetFont(LyXParagraph * par,
113 LyXParagraph::size_type pos) const
115 LyXLayout const & layout =
116 textclasslist.Style(parameters->textclass, par->GetLayout());
118 char par_depth = par->GetDepth();
119 // We specialize the 95% common case:
120 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
123 if (layout.labeltype == LABEL_MANUAL
124 && pos < BeginningOfMainBody(par)) {
126 return par->GetFontSettings(pos).
127 realize(layout.reslabelfont);
129 return par->GetFontSettings(pos).
130 realize(layout.resfont);
133 // process layoutfont for pos == -1 and labelfont for pos < -1
135 return layout.resfont;
137 return layout.reslabelfont;
141 // The uncommon case need not be optimized as much
143 LyXFont layoutfont, tmpfont;
147 if (pos < BeginningOfMainBody(par)) {
149 layoutfont = layout.labelfont;
152 layoutfont = layout.font;
154 tmpfont = par->GetFontSettings(pos);
155 tmpfont.realize(layoutfont);
158 // process layoutfont for pos == -1 and labelfont for pos < -1
160 tmpfont = layout.font;
162 tmpfont = layout.labelfont;
165 // Resolve against environment font information
166 while (par && par_depth && !tmpfont.resolved()) {
167 par = par->DepthHook(par_depth - 1);
169 tmpfont.realize(textclasslist.
170 Style(parameters->textclass,
171 par->GetLayout()).font);
172 par_depth = par->GetDepth();
176 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
178 // Cosmetic improvement: If this is an open footnote, make the font
180 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
181 && par->footnotekind == LyXParagraph::FOOTNOTE) {
189 void LyXText::SetCharFont(LyXParagraph * par,
190 LyXParagraph::size_type pos,
194 // Let the insets convert their font
195 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
196 if (par->GetInset(pos))
197 font = par->GetInset(pos)->ConvertFont(font);
200 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
203 // Get concrete layout font to reduce against
206 if (pos < BeginningOfMainBody(par))
207 layoutfont = layout.labelfont;
209 layoutfont = layout.font;
211 // Realize against environment font information
212 if (par->GetDepth()){
213 LyXParagraph * tp = par;
214 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
215 tp = tp->DepthHook(tp->GetDepth()-1);
217 layoutfont.realize(textclasslist.
218 Style(parameters->textclass,
219 tp->GetLayout()).font);
223 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
225 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
226 && par->footnotekind == LyXParagraph::FOOTNOTE) {
227 layoutfont.decSize();
230 // Now, reduce font against full layout font
231 font.reduce(layoutfont);
233 par->SetFont(pos, font);
237 /* inserts a new row behind the specified row, increments
238 * the touched counters */
239 void LyXText::InsertRow(Row * row, LyXParagraph * par,
240 LyXParagraph::size_type pos) const
242 Row * tmprow = new Row;
244 tmprow->previous = 0;
245 tmprow->next = firstrow;
248 tmprow->previous = row;
249 tmprow->next = row->next;
254 tmprow->next->previous = tmprow;
256 if (tmprow->previous)
257 tmprow->previous->next = tmprow;
265 ++number_of_rows; // one more row
269 // removes the row and reset the touched counters
270 void LyXText::RemoveRow(Row * row) const
272 /* this must not happen before the currentrow for clear reasons.
273 so the trick is just to set the current row onto the previous
276 GetRow(row->par, row->pos, unused_y);
277 currentrow = currentrow->previous;
279 currentrow_y -= currentrow->height;
284 row->next->previous = row->previous;
285 if (!row->previous) {
286 firstrow = row->next;
288 row->previous->next = row->next;
291 lastrow = row->previous;
293 height -= row->height; // the text becomes smaller
296 --number_of_rows; // one row less
300 // remove all following rows of the paragraph of the specified row.
301 void LyXText::RemoveParagraph(Row * row) const
303 LyXParagraph * tmppar = row->par;
307 while (row && row->par == tmppar) {
315 // insert the specified paragraph behind the specified row
316 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
318 InsertRow(row, par, 0); /* insert a new row, starting
321 SetCounter(par); // set the counters
323 // and now append the whole paragraph behind the new row
325 firstrow->height = 0;
326 AppendParagraph(firstrow);
328 row->next->height = 0;
329 AppendParagraph(row->next);
334 void LyXText::ToggleFootnote()
336 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
338 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
340 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
342 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
348 void LyXText::OpenStuff()
350 if (cursor.pos == 0 && cursor.par->bibkey){
351 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
353 else if (cursor.pos < cursor.par->Last()
354 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
355 && cursor.par->GetInset(cursor.pos)->Editable()) {
356 owner_->owner()->getMiniBuffer()
357 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
358 if (cursor.par->GetInset(cursor.pos)->Editable() != 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)->text.size());
395 /* we are in a footnote, so let us move at the beginning */
396 /* this is just faster than using just CursorLeft() */
399 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
400 // just a little bit faster than movin the cursor
401 tmppar = tmppar->Previous();
403 SetCursor(tmppar, tmppar->Last());
406 // the cursor must be exactly before the footnote
407 par = cursor.par->ParFromPos(cursor.pos);
409 status = LyXText::NEED_MORE_REFRESH;
410 refresh_row = cursor.row;
411 refresh_y = cursor.y - cursor.row->baseline;
414 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
415 Row * row = cursor.row;
417 tmppar->CloseFootnote(cursor.pos);
419 while (tmppar != endpar) {
420 RemoveRow(row->next);
422 tmppar = row->next->par;
427 AppendParagraph(cursor.row);
429 SetCursor(cursor.par, cursor.pos);
433 if (cursor.row->next)
434 SetHeightOfRow(cursor.row->next);
438 /* used in setlayout */
439 // Asger is not sure we want to do this...
440 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
443 LyXLayout const & layout =
444 textclasslist.Style(parameters->textclass, par->GetLayout());
446 LyXFont layoutfont, tmpfont;
447 for (LyXParagraph::size_type pos = 0;
448 pos < par->Last(); ++pos) {
449 if (pos < BeginningOfMainBody(par))
450 layoutfont = layout.labelfont;
452 layoutfont = layout.font;
454 tmpfont = par->GetFontSettings(pos);
455 tmpfont.reduce(layoutfont);
456 par->SetFont(pos, tmpfont);
461 // set layout over selection and make a total rebreak of those paragraphs
462 void LyXText::SetLayout(LyXTextClass::size_type layout)
466 // if there is no selection just set the layout
467 // of the current paragraph */
469 sel_start_cursor = cursor; // dummy selection
470 sel_end_cursor = cursor;
473 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
474 LyXParagraph * undoendpar = endpar;
476 if (endpar && endpar->GetDepth()) {
477 while (endpar && endpar->GetDepth()) {
478 endpar = endpar->LastPhysicalPar()->Next();
483 endpar = endpar->Next(); // because of parindents etc.
487 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
490 tmpcursor = cursor; /* store the current cursor */
492 /* ok we have a selection. This is always between sel_start_cursor
493 * and sel_end cursor */
494 cursor = sel_start_cursor;
496 LyXLayout const & lyxlayout =
497 textclasslist.Style(parameters->textclass, layout);
499 while (cursor.par != sel_end_cursor.par) {
500 if (cursor.par->footnoteflag ==
501 sel_start_cursor.par->footnoteflag) {
502 cursor.par->SetLayout(layout);
503 MakeFontEntriesLayoutSpecific(cursor.par);
504 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
505 fppar->added_space_top = lyxlayout.fill_top ?
506 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
507 fppar->added_space_bottom = lyxlayout.fill_bottom ?
508 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
509 if (lyxlayout.margintype == MARGIN_MANUAL)
510 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
511 if (lyxlayout.labeltype != LABEL_BIBLIO
513 delete fppar->bibkey;
517 cursor.par = cursor.par->Next();
519 if (cursor.par->footnoteflag ==
520 sel_start_cursor.par->footnoteflag) {
521 cursor.par->SetLayout(layout);
522 MakeFontEntriesLayoutSpecific(cursor.par);
523 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
524 fppar->added_space_top = lyxlayout.fill_top ?
525 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
526 fppar->added_space_bottom = lyxlayout.fill_bottom ?
527 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
528 if (lyxlayout.margintype == MARGIN_MANUAL)
529 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
530 if (lyxlayout.labeltype != LABEL_BIBLIO
532 delete fppar->bibkey;
537 RedoParagraphs(sel_start_cursor, endpar);
539 // we have to reset the selection, because the
540 // geometry could have changed */
541 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
543 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
544 UpdateCounters(cursor.row);
547 SetCursor(tmpcursor.par, tmpcursor.pos);
551 // increment depth over selection and
552 // make a total rebreak of those paragraphs
553 void LyXText::IncDepth()
555 // If there is no selection, just use the current paragraph
557 sel_start_cursor = cursor; // dummy selection
558 sel_end_cursor = cursor;
561 // We end at the next paragraph with depth 0
562 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
563 LyXParagraph * undoendpar = endpar;
565 if (endpar && endpar->GetDepth()) {
566 while (endpar && endpar->GetDepth()) {
567 endpar = endpar->LastPhysicalPar()->Next();
572 endpar = endpar->Next(); // because of parindents etc.
577 .par->ParFromPos(sel_start_cursor.pos)->previous,
580 LyXCursor tmpcursor = cursor; // store the current cursor
582 // ok we have a selection. This is always between sel_start_cursor
583 // and sel_end cursor
584 cursor = sel_start_cursor;
586 bool anything_changed = false;
589 // NOTE: you can't change the depth of a bibliography entry
590 if (cursor.par->footnoteflag ==
591 sel_start_cursor.par->footnoteflag
592 && textclasslist.Style(parameters->textclass,
593 cursor.par->GetLayout()
594 ).labeltype != LABEL_BIBLIO) {
595 LyXParagraph * prev =
596 cursor.par->FirstPhysicalPar()->Previous();
598 && (prev->GetDepth() - cursor.par->GetDepth() > 0
599 || (prev->GetDepth() == cursor.par->GetDepth()
600 && textclasslist.Style(parameters->textclass,
601 prev->GetLayout()).isEnvironment()))) {
602 cursor.par->FirstPhysicalPar()->depth++;
603 anything_changed = true;
606 if (cursor.par == sel_end_cursor.par)
608 cursor.par = cursor.par->Next();
611 // if nothing changed set all depth to 0
612 if (!anything_changed) {
613 cursor = sel_start_cursor;
614 while (cursor.par != sel_end_cursor.par) {
615 cursor.par->FirstPhysicalPar()->depth = 0;
616 cursor.par = cursor.par->Next();
618 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
619 cursor.par->FirstPhysicalPar()->depth = 0;
622 RedoParagraphs(sel_start_cursor, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
628 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
629 UpdateCounters(cursor.row);
632 SetCursor(tmpcursor.par, tmpcursor.pos);
636 // decrement depth over selection and
637 // make a total rebreak of those paragraphs
638 void LyXText::DecDepth()
640 // if there is no selection just set the layout
641 // of the current paragraph
643 sel_start_cursor = cursor; // dummy selection
644 sel_end_cursor = cursor;
647 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
648 LyXParagraph * undoendpar = endpar;
650 if (endpar && endpar->GetDepth()) {
651 while (endpar && endpar->GetDepth()) {
652 endpar = endpar->LastPhysicalPar()->Next();
657 endpar = endpar->Next(); // because of parindents etc.
662 .par->ParFromPos(sel_start_cursor.pos)->previous,
665 LyXCursor tmpcursor = cursor; // store the current cursor
667 // ok we have a selection. This is always between sel_start_cursor
668 // and sel_end cursor
669 cursor = sel_start_cursor;
672 if (cursor.par->footnoteflag ==
673 sel_start_cursor.par->footnoteflag) {
674 if (cursor.par->FirstPhysicalPar()->depth)
675 cursor.par->FirstPhysicalPar()->depth--;
677 if (cursor.par == sel_end_cursor.par)
679 cursor.par = cursor.par->Next();
682 RedoParagraphs(sel_start_cursor, endpar);
684 // we have to reset the selection, because the
685 // geometry could have changed
686 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
688 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
689 UpdateCounters(cursor.row);
692 SetCursor(tmpcursor.par, tmpcursor.pos);
696 // set font over selection and make a total rebreak of those paragraphs
697 void LyXText::SetFont(LyXFont const & font, bool toggleall)
699 // if there is no selection just set the current_font
701 // Determine basis font
703 if (cursor.pos < BeginningOfMainBody(cursor.par))
704 layoutfont = GetFont(cursor.par, -2);
706 layoutfont = GetFont(cursor.par, -1);
707 // Update current font
708 real_current_font.update(font, toggleall);
710 // Reduce to implicit settings
711 current_font = real_current_font;
712 current_font.reduce(layoutfont);
713 // And resolve it completely
714 real_current_font.realize(layoutfont);
718 LyXCursor tmpcursor = cursor; // store the current cursor
720 // ok we have a selection. This is always between sel_start_cursor
721 // and sel_end cursor
724 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
725 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
726 cursor = sel_start_cursor;
727 while (cursor.par != sel_end_cursor.par ||
728 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
729 && cursor.pos < sel_end_cursor.pos))
731 if (cursor.pos < cursor.par->Last()
732 && cursor.par->footnoteflag
733 == sel_start_cursor.par->footnoteflag) {
734 // an open footnote should behave
736 LyXFont newfont = GetFont(cursor.par, cursor.pos);
737 newfont.update(font, toggleall);
738 SetCharFont(cursor.par, cursor.pos, newfont);
742 cursor.par = cursor.par->Next();
746 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
748 // we have to reset the selection, because the
749 // geometry could have changed
750 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
752 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
755 SetCursor(tmpcursor.par, tmpcursor.pos);
759 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
761 Row * tmprow = cur.row;
762 long y = cur.y - tmprow->baseline;
764 SetHeightOfRow(tmprow);
765 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
766 // find the first row of the paragraph
767 if (first_phys_par != tmprow->par)
768 while (tmprow->previous
769 && tmprow->previous->par != first_phys_par) {
770 tmprow = tmprow->previous;
772 SetHeightOfRow(tmprow);
774 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
775 tmprow = tmprow->previous;
777 SetHeightOfRow(tmprow);
780 // we can set the refreshing parameters now
781 status = LyXText::NEED_MORE_REFRESH;
783 refresh_row = tmprow;
784 SetCursor(cur.par, cur.pos);
788 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
790 Row * tmprow = cur.row;
792 long y = cur.y - tmprow->baseline;
793 SetHeightOfRow(tmprow);
794 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
795 // find the first row of the paragraph
796 if (first_phys_par != tmprow->par)
797 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
798 tmprow = tmprow->previous;
801 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
802 tmprow = tmprow->previous;
806 // we can set the refreshing parameters now
807 if (status == LyXText::UNCHANGED || y < refresh_y) {
809 refresh_row = tmprow;
811 status = LyXText::NEED_MORE_REFRESH;
812 SetCursor(cur.par, cur.pos);
816 /* deletes and inserts again all paragaphs between the cursor
817 * and the specified par
818 * This function is needed after SetLayout and SetFont etc. */
819 void LyXText::RedoParagraphs(LyXCursor const & cur,
820 LyXParagraph const * endpar) const
823 LyXParagraph * tmppar, * first_phys_par;
825 Row * tmprow = cur.row;
827 long y = cur.y - tmprow->baseline;
829 if (!tmprow->previous){
830 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
832 first_phys_par = tmprow->par->FirstPhysicalPar();
833 // find the first row of the paragraph
834 if (first_phys_par != tmprow->par)
835 while (tmprow->previous
836 && tmprow->previous->par != first_phys_par) {
837 tmprow = tmprow->previous;
840 while (tmprow->previous
841 && tmprow->previous->par == first_phys_par) {
842 tmprow = tmprow->previous;
847 // we can set the refreshing parameters now
848 status = LyXText::NEED_MORE_REFRESH;
850 refresh_row = tmprow->previous; /* the real refresh row will
851 be deleted, so I store
855 tmppar = tmprow->next->par;
858 while (tmppar != endpar) {
859 RemoveRow(tmprow->next);
861 tmppar = tmprow->next->par;
866 // remove the first one
867 tmprow2 = tmprow; /* this is because tmprow->previous
869 tmprow = tmprow->previous;
872 tmppar = first_phys_par;
876 InsertParagraph(tmppar, tmprow);
879 while (tmprow->next && tmprow->next->par == tmppar)
880 tmprow = tmprow->next;
881 tmppar = tmppar->Next();
883 } while (tmppar != endpar);
885 // this is because of layout changes
887 refresh_y -= refresh_row->height;
888 SetHeightOfRow(refresh_row);
890 refresh_row = firstrow;
892 SetHeightOfRow(refresh_row);
895 if (tmprow && tmprow->next)
896 SetHeightOfRow(tmprow->next);
900 int LyXText::FullRebreak()
902 if (need_break_row) {
903 BreakAgain(need_break_row);
911 /* important for the screen */
914 /* the cursor set functions have a special mechanism. When they
915 * realize, that you left an empty paragraph, they will delete it.
916 * They also delet the corresponding row */
918 // need the selection cursor:
919 void LyXText::SetSelection()
922 last_sel_cursor = sel_cursor;
923 sel_start_cursor = sel_cursor;
924 sel_end_cursor = sel_cursor;
929 // first the toggling area
930 if (cursor.y < last_sel_cursor.y ||
931 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
932 toggle_end_cursor = last_sel_cursor;
933 toggle_cursor = cursor;
936 toggle_end_cursor = cursor;
937 toggle_cursor = last_sel_cursor;
940 last_sel_cursor = cursor;
942 // and now the whole selection
944 if (sel_cursor.par == cursor.par)
945 if (sel_cursor.pos < cursor.pos) {
946 sel_end_cursor = cursor;
947 sel_start_cursor = sel_cursor;
949 sel_end_cursor = sel_cursor;
950 sel_start_cursor = cursor;
952 else if (sel_cursor.y < cursor.y ||
953 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
954 sel_end_cursor = cursor;
955 sel_start_cursor = sel_cursor;
958 sel_end_cursor = sel_cursor;
959 sel_start_cursor = cursor;
962 // a selection with no contents is not a selection
963 if (sel_start_cursor.x == sel_end_cursor.x &&
964 sel_start_cursor.y == sel_end_cursor.y)
969 void LyXText::ClearSelection() const
976 void LyXText::CursorHome() const
978 SetCursor(cursor.par, cursor.row->pos);
982 void LyXText::CursorEnd() const
984 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
985 SetCursor(cursor.par, RowLast(cursor.row) + 1);
987 if (cursor.par->Last() &&
988 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
989 || cursor.par->IsNewline(RowLast(cursor.row))))
990 SetCursor(cursor.par, RowLast(cursor.row));
992 SetCursor(cursor.par, RowLast(cursor.row) + 1);
994 if (cursor.par->table) {
995 int cell = NumberOfCell(cursor.par, cursor.pos);
996 if (cursor.par->table->RowHasContRow(cell) &&
997 cursor.par->table->CellHasContRow(cell)<0) {
998 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
999 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1001 if (cursor.par->Last() &&
1002 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1003 || cursor.par->IsNewline(RowLast(cursor.row))))
1004 SetCursor(cursor.par, RowLast(cursor.row));
1006 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1013 void LyXText::CursorTop() const
1015 while (cursor.par->Previous())
1016 cursor.par = cursor.par->Previous();
1017 SetCursor(cursor.par, 0);
1021 void LyXText::CursorBottom() const
1023 while (cursor.par->Next())
1024 cursor.par = cursor.par->Next();
1025 SetCursor(cursor.par, cursor.par->Last());
1029 /* returns a pointer to the row near the specified y-coordinate
1030 * (relative to the whole text). y is set to the real beginning
1032 Row * LyXText::GetRowNearY(long & y) const
1038 tmprow = currentrow;
1039 tmpy = currentrow_y;
1046 while (tmprow->next && tmpy + tmprow->height <= y) {
1047 tmpy += tmprow->height;
1048 tmprow = tmprow->next;
1051 while (tmprow->previous && tmpy > y) {
1052 tmprow = tmprow->previous;
1053 tmpy -= tmprow->height;
1056 currentrow = tmprow;
1057 currentrow_y = tmpy;
1059 y = tmpy; // return the real y
1064 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1066 // If the mask is completely neutral, tell user
1067 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1068 // Could only happen with user style
1069 owner_->owner()->getMiniBuffer()
1070 ->Set(_("No font change defined. Use Character under"
1071 " the Layout menu to define font change."));
1075 // Try implicit word selection
1076 LyXCursor resetCursor = cursor;
1077 int implicitSelection = SelectWordWhenUnderCursor();
1080 SetFont(font, toggleall);
1082 /* Implicit selections are cleared afterwards and cursor is set to the
1083 original position. */
1084 if (implicitSelection) {
1086 cursor = resetCursor;
1087 SetCursor( cursor.par, cursor.pos );
1088 sel_cursor = cursor;
1093 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1095 if (textclasslist.Style(parameters->textclass,
1096 par->GetLayout()).labeltype != LABEL_MANUAL)
1099 return par->BeginningOfMainBody();
1103 /* if there is a selection, reset every environment you can find
1104 * in the selection, otherwise just the environment you are in */
1105 void LyXText::MeltFootnoteEnvironment()
1107 LyXParagraph * tmppar, * firsttmppar;
1111 /* is is only allowed, if the cursor is IN an open footnote.
1112 * Otherwise it is too dangerous */
1113 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1116 SetUndo(Undo::FINISH,
1117 cursor.par->PreviousBeforeFootnote()->previous,
1118 cursor.par->NextAfterFootnote()->next);
1120 /* ok, move to the beginning of the footnote. */
1121 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1122 cursor.par = cursor.par->Previous();
1124 SetCursor(cursor.par, cursor.par->Last());
1125 /* this is just faster than using CursorLeft(); */
1127 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1128 tmppar = firsttmppar;
1129 /* tmppar is now the paragraph right before the footnote */
1131 char first_footnote_par_is_not_empty = tmppar->next->text.size();
1134 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1135 tmppar = tmppar->next; /* I use next instead of Next(),
1136 * because there cannot be any
1137 * footnotes in a footnote
1139 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1141 /* remember the captions and empty paragraphs */
1142 if ((textclasslist.Style(parameters->textclass,
1143 tmppar->GetLayout())
1144 .labeltype == LABEL_SENSITIVE)
1146 tmppar->SetLayout(0);
1149 // now we will paste the ex-footnote, if the layouts allow it
1150 // first restore the layout of the paragraph right behind
1153 tmppar->next->MakeSameLayout(cursor.par);
1156 if ((!tmppar->GetLayout() && !tmppar->table)
1158 && (!tmppar->Next()->Last()
1159 || tmppar->Next()->HasSameLayout(tmppar)))) {
1160 if (tmppar->Next()->Last()
1161 && tmppar->Next()->IsLineSeparator(0))
1162 tmppar->Next()->Erase(0);
1163 tmppar->PasteParagraph();
1166 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1167 * by the pasting of the beginning */
1169 /* then the beginning */
1170 /* if there is no space between the text and the footnote, so we insert
1172 * (only if the previous par and the footnotepar are not empty!) */
1173 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1174 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1175 if (firsttmppar->text.size()
1176 && !firsttmppar->IsSeparator(firsttmppar->text.size() - 1)
1177 && first_footnote_par_is_not_empty) {
1178 firsttmppar->next->InsertChar(0, ' ');
1180 firsttmppar->PasteParagraph();
1183 /* now redo the paragaphs */
1184 RedoParagraphs(cursor, tmppar);
1186 SetCursor(cursor.par, cursor.pos);
1188 /* sometimes it can happen, that there is a counter change */
1189 Row * row = cursor.row;
1190 while (row->next && row->par != tmppar && row->next->par != tmppar)
1192 UpdateCounters(row);
1199 /* the DTP switches for paragraphs. LyX will store them in the
1200 * first physicla paragraph. When a paragraph is broken, the top settings
1201 * rest, the bottom settings are given to the new one. So I can make shure,
1202 * they do not duplicate themself and you cannnot make dirty things with
1205 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1206 bool pagebreak_top, bool pagebreak_bottom,
1207 VSpace const & space_top,
1208 VSpace const & space_bottom,
1210 string labelwidthstring,
1213 LyXCursor tmpcursor = cursor;
1215 sel_start_cursor = cursor;
1216 sel_end_cursor = cursor;
1219 // make sure that the depth behind the selection are restored, too
1220 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1221 LyXParagraph * undoendpar = endpar;
1223 if (endpar && endpar->GetDepth()) {
1224 while (endpar && endpar->GetDepth()) {
1225 endpar = endpar->LastPhysicalPar()->Next();
1226 undoendpar = endpar;
1230 endpar = endpar->Next(); // because of parindents etc.
1235 .par->ParFromPos(sel_start_cursor.pos)->previous,
1239 LyXParagraph * tmppar = sel_end_cursor.par;
1240 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1241 SetCursor(tmppar->FirstPhysicalPar(), 0);
1242 status = LyXText::NEED_MORE_REFRESH;
1243 refresh_row = cursor.row;
1244 refresh_y = cursor.y - cursor.row->baseline;
1245 if (cursor.par->footnoteflag ==
1246 sel_start_cursor.par->footnoteflag) {
1247 cursor.par->line_top = line_top;
1248 cursor.par->line_bottom = line_bottom;
1249 cursor.par->pagebreak_top = pagebreak_top;
1250 cursor.par->pagebreak_bottom = pagebreak_bottom;
1251 cursor.par->added_space_top = space_top;
1252 cursor.par->added_space_bottom = space_bottom;
1253 // does the layout allow the new alignment?
1254 if (align == LYX_ALIGN_LAYOUT)
1255 align = textclasslist
1256 .Style(parameters->textclass,
1257 cursor.par->GetLayout()).align;
1258 if (align & textclasslist
1259 .Style(parameters->textclass,
1260 cursor.par->GetLayout()).alignpossible) {
1261 if (align == textclasslist
1262 .Style(parameters->textclass,
1263 cursor.par->GetLayout()).align)
1264 cursor.par->align = LYX_ALIGN_LAYOUT;
1266 cursor.par->align = align;
1268 cursor.par->SetLabelWidthString(labelwidthstring);
1269 cursor.par->noindent = noindent;
1272 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1275 RedoParagraphs(sel_start_cursor, endpar);
1278 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1279 sel_cursor = cursor;
1280 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1282 SetCursor(tmpcursor.par, tmpcursor.pos);
1286 void LyXText::SetParagraphExtraOpt(int type,
1288 char const * widthp,
1289 int alignment, bool hfill,
1290 bool start_minipage)
1292 LyXCursor tmpcursor = cursor;
1293 LyXParagraph * tmppar;
1295 sel_start_cursor = cursor;
1296 sel_end_cursor = cursor;
1299 // make sure that the depth behind the selection are restored, too
1300 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1301 LyXParagraph * undoendpar = endpar;
1303 if (endpar && endpar->GetDepth()) {
1304 while (endpar && endpar->GetDepth()) {
1305 endpar = endpar->LastPhysicalPar()->Next();
1306 undoendpar = endpar;
1310 endpar = endpar->Next(); // because of parindents etc.
1315 .par->ParFromPos(sel_start_cursor.pos)->previous,
1318 tmppar = sel_end_cursor.par;
1319 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1320 SetCursor(tmppar->FirstPhysicalPar(), 0);
1321 status = LyXText::NEED_MORE_REFRESH;
1322 refresh_row = cursor.row;
1323 refresh_y = cursor.y - cursor.row->baseline;
1324 if (cursor.par->footnoteflag ==
1325 sel_start_cursor.par->footnoteflag) {
1326 if (type == LyXParagraph::PEXTRA_NONE) {
1327 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1328 cursor.par->UnsetPExtraType();
1329 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1332 cursor.par->SetPExtraType(type, width, widthp);
1333 cursor.par->pextra_hfill = hfill;
1334 cursor.par->pextra_start_minipage = start_minipage;
1335 cursor.par->pextra_alignment = alignment;
1338 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1340 RedoParagraphs(sel_start_cursor, endpar);
1342 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1343 sel_cursor = cursor;
1344 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1346 SetCursor(tmpcursor.par, tmpcursor.pos);
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->text.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->text.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->text.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 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2627 char * str = new char[text.size() + 1];
2628 copy(text.begin(), text.end(), str);
2629 str[text.size()] = '\0';
2635 // needed to insert the selection
2636 void LyXText::InsertStringA(char const * s)
2639 LyXParagraph * par = cursor.par;
2640 LyXParagraph::size_type pos = cursor.pos;
2641 LyXParagraph::size_type a = 0;
2643 LyXParagraph * endpar = cursor.par->Next();
2648 textclasslist.Style(parameters->textclass,
2649 cursor.par->GetLayout()).isEnvironment();
2650 // only to be sure, should not be neccessary
2653 // insert the string, don't insert doublespace
2654 string::size_type i = 0;
2655 while (i < str.length()) {
2656 if (str[i] != '\n') {
2658 && i + 1 < str.length() && str[i + 1] != ' '
2659 && pos && par->GetChar(pos - 1)!= ' ') {
2660 par->InsertChar(pos,' ');
2662 } else if (par->table) {
2663 if (str[i] == '\t') {
2664 while((pos < par->size()) &&
2665 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2667 if (pos < par->size())
2669 else // no more fields to fill skip the rest
2671 } else if ((str[i] != 13) &&
2672 ((str[i] & 127) >= ' ')) {
2673 par->InsertChar(pos, str[i]);
2676 } else if (str[i] == ' ') {
2678 InsetSpecialChar * new_inset =
2679 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2680 par->InsertInset(pos, new_inset);
2682 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2685 } else if (str[i] == '\t') {
2686 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2688 InsetSpecialChar * new_inset =
2689 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2690 par->InsertInset(pos, new_inset);
2692 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2696 } else if (str[i]!= 13 &&
2697 // Ignore unprintables
2698 (str[i] & 127) >= ' ') {
2699 par->InsertChar(pos, str[i]);
2704 if (i + 1 >= str.length()) {
2708 while((pos < par->size()) &&
2709 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2712 cell = NumberOfCell(par, pos);
2713 while((pos < par->size()) &&
2714 !(par->table->IsFirstCell(cell))) {
2716 while((pos < par->size()) &&
2717 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2720 cell = NumberOfCell(par, pos);
2722 if (pos >= par->size())
2723 // no more fields to fill skip the rest
2726 if (!par->text.size()) {
2728 InsetSpecialChar * new_inset =
2729 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2730 par->InsertInset(pos, new_inset);
2732 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2736 par->BreakParagraph(pos, flag);
2744 RedoParagraphs(cursor, endpar);
2745 SetCursor(cursor.par, cursor.pos);
2746 sel_cursor = cursor;
2747 SetCursor(par, pos);
2752 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2754 char * str = new char[text.size() + 1];
2755 copy(text.begin(), text.end(), str);
2756 str[text.size()] = '\0';
2762 /* turns double-CR to single CR, others where converted into one blank and 13s
2763 * that are ignored .Double spaces are also converted into one. Spaces at
2764 * the beginning of a paragraph are forbidden. tabs are converted into one
2765 * space. then InsertStringA is called */
2766 void LyXText::InsertStringB(char const * s)
2769 LyXParagraph * par = cursor.par;
2770 string::size_type i = 1;
2771 while (i < str.length()) {
2772 if (str[i] == '\t' && !par->table)
2774 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2776 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2777 if (str[i + 1] != '\n') {
2778 if (str[i - 1] != ' ')
2783 while (i + 1 < str.length()
2784 && (str[i + 1] == ' '
2785 || str[i + 1] == '\t'
2786 || str[i + 1] == '\n'
2787 || str[i + 1] == 13)) {
2794 InsertStringA(str.c_str());
2798 bool LyXText::GotoNextError() const
2800 LyXCursor res = cursor;
2802 if (res.pos < res.par->Last() - 1) {
2806 res.par = res.par->Next();
2811 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2812 && res.par->GetInset(res.pos)->AutoDelete()));
2815 SetCursor(res.par, res.pos);
2822 bool LyXText::GotoNextNote() const
2824 LyXCursor res = cursor;
2826 if (res.pos < res.par->Last() - 1) {
2829 res.par = res.par->Next();
2834 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2835 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2838 SetCursor(res.par, res.pos);
2845 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2846 LyXTextClassList::size_type class2,
2850 if (!par || class1 == class2)
2852 par = par->FirstPhysicalPar();
2854 string name = textclasslist.NameOfLayout(class1, par->layout);
2856 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2857 textclasslist.NumberOfLayout(class2, name);
2860 } else { // layout not found
2861 // use default layout "Standard" (0)
2866 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2868 string s = "Layout had to be changed from\n"
2869 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2870 + "\nbecause of class conversion from\n"
2871 + textclasslist.NameOfClass(class1) + " to "
2872 + textclasslist.NameOfClass(class2);
2873 InsetError * new_inset = new InsetError(s);
2874 par->InsertChar(0, LyXParagraph::META_INSET);
2875 par->InsertInset(0, new_inset);
2884 void LyXText::CheckParagraph(LyXParagraph * par,
2885 LyXParagraph::size_type pos)
2888 LyXCursor tmpcursor;
2890 /* table stuff -- begin*/
2893 CheckParagraphInTable(par, pos);
2896 /* table stuff -- end*/
2899 LyXParagraph::size_type z;
2900 Row * row = GetRow(par, pos, y);
2902 // is there a break one row above
2903 if (row->previous && row->previous->par == row->par) {
2904 z = NextBreakPoint(row->previous, paperwidth);
2905 if ( z >= row->pos) {
2906 // set the dimensions of the row above
2907 y -= row->previous->height;
2909 refresh_row = row->previous;
2910 status = LyXText::NEED_MORE_REFRESH;
2912 BreakAgain(row->previous);
2914 // set the cursor again. Otherwise
2915 // dangling pointers are possible
2916 SetCursor(cursor.par, cursor.pos);
2917 sel_cursor = cursor;
2922 int tmpheight = row->height;
2923 LyXParagraph::size_type tmplast = RowLast(row);
2928 if (row->height == tmpheight && RowLast(row) == tmplast)
2929 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2931 status = LyXText::NEED_MORE_REFRESH;
2933 // check the special right address boxes
2934 if (textclasslist.Style(parameters->textclass,
2935 par->GetLayout()).margintype
2936 == MARGIN_RIGHT_ADDRESS_BOX) {
2937 tmpcursor.par = par;
2938 tmpcursor.row = row;
2941 tmpcursor.x_fix = 0;
2942 tmpcursor.pos = pos;
2943 RedoDrawingOfParagraph(tmpcursor);
2948 // set the cursor again. Otherwise dangling pointers are possible
2949 // also set the selection
2953 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2954 sel_cursor = cursor;
2955 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2956 sel_start_cursor = cursor;
2957 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2958 sel_end_cursor = cursor;
2959 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2960 last_sel_cursor = cursor;
2963 SetCursorIntern(cursor.par, cursor.pos);
2967 // returns 0 if inset wasn't found
2968 int LyXText::UpdateInset(Inset * inset)
2970 // first check the current paragraph
2971 int pos = cursor.par->GetPositionOfInset(inset);
2973 CheckParagraph(cursor.par, pos);
2977 // check every paragraph
2979 LyXParagraph * par = FirstParagraph();
2981 // make sure the paragraph is open
2982 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2983 pos = par->GetPositionOfInset(inset);
2985 CheckParagraph(par, pos);
2996 void LyXText::SetCursor(LyXParagraph * par,
2997 LyXParagraph::size_type pos, bool setfont) const
2999 LyXCursor old_cursor = cursor;
3000 SetCursorIntern(par, pos, setfont);
3001 DeleteEmptyParagraphMechanism(old_cursor);
3005 void LyXText::SetCursorIntern(LyXParagraph * par,
3006 LyXParagraph::size_type pos, bool setfont) const
3010 LyXParagraph * tmppar;
3011 LyXParagraph::size_type vpos,cursor_vpos;
3013 // correct the cursor position if impossible
3014 if (pos > par->Last()){
3015 tmppar = par->ParFromPos(pos);
3016 pos = par->PositionInParFromPos(pos);
3019 if (par->IsDummy() && par->previous &&
3020 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3021 while (par->previous &&
3022 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3023 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3024 par = par->previous ;
3025 if (par->IsDummy() &&
3026 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3027 pos += par->text.size() + 1;
3029 if (par->previous) {
3030 par = par->previous;
3032 pos += par->text.size() + 1;
3040 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3041 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3042 && !cursor.par->IsSeparator(cursor.pos))
3043 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3045 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3046 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3048 current_font = cursor.par->GetFontSettings(cursor.pos);
3049 real_current_font = GetFont(cursor.par, cursor.pos);
3050 if (pos == 0 && par->size() == 0
3051 && parameters->getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
3052 current_font.setDirection(LyXFont::RTL_DIR);
3053 real_current_font.setDirection(LyXFont::RTL_DIR);
3057 /* get the cursor y position in text */
3058 row = GetRow(par, pos, y);
3059 /* y is now the beginning of the cursor row */
3061 /* y is now the cursor baseline */
3064 /* now get the cursors x position */
3066 float fill_separator, fill_hfill, fill_label_hfill;
3067 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3069 LyXParagraph::size_type last = RowLast(row);
3070 if (row->pos > last)
3072 else if (pos <= last ) {
3073 LyXDirection letter_direction =
3074 row->par->getLetterDirection(pos);
3075 LyXDirection font_direction =
3076 real_current_font.getFontDirection();
3077 if (letter_direction == font_direction || pos == 0)
3078 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3079 ? log2vis(pos) : log2vis(pos)+1;
3081 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3082 ? log2vis(pos-1)+1 : log2vis(pos-1);
3084 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3085 ? log2vis(last)+1 : log2vis(last);
3087 /* table stuff -- begin*/
3088 if (row->par->table) {
3089 int cell = NumberOfCell(row->par, row->pos);
3091 x += row->par->table->GetBeginningOfTextInCell(cell);
3092 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3093 pos = vis2log(vpos);
3094 if (row->par->IsNewline(pos)) {
3095 x = x_old + row->par->table->WidthOfColumn(cell);
3098 x += row->par->table->GetBeginningOfTextInCell(cell);
3100 x += SingleWidth(row->par, pos);
3104 /* table stuff -- end*/
3105 LyXParagraph::size_type main_body =
3106 BeginningOfMainBody(row->par);
3107 if (main_body > 0 &&
3108 (main_body-1 > last ||
3109 !row->par->IsLineSeparator(main_body-1)))
3112 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3113 pos = vis2log(vpos);
3114 if (main_body > 0 && pos == main_body-1) {
3115 x += fill_label_hfill +
3116 GetFont(row->par, -2).stringWidth(
3117 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3118 if (row->par->IsLineSeparator(main_body-1))
3119 x -= SingleWidth(row->par, main_body-1);
3122 x += SingleWidth(row->par, pos);
3123 if (HfillExpansion(row, pos)) {
3124 if (pos >= main_body)
3127 x += fill_label_hfill;
3129 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3137 cursor.x_fix = cursor.x;
3142 void LyXText::SetCursorFromCoordinates(int x, long y) const
3144 LyXCursor old_cursor = cursor;
3146 /* get the row first */
3148 Row * row = GetRowNearY(y);
3150 cursor.par = row->par;
3152 int column = GetColumnNearX(row, x);
3153 cursor.pos = row->pos + column;
3155 cursor.y = y + row->baseline;
3160 (cursor.pos == cursor.par->Last()
3161 || cursor.par->IsSeparator(cursor.pos)
3162 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3163 && !cursor.par->IsSeparator(cursor.pos))
3164 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3166 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3167 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3169 current_font = cursor.par->GetFontSettings(cursor.pos);
3170 real_current_font = GetFont(cursor.par, cursor.pos);
3172 DeleteEmptyParagraphMechanism(old_cursor);
3176 void LyXText::CursorLeft() const
3179 if (cursor.par->table) {
3180 int cell = NumberOfCell(cursor.par, cursor.pos);
3181 if (cursor.par->table->IsContRow(cell) &&
3182 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3189 void LyXText::CursorLeftIntern() const
3191 if (cursor.pos > 0) {
3192 SetCursor(cursor.par, cursor.pos - 1);
3194 else if (cursor.par->Previous()) {
3195 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3200 void LyXText::CursorRight() const
3202 CursorRightIntern();
3203 if (cursor.par->table) {
3204 int cell = NumberOfCell(cursor.par, cursor.pos);
3205 if (cursor.par->table->IsContRow(cell) &&
3206 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3213 void LyXText::CursorRightIntern() const
3215 if (cursor.pos < cursor.par->Last()) {
3216 SetCursor(cursor.par, cursor.pos + 1);
3218 else if (cursor.par->Next()) {
3219 SetCursor(cursor.par->Next(), 0);
3224 void LyXText::CursorUp() const
3226 SetCursorFromCoordinates(cursor.x_fix,
3227 cursor.y - cursor.row->baseline - 1);
3228 if (cursor.par->table) {
3229 int cell = NumberOfCell(cursor.par, cursor.pos);
3230 if (cursor.par->table->IsContRow(cell) &&
3231 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3238 void LyXText::CursorDown() const
3240 if (cursor.par->table &&
3241 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3244 SetCursorFromCoordinates(cursor.x_fix,
3245 cursor.y - cursor.row->baseline
3246 + cursor.row->height + 1);
3247 if (cursor.par->table) {
3248 int cell = NumberOfCell(cursor.par, cursor.pos);
3249 int cell_above = cursor.par->table->GetCellAbove(cell);
3250 while(cursor.par->table &&
3251 cursor.par->table->IsContRow(cell) &&
3252 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3253 SetCursorFromCoordinates(cursor.x_fix,
3254 cursor.y - cursor.row->baseline
3255 + cursor.row->height + 1);
3256 if (cursor.par->table) {
3257 cell = NumberOfCell(cursor.par, cursor.pos);
3258 cell_above = cursor.par->table->GetCellAbove(cell);
3265 void LyXText::CursorUpParagraph() const
3267 if (cursor.pos > 0) {
3268 SetCursor(cursor.par, 0);
3270 else if (cursor.par->Previous()) {
3271 SetCursor(cursor.par->Previous(), 0);
3276 void LyXText::CursorDownParagraph() const
3278 if (cursor.par->Next()) {
3279 SetCursor(cursor.par->Next(), 0);
3281 SetCursor(cursor.par, cursor.par->Last());
3287 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3289 bool deleted = false;
3291 // this is the delete-empty-paragraph-mechanism.
3292 if (selection) return;
3294 // if free-spacing, then return also.
3295 if (textclasslist.Style(parameters->textclass,
3296 old_cursor.par->GetLayout()).free_spacing)
3299 #ifdef FIX_DOUBLE_SPACE
3300 /* Ok I'll put some comments here about what is missing.
3301 I have fixed BackSpace (and thus Delete) to not delete
3302 double-spaces automagically. I have also changed Cut,
3303 Copy and Paste to hopefully do some sensible things.
3304 There are still some small problems that can lead to
3305 double spaces stored in the document file or space at
3306 the beginning of paragraphs. This happens if you have
3307 the cursor betwenn to spaces and then save. Or if you
3308 cut and paste and the selection have a space at the
3309 beginning and then save right after the paste. I am
3310 sure none of these are very hard to fix, but I will
3311 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3312 that I can get some feedback. (Lgb)
3315 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3316 // delete the LineSeparator.
3319 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3320 // delete the LineSeparator.
3323 // If the pos around the old_cursor were spaces, delete one of them.
3324 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3325 && old_cursor.pos > 0
3326 && old_cursor.pos < old_cursor.par->Last()
3327 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3328 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3329 old_cursor.par->Erase(old_cursor.pos - 1);
3330 RedoParagraphs(old_cursor, old_cursor.par->Next());
3331 // or RedoDrawingOfParagraph(old_cursor);
3333 if (old_cursor.par == cursor.par &&
3334 cursor.pos > old_cursor.pos)
3335 SetCursor(cursor.par, cursor.pos - 1);
3337 SetCursor(cursor.par, cursor.pos);
3342 // Paragraph should not be deleted if empty
3343 if ((textclasslist.Style(parameters->textclass,
3344 old_cursor.par->GetLayout())).keepempty)
3347 LyXCursor tmpcursor;
3349 if (old_cursor.par != cursor.par) {
3350 if ( (old_cursor.par->Last() == 0
3351 || (old_cursor.par->Last() == 1
3352 && (old_cursor.par->IsLineSeparator(0))))
3353 && old_cursor.par->FirstPhysicalPar()
3354 == old_cursor.par->LastPhysicalPar()) {
3356 // ok, we will delete anything
3358 // make sure that you do not delete any environments
3359 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3360 !(old_cursor.row->previous
3361 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3362 && !(old_cursor.row->next
3363 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3365 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3366 ((old_cursor.row->previous
3367 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3369 (old_cursor.row->next
3370 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3372 status = LyXText::NEED_MORE_REFRESH;
3375 if (old_cursor.row->previous) {
3376 refresh_row = old_cursor.row->previous;
3377 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3379 cursor = old_cursor; // that undo can restore the right cursor position
3380 LyXParagraph * endpar = old_cursor.par->next;
3381 if (endpar && endpar->GetDepth()) {
3382 while (endpar && endpar->GetDepth()) {
3383 endpar = endpar->LastPhysicalPar()->Next();
3386 SetUndo(Undo::DELETE,
3387 old_cursor.par->previous,
3392 RemoveRow(old_cursor.row);
3393 if (params->paragraph == old_cursor.par) {
3394 params->paragraph = params->paragraph->next;
3397 delete old_cursor.par;
3399 /* Breakagain the next par. Needed
3400 * because of the parindent that
3401 * can occur or dissappear. The
3402 * next row can change its height,
3403 * if there is another layout before */
3404 if (refresh_row->next) {
3405 BreakAgain(refresh_row->next);
3406 UpdateCounters(refresh_row);
3408 SetHeightOfRow(refresh_row);
3410 refresh_row = old_cursor.row->next;
3411 refresh_y = old_cursor.y - old_cursor.row->baseline;
3414 cursor = old_cursor; // that undo can restore the right cursor position
3415 LyXParagraph *endpar = old_cursor.par->next;
3416 if (endpar && endpar->GetDepth()) {
3417 while (endpar && endpar->GetDepth()) {
3418 endpar = endpar->LastPhysicalPar()->Next();
3421 SetUndo(Undo::DELETE,
3422 old_cursor.par->previous,
3427 RemoveRow(old_cursor.row);
3429 if (params->paragraph == old_cursor.par) {
3430 params->paragraph = params->paragraph->next;
3432 delete old_cursor.par;
3434 /* Breakagain the next par. Needed
3435 because of the parindent that can
3436 occur or dissappear.
3437 The next row can change its height,
3438 if there is another layout before
3441 BreakAgain(refresh_row);
3442 UpdateCounters(refresh_row->previous);
3447 SetCursor(cursor.par, cursor.pos);
3449 /* if (cursor.y > old_cursor.y)
3450 cursor.y -= old_cursor.row->height; */
3452 if (sel_cursor.par == old_cursor.par
3453 && sel_cursor.pos == sel_cursor.pos) {
3454 // correct selection
3455 sel_cursor = cursor;
3460 if (old_cursor.par->ClearParagraph()){
3461 RedoParagraphs(old_cursor, old_cursor.par->Next());
3463 SetCursor(cursor.par, cursor.pos);
3464 sel_cursor = cursor;
3471 LyXParagraph * LyXText::GetParFromID(int id)
3473 LyXParagraph * result = FirstParagraph();
3474 while (result && result->id() != id)
3475 result = result->next;
3481 bool LyXText::TextUndo()
3483 // returns false if no undo possible
3484 Undo * undo = params->undostack.pop();
3489 .push(CreateUndo(undo->kind,
3490 GetParFromID(undo->number_of_before_par),
3491 GetParFromID(undo->number_of_behind_par)));
3493 return TextHandleUndo(undo);
3497 bool LyXText::TextRedo()
3499 // returns false if no redo possible
3500 Undo * undo = params->redostack.pop();
3505 .push(CreateUndo(undo->kind,
3506 GetParFromID(undo->number_of_before_par),
3507 GetParFromID(undo->number_of_behind_par)));
3509 return TextHandleUndo(undo);
3513 bool LyXText::TextHandleUndo(Undo * undo)
3515 // returns false if no undo possible
3516 bool result = false;
3518 LyXParagraph * before =
3519 GetParFromID(undo->number_of_before_par);
3520 LyXParagraph * behind =
3521 GetParFromID(undo->number_of_behind_par);
3522 LyXParagraph * tmppar;
3523 LyXParagraph * tmppar2;
3524 LyXParagraph * tmppar3;
3525 LyXParagraph * tmppar4;
3526 LyXParagraph * endpar;
3527 LyXParagraph * tmppar5;
3529 // if there's no before take the beginning
3530 // of the document for redoing
3532 SetCursorIntern(FirstParagraph(), 0);
3534 // replace the paragraphs with the undo informations
3536 tmppar3 = undo->par;
3537 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3540 while (tmppar4->next)
3541 tmppar4 = tmppar4->next;
3542 } // get last undo par
3544 // now remove the old text if there is any
3545 if (before != behind || (!behind && !before)){
3547 tmppar5 = before->next;
3549 tmppar5 = params->paragraph;
3551 while (tmppar5 && tmppar5 != behind){
3553 tmppar5 = tmppar5->next;
3554 // a memory optimization for edit: Only layout information
3555 // is stored in the undo. So restore the text informations.
3556 if (undo->kind == Undo::EDIT){
3557 tmppar2->text = tmppar->text;
3558 tmppar->text.clear();
3559 tmppar2 = tmppar2->next;
3561 if ( currentrow && currentrow->par == tmppar )
3562 currentrow = currentrow -> previous;
3563 // Commenting out this might remove the error
3564 // reported by Purify, but it might also
3565 // introduce a memory leak. We need to
3571 // put the new stuff in the list if there is one
3574 before->next = tmppar3;
3576 params->paragraph = tmppar3;
3577 tmppar3->previous = before;
3581 params->paragraph = behind;
3584 tmppar4->next = behind;
3586 behind->previous = tmppar4;
3590 // Set the cursor for redoing
3592 SetCursorIntern(before->FirstSelfrowPar(), 0);
3593 // check wether before points to a closed float and open it if necessary
3594 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3595 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3597 while (tmppar4->previous &&
3598 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3599 tmppar4 = tmppar4->previous;
3600 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3601 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3602 tmppar4 = tmppar4->next;
3607 // open a cosed footnote at the end if necessary
3608 if (behind && behind->previous &&
3609 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3610 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3611 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3612 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3613 behind = behind->next;
3617 // calculate the endpar for redoing the paragraphs.
3619 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3620 endpar = behind->LastPhysicalPar()->Next();
3622 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3627 tmppar = GetParFromID(undo->number_of_cursor_par);
3628 RedoParagraphs(cursor, endpar);
3630 SetCursorIntern(tmppar, undo->cursor_pos);
3631 UpdateCounters(cursor.row);
3641 void LyXText::FinishUndo()
3643 // makes sure the next operation will be stored
3644 undo_finished = True;
3648 void LyXText::FreezeUndo()
3650 // this is dangerous and for internal use only
3655 void LyXText::UnFreezeUndo()
3657 // this is dangerous and for internal use only
3658 undo_frozen = false;
3662 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3663 LyXParagraph const * behind) const
3666 params->undostack.push(CreateUndo(kind, before, behind));
3667 params->redostack.clear();
3671 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3672 LyXParagraph const * behind)
3674 params->redostack.push(CreateUndo(kind, before, behind));
3678 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3679 LyXParagraph const * behind) const
3681 int before_number = -1;
3682 int behind_number = -1;
3684 before_number = before->id();
3686 behind_number = behind->id();
3687 // Undo::EDIT and Undo::FINISH are
3688 // always finished. (no overlapping there)
3689 // overlapping only with insert and delete inside one paragraph:
3690 // Nobody wants all removed character
3691 // appear one by one when undoing.
3692 // EDIT is special since only layout information, not the
3693 // contents of a paragaph are stored.
3694 if (!undo_finished && kind != Undo::EDIT &&
3695 kind != Undo::FINISH){
3696 // check wether storing is needed
3697 if (!params->undostack.empty() &&
3698 params->undostack.top()->kind == kind &&
3699 params->undostack.top()->number_of_before_par == before_number &&
3700 params->undostack.top()->number_of_behind_par == behind_number ){
3705 // create a new Undo
3706 LyXParagraph * undopar;
3707 LyXParagraph * tmppar;
3708 LyXParagraph * tmppar2;
3710 LyXParagraph * start = 0;
3711 LyXParagraph * end = 0;
3714 start = before->next;
3716 start = FirstParagraph();
3718 end = behind->previous;
3720 end = FirstParagraph();
3726 && start != end->next
3727 && (before != behind || (!before && !behind))) {
3729 tmppar2 = tmppar->Clone();
3730 tmppar2->id(tmppar->id());
3732 // a memory optimization: Just store the layout information
3734 if (kind == Undo::EDIT){
3735 tmppar2->text.clear();
3740 while (tmppar != end && tmppar->next) {
3741 tmppar = tmppar->next;
3742 tmppar2->next = tmppar->Clone();
3743 tmppar2->next->id(tmppar->id());
3744 // a memory optimization: Just store the layout
3745 // information when only edit
3746 if (kind == Undo::EDIT){
3747 tmppar2->next->text.clear();
3749 tmppar2->next->previous = tmppar2;
3750 tmppar2 = tmppar2->next;
3754 undopar = 0; // nothing to replace (undo of delete maybe)
3756 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3757 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3759 Undo * undo = new Undo(kind,
3760 before_number, behind_number,
3761 cursor_par, cursor_pos,
3764 undo_finished = false;
3769 void LyXText::SetCursorParUndo()
3771 SetUndo(Undo::FINISH,
3772 cursor.par->ParFromPos(cursor.pos)->previous,
3773 cursor.par->ParFromPos(cursor.pos)->next);
3777 void LyXText::RemoveTableRow(LyXCursor * cur) const
3783 // move to the previous row
3784 int cell_act = NumberOfCell(cur->par, cur->pos);
3787 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3790 !cur->par->table->IsFirstCell(cell_act)) {
3792 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3797 // now we have to pay attention if the actual table is the
3798 // main row of TableContRows and if yes to delete all of them
3803 // delete up to the next row
3804 while (cur->pos < cur->par->Last() &&
3806 || !cur->par->table->IsFirstCell(cell_act))) {
3807 while (cur->pos < cur->par->Last() &&
3808 !cur->par->IsNewline(cur->pos))
3809 cur->par->Erase(cur->pos);
3812 if (cur->pos < cur->par->Last())
3813 cur->par->Erase(cur->pos);
3815 if (cur->pos && cur->pos == cur->par->Last()) {
3817 cur->par->Erase(cur->pos); // no newline at very end!
3819 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3820 !cur->par->table->IsContRow(cell_org) &&
3821 cur->par->table->IsContRow(cell));
3822 cur->par->table->DeleteRow(cell_org);
3827 bool LyXText::IsEmptyTableCell() const
3829 LyXParagraph::size_type pos = cursor.pos - 1;
3830 while (pos >= 0 && pos < cursor.par->Last()
3831 && !cursor.par->IsNewline(pos))
3833 return cursor.par->IsNewline(pos + 1);
3837 void LyXText::toggleAppendix(){
3838 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3839 bool start = !par->start_of_appendix;
3841 // ensure that we have only one start_of_appendix in this document
3842 LyXParagraph * tmp = FirstParagraph();
3843 for (; tmp; tmp = tmp->next)
3844 tmp->start_of_appendix = 0;
3845 par->start_of_appendix = start;
3847 // we can set the refreshing parameters now
3848 status = LyXText::NEED_MORE_REFRESH;
3850 refresh_row = 0; // not needed for full update
3852 SetCursor(cursor.par, cursor.pos);