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->InsertChar(pos, LyXParagraph::META_INSET);
2681 par->InsertInset(pos, new_inset);
2683 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2686 } else if (str[i] == '\t') {
2687 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2689 InsetSpecialChar * new_inset =
2690 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2691 par->InsertChar(pos, LyXParagraph::META_INSET);
2692 par->InsertInset(pos, new_inset);
2694 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2698 } else if (str[i]!= 13 &&
2699 // Ignore unprintables
2700 (str[i] & 127) >= ' ') {
2701 par->InsertChar(pos, str[i]);
2706 if (i + 1 >= str.length()) {
2710 while((pos < par->size()) &&
2711 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2714 cell = NumberOfCell(par, pos);
2715 while((pos < par->size()) &&
2716 !(par->table->IsFirstCell(cell))) {
2718 while((pos < par->size()) &&
2719 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2722 cell = NumberOfCell(par, pos);
2724 if (pos >= par->size())
2725 // no more fields to fill skip the rest
2728 if (!par->text.size()) {
2730 InsetSpecialChar * new_inset =
2731 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2732 par->InsertChar(pos, LyXParagraph::META_INSET);
2733 par->InsertInset(pos, new_inset);
2735 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2739 par->BreakParagraph(pos, flag);
2747 RedoParagraphs(cursor, endpar);
2748 SetCursor(cursor.par, cursor.pos);
2749 sel_cursor = cursor;
2750 SetCursor(par, pos);
2755 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2757 char * str = new char[text.size() + 1];
2758 copy(text.begin(), text.end(), str);
2759 str[text.size()] = '\0';
2765 /* turns double-CR to single CR, others where converted into one blank and 13s
2766 * that are ignored .Double spaces are also converted into one. Spaces at
2767 * the beginning of a paragraph are forbidden. tabs are converted into one
2768 * space. then InsertStringA is called */
2769 void LyXText::InsertStringB(char const * s)
2772 LyXParagraph * par = cursor.par;
2773 string::size_type i = 1;
2774 while (i < str.length()) {
2775 if (str[i] == '\t' && !par->table)
2777 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2779 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2780 if (str[i + 1] != '\n') {
2781 if (str[i - 1] != ' ')
2786 while (i + 1 < str.length()
2787 && (str[i + 1] == ' '
2788 || str[i + 1] == '\t'
2789 || str[i + 1] == '\n'
2790 || str[i + 1] == 13)) {
2797 InsertStringA(str.c_str());
2801 bool LyXText::GotoNextError() const
2803 LyXCursor res = cursor;
2805 if (res.pos < res.par->Last() - 1) {
2809 res.par = res.par->Next();
2814 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2815 && res.par->GetInset(res.pos)->AutoDelete()));
2818 SetCursor(res.par, res.pos);
2825 bool LyXText::GotoNextNote() const
2827 LyXCursor res = cursor;
2829 if (res.pos < res.par->Last() - 1) {
2832 res.par = res.par->Next();
2837 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2838 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2841 SetCursor(res.par, res.pos);
2848 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2849 LyXTextClassList::size_type class2,
2853 if (!par || class1 == class2)
2855 par = par->FirstPhysicalPar();
2857 string name = textclasslist.NameOfLayout(class1, par->layout);
2859 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2860 textclasslist.NumberOfLayout(class2, name);
2863 } else { // layout not found
2864 // use default layout "Standard" (0)
2869 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2871 string s = "Layout had to be changed from\n"
2872 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2873 + "\nbecause of class conversion from\n"
2874 + textclasslist.NameOfClass(class1) + " to "
2875 + textclasslist.NameOfClass(class2);
2876 InsetError * new_inset = new InsetError(s);
2877 par->InsertChar(0, LyXParagraph::META_INSET);
2878 par->InsertInset(0, new_inset);
2887 void LyXText::CheckParagraph(LyXParagraph * par,
2888 LyXParagraph::size_type pos)
2891 LyXCursor tmpcursor;
2893 /* table stuff -- begin*/
2896 CheckParagraphInTable(par, pos);
2899 /* table stuff -- end*/
2902 LyXParagraph::size_type z;
2903 Row * row = GetRow(par, pos, y);
2905 // is there a break one row above
2906 if (row->previous && row->previous->par == row->par) {
2907 z = NextBreakPoint(row->previous, paperwidth);
2908 if ( z >= row->pos) {
2909 // set the dimensions of the row above
2910 y -= row->previous->height;
2912 refresh_row = row->previous;
2913 status = LyXText::NEED_MORE_REFRESH;
2915 BreakAgain(row->previous);
2917 // set the cursor again. Otherwise
2918 // dangling pointers are possible
2919 SetCursor(cursor.par, cursor.pos);
2920 sel_cursor = cursor;
2925 int tmpheight = row->height;
2926 LyXParagraph::size_type tmplast = RowLast(row);
2931 if (row->height == tmpheight && RowLast(row) == tmplast)
2932 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2934 status = LyXText::NEED_MORE_REFRESH;
2936 // check the special right address boxes
2937 if (textclasslist.Style(parameters->textclass,
2938 par->GetLayout()).margintype
2939 == MARGIN_RIGHT_ADDRESS_BOX) {
2940 tmpcursor.par = par;
2941 tmpcursor.row = row;
2944 tmpcursor.x_fix = 0;
2945 tmpcursor.pos = pos;
2946 RedoDrawingOfParagraph(tmpcursor);
2951 // set the cursor again. Otherwise dangling pointers are possible
2952 // also set the selection
2956 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2957 sel_cursor = cursor;
2958 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2959 sel_start_cursor = cursor;
2960 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2961 sel_end_cursor = cursor;
2962 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2963 last_sel_cursor = cursor;
2966 SetCursorIntern(cursor.par, cursor.pos);
2970 // returns 0 if inset wasn't found
2971 int LyXText::UpdateInset(Inset * inset)
2973 // first check the current paragraph
2974 int pos = cursor.par->GetPositionOfInset(inset);
2976 CheckParagraph(cursor.par, pos);
2980 // check every paragraph
2982 LyXParagraph * par = FirstParagraph();
2984 // make sure the paragraph is open
2985 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2986 pos = par->GetPositionOfInset(inset);
2988 CheckParagraph(par, pos);
2999 void LyXText::SetCursor(LyXParagraph * par,
3000 LyXParagraph::size_type pos, bool setfont) const
3002 LyXCursor old_cursor = cursor;
3003 SetCursorIntern(par, pos, setfont);
3004 DeleteEmptyParagraphMechanism(old_cursor);
3008 void LyXText::SetCursorIntern(LyXParagraph * par,
3009 LyXParagraph::size_type pos, bool setfont) const
3013 LyXParagraph * tmppar;
3014 LyXParagraph::size_type vpos,cursor_vpos;
3016 // correct the cursor position if impossible
3017 if (pos > par->Last()){
3018 tmppar = par->ParFromPos(pos);
3019 pos = par->PositionInParFromPos(pos);
3022 if (par->IsDummy() && par->previous &&
3023 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3024 while (par->previous &&
3025 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3026 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3027 par = par->previous ;
3028 if (par->IsDummy() &&
3029 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3030 pos += par->text.size() + 1;
3032 if (par->previous) {
3033 par = par->previous;
3035 pos += par->text.size() + 1;
3043 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3044 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3045 && !cursor.par->IsSeparator(cursor.pos))
3046 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3048 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3049 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3051 current_font = cursor.par->GetFontSettings(cursor.pos);
3052 real_current_font = GetFont(cursor.par, cursor.pos);
3053 if (pos == 0 && par->size() == 0
3054 && parameters->getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
3055 current_font.setDirection(LyXFont::RTL_DIR);
3056 real_current_font.setDirection(LyXFont::RTL_DIR);
3060 /* get the cursor y position in text */
3061 row = GetRow(par, pos, y);
3062 /* y is now the beginning of the cursor row */
3064 /* y is now the cursor baseline */
3067 /* now get the cursors x position */
3069 float fill_separator, fill_hfill, fill_label_hfill;
3070 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3072 LyXParagraph::size_type last = RowLast(row);
3073 if (row->pos > last)
3075 else if (pos <= last ) {
3076 LyXDirection letter_direction =
3077 row->par->getLetterDirection(pos);
3078 LyXDirection font_direction =
3079 real_current_font.getFontDirection();
3080 if (letter_direction == font_direction || pos == 0)
3081 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3082 ? log2vis(pos) : log2vis(pos)+1;
3084 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3085 ? log2vis(pos-1)+1 : log2vis(pos-1);
3087 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3088 ? log2vis(last)+1 : log2vis(last);
3090 /* table stuff -- begin*/
3091 if (row->par->table) {
3092 int cell = NumberOfCell(row->par, row->pos);
3094 x += row->par->table->GetBeginningOfTextInCell(cell);
3095 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3096 pos = vis2log(vpos);
3097 if (row->par->IsNewline(pos)) {
3098 x = x_old + row->par->table->WidthOfColumn(cell);
3101 x += row->par->table->GetBeginningOfTextInCell(cell);
3103 x += SingleWidth(row->par, pos);
3107 /* table stuff -- end*/
3108 LyXParagraph::size_type main_body =
3109 BeginningOfMainBody(row->par);
3110 if (main_body > 0 &&
3111 (main_body-1 > last ||
3112 !row->par->IsLineSeparator(main_body-1)))
3115 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3116 pos = vis2log(vpos);
3117 if (main_body > 0 && pos == main_body-1) {
3118 x += fill_label_hfill +
3119 GetFont(row->par, -2).stringWidth(
3120 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3121 if (row->par->IsLineSeparator(main_body-1))
3122 x -= SingleWidth(row->par, main_body-1);
3125 x += SingleWidth(row->par, pos);
3126 if (HfillExpansion(row, pos)) {
3127 if (pos >= main_body)
3130 x += fill_label_hfill;
3132 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3140 cursor.x_fix = cursor.x;
3145 void LyXText::SetCursorFromCoordinates(int x, long y) const
3147 LyXCursor old_cursor = cursor;
3149 /* get the row first */
3151 Row * row = GetRowNearY(y);
3153 cursor.par = row->par;
3155 int column = GetColumnNearX(row, x);
3156 cursor.pos = row->pos + column;
3158 cursor.y = y + row->baseline;
3163 (cursor.pos == cursor.par->Last()
3164 || cursor.par->IsSeparator(cursor.pos)
3165 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3166 && !cursor.par->IsSeparator(cursor.pos))
3167 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3169 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3170 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3172 current_font = cursor.par->GetFontSettings(cursor.pos);
3173 real_current_font = GetFont(cursor.par, cursor.pos);
3175 DeleteEmptyParagraphMechanism(old_cursor);
3179 void LyXText::CursorLeft() const
3182 if (cursor.par->table) {
3183 int cell = NumberOfCell(cursor.par, cursor.pos);
3184 if (cursor.par->table->IsContRow(cell) &&
3185 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3192 void LyXText::CursorLeftIntern() const
3194 if (cursor.pos > 0) {
3195 SetCursor(cursor.par, cursor.pos - 1);
3197 else if (cursor.par->Previous()) {
3198 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3203 void LyXText::CursorRight() const
3205 CursorRightIntern();
3206 if (cursor.par->table) {
3207 int cell = NumberOfCell(cursor.par, cursor.pos);
3208 if (cursor.par->table->IsContRow(cell) &&
3209 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3216 void LyXText::CursorRightIntern() const
3218 if (cursor.pos < cursor.par->Last()) {
3219 SetCursor(cursor.par, cursor.pos + 1);
3221 else if (cursor.par->Next()) {
3222 SetCursor(cursor.par->Next(), 0);
3227 void LyXText::CursorUp() const
3229 SetCursorFromCoordinates(cursor.x_fix,
3230 cursor.y - cursor.row->baseline - 1);
3231 if (cursor.par->table) {
3232 int cell = NumberOfCell(cursor.par, cursor.pos);
3233 if (cursor.par->table->IsContRow(cell) &&
3234 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3241 void LyXText::CursorDown() const
3243 if (cursor.par->table &&
3244 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3247 SetCursorFromCoordinates(cursor.x_fix,
3248 cursor.y - cursor.row->baseline
3249 + cursor.row->height + 1);
3250 if (cursor.par->table) {
3251 int cell = NumberOfCell(cursor.par, cursor.pos);
3252 int cell_above = cursor.par->table->GetCellAbove(cell);
3253 while(cursor.par->table &&
3254 cursor.par->table->IsContRow(cell) &&
3255 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3256 SetCursorFromCoordinates(cursor.x_fix,
3257 cursor.y - cursor.row->baseline
3258 + cursor.row->height + 1);
3259 if (cursor.par->table) {
3260 cell = NumberOfCell(cursor.par, cursor.pos);
3261 cell_above = cursor.par->table->GetCellAbove(cell);
3268 void LyXText::CursorUpParagraph() const
3270 if (cursor.pos > 0) {
3271 SetCursor(cursor.par, 0);
3273 else if (cursor.par->Previous()) {
3274 SetCursor(cursor.par->Previous(), 0);
3279 void LyXText::CursorDownParagraph() const
3281 if (cursor.par->Next()) {
3282 SetCursor(cursor.par->Next(), 0);
3284 SetCursor(cursor.par, cursor.par->Last());
3290 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3292 bool deleted = false;
3294 // this is the delete-empty-paragraph-mechanism.
3295 if (selection) return;
3297 // if free-spacing, then return also.
3298 if (textclasslist.Style(parameters->textclass,
3299 old_cursor.par->GetLayout()).free_spacing)
3302 #ifdef FIX_DOUBLE_SPACE
3303 /* Ok I'll put some comments here about what is missing.
3304 I have fixed BackSpace (and thus Delete) to not delete
3305 double-spaces automagically. I have also changed Cut,
3306 Copy and Paste to hopefully do some sensible things.
3307 There are still some small problems that can lead to
3308 double spaces stored in the document file or space at
3309 the beginning of paragraphs. This happens if you have
3310 the cursor betwenn to spaces and then save. Or if you
3311 cut and paste and the selection have a space at the
3312 beginning and then save right after the paste. I am
3313 sure none of these are very hard to fix, but I will
3314 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3315 that I can get some feedback. (Lgb)
3318 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3319 // delete the LineSeparator.
3322 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3323 // delete the LineSeparator.
3326 // If the pos around the old_cursor were spaces, delete one of them.
3327 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3328 && old_cursor.pos > 0
3329 && old_cursor.pos < old_cursor.par->Last()
3330 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3331 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3332 old_cursor.par->Erase(old_cursor.pos - 1);
3333 RedoParagraphs(old_cursor, old_cursor.par->Next());
3334 // or RedoDrawingOfParagraph(old_cursor);
3336 if (old_cursor.par == cursor.par &&
3337 cursor.pos > old_cursor.pos)
3338 SetCursor(cursor.par, cursor.pos - 1);
3340 SetCursor(cursor.par, cursor.pos);
3345 // Paragraph should not be deleted if empty
3346 if ((textclasslist.Style(parameters->textclass,
3347 old_cursor.par->GetLayout())).keepempty)
3350 LyXCursor tmpcursor;
3352 if (old_cursor.par != cursor.par) {
3353 if ( (old_cursor.par->Last() == 0
3354 || (old_cursor.par->Last() == 1
3355 && (old_cursor.par->IsLineSeparator(0))))
3356 && old_cursor.par->FirstPhysicalPar()
3357 == old_cursor.par->LastPhysicalPar()) {
3359 // ok, we will delete anything
3361 // make sure that you do not delete any environments
3362 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3363 !(old_cursor.row->previous
3364 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3365 && !(old_cursor.row->next
3366 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3368 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3369 ((old_cursor.row->previous
3370 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3372 (old_cursor.row->next
3373 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3375 status = LyXText::NEED_MORE_REFRESH;
3378 if (old_cursor.row->previous) {
3379 refresh_row = old_cursor.row->previous;
3380 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3382 cursor = old_cursor; // that undo can restore the right cursor position
3383 LyXParagraph * endpar = old_cursor.par->next;
3384 if (endpar && endpar->GetDepth()) {
3385 while (endpar && endpar->GetDepth()) {
3386 endpar = endpar->LastPhysicalPar()->Next();
3389 SetUndo(Undo::DELETE,
3390 old_cursor.par->previous,
3395 RemoveRow(old_cursor.row);
3396 if (params->paragraph == old_cursor.par) {
3397 params->paragraph = params->paragraph->next;
3400 delete old_cursor.par;
3402 /* Breakagain the next par. Needed
3403 * because of the parindent that
3404 * can occur or dissappear. The
3405 * next row can change its height,
3406 * if there is another layout before */
3407 if (refresh_row->next) {
3408 BreakAgain(refresh_row->next);
3409 UpdateCounters(refresh_row);
3411 SetHeightOfRow(refresh_row);
3413 refresh_row = old_cursor.row->next;
3414 refresh_y = old_cursor.y - old_cursor.row->baseline;
3417 cursor = old_cursor; // that undo can restore the right cursor position
3418 LyXParagraph *endpar = old_cursor.par->next;
3419 if (endpar && endpar->GetDepth()) {
3420 while (endpar && endpar->GetDepth()) {
3421 endpar = endpar->LastPhysicalPar()->Next();
3424 SetUndo(Undo::DELETE,
3425 old_cursor.par->previous,
3430 RemoveRow(old_cursor.row);
3432 if (params->paragraph == old_cursor.par) {
3433 params->paragraph = params->paragraph->next;
3435 delete old_cursor.par;
3437 /* Breakagain the next par. Needed
3438 because of the parindent that can
3439 occur or dissappear.
3440 The next row can change its height,
3441 if there is another layout before
3444 BreakAgain(refresh_row);
3445 UpdateCounters(refresh_row->previous);
3450 SetCursor(cursor.par, cursor.pos);
3452 /* if (cursor.y > old_cursor.y)
3453 cursor.y -= old_cursor.row->height; */
3455 if (sel_cursor.par == old_cursor.par
3456 && sel_cursor.pos == sel_cursor.pos) {
3457 // correct selection
3458 sel_cursor = cursor;
3463 if (old_cursor.par->ClearParagraph()){
3464 RedoParagraphs(old_cursor, old_cursor.par->Next());
3466 SetCursor(cursor.par, cursor.pos);
3467 sel_cursor = cursor;
3474 LyXParagraph * LyXText::GetParFromID(int id)
3476 LyXParagraph * result = FirstParagraph();
3477 while (result && result->id() != id)
3478 result = result->next;
3484 bool LyXText::TextUndo()
3486 // returns false if no undo possible
3487 Undo * undo = params->undostack.pop();
3492 .push(CreateUndo(undo->kind,
3493 GetParFromID(undo->number_of_before_par),
3494 GetParFromID(undo->number_of_behind_par)));
3496 return TextHandleUndo(undo);
3500 bool LyXText::TextRedo()
3502 // returns false if no redo possible
3503 Undo * undo = params->redostack.pop();
3508 .push(CreateUndo(undo->kind,
3509 GetParFromID(undo->number_of_before_par),
3510 GetParFromID(undo->number_of_behind_par)));
3512 return TextHandleUndo(undo);
3516 bool LyXText::TextHandleUndo(Undo * undo)
3518 // returns false if no undo possible
3519 bool result = false;
3521 LyXParagraph * before =
3522 GetParFromID(undo->number_of_before_par);
3523 LyXParagraph * behind =
3524 GetParFromID(undo->number_of_behind_par);
3525 LyXParagraph * tmppar;
3526 LyXParagraph * tmppar2;
3527 LyXParagraph * tmppar3;
3528 LyXParagraph * tmppar4;
3529 LyXParagraph * endpar;
3530 LyXParagraph * tmppar5;
3532 // if there's no before take the beginning
3533 // of the document for redoing
3535 SetCursorIntern(FirstParagraph(), 0);
3537 // replace the paragraphs with the undo informations
3539 tmppar3 = undo->par;
3540 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3543 while (tmppar4->next)
3544 tmppar4 = tmppar4->next;
3545 } // get last undo par
3547 // now remove the old text if there is any
3548 if (before != behind || (!behind && !before)){
3550 tmppar5 = before->next;
3552 tmppar5 = params->paragraph;
3554 while (tmppar5 && tmppar5 != behind){
3556 tmppar5 = tmppar5->next;
3557 // a memory optimization for edit: Only layout information
3558 // is stored in the undo. So restore the text informations.
3559 if (undo->kind == Undo::EDIT){
3560 tmppar2->text = tmppar->text;
3561 tmppar->text.clear();
3562 tmppar2 = tmppar2->next;
3564 if ( currentrow && currentrow->par == tmppar )
3565 currentrow = currentrow -> previous;
3566 // Commenting out this might remove the error
3567 // reported by Purify, but it might also
3568 // introduce a memory leak. We need to
3574 // put the new stuff in the list if there is one
3577 before->next = tmppar3;
3579 params->paragraph = tmppar3;
3580 tmppar3->previous = before;
3584 params->paragraph = behind;
3587 tmppar4->next = behind;
3589 behind->previous = tmppar4;
3593 // Set the cursor for redoing
3595 SetCursorIntern(before->FirstSelfrowPar(), 0);
3596 // check wether before points to a closed float and open it if necessary
3597 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3598 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3600 while (tmppar4->previous &&
3601 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3602 tmppar4 = tmppar4->previous;
3603 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3604 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3605 tmppar4 = tmppar4->next;
3610 // open a cosed footnote at the end if necessary
3611 if (behind && behind->previous &&
3612 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3613 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3614 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3615 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3616 behind = behind->next;
3620 // calculate the endpar for redoing the paragraphs.
3622 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3623 endpar = behind->LastPhysicalPar()->Next();
3625 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3630 tmppar = GetParFromID(undo->number_of_cursor_par);
3631 RedoParagraphs(cursor, endpar);
3633 SetCursorIntern(tmppar, undo->cursor_pos);
3634 UpdateCounters(cursor.row);
3644 void LyXText::FinishUndo()
3646 // makes sure the next operation will be stored
3647 undo_finished = True;
3651 void LyXText::FreezeUndo()
3653 // this is dangerous and for internal use only
3658 void LyXText::UnFreezeUndo()
3660 // this is dangerous and for internal use only
3661 undo_frozen = false;
3665 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3666 LyXParagraph const * behind) const
3669 params->undostack.push(CreateUndo(kind, before, behind));
3670 params->redostack.clear();
3674 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3675 LyXParagraph const * behind)
3677 params->redostack.push(CreateUndo(kind, before, behind));
3681 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3682 LyXParagraph const * behind) const
3684 int before_number = -1;
3685 int behind_number = -1;
3687 before_number = before->id();
3689 behind_number = behind->id();
3690 // Undo::EDIT and Undo::FINISH are
3691 // always finished. (no overlapping there)
3692 // overlapping only with insert and delete inside one paragraph:
3693 // Nobody wants all removed character
3694 // appear one by one when undoing.
3695 // EDIT is special since only layout information, not the
3696 // contents of a paragaph are stored.
3697 if (!undo_finished && kind != Undo::EDIT &&
3698 kind != Undo::FINISH){
3699 // check wether storing is needed
3700 if (!params->undostack.empty() &&
3701 params->undostack.top()->kind == kind &&
3702 params->undostack.top()->number_of_before_par == before_number &&
3703 params->undostack.top()->number_of_behind_par == behind_number ){
3708 // create a new Undo
3709 LyXParagraph * undopar;
3710 LyXParagraph * tmppar;
3711 LyXParagraph * tmppar2;
3713 LyXParagraph * start = 0;
3714 LyXParagraph * end = 0;
3717 start = before->next;
3719 start = FirstParagraph();
3721 end = behind->previous;
3723 end = FirstParagraph();
3729 && start != end->next
3730 && (before != behind || (!before && !behind))) {
3732 tmppar2 = tmppar->Clone();
3733 tmppar2->id(tmppar->id());
3735 // a memory optimization: Just store the layout information
3737 if (kind == Undo::EDIT){
3738 tmppar2->text.clear();
3743 while (tmppar != end && tmppar->next) {
3744 tmppar = tmppar->next;
3745 tmppar2->next = tmppar->Clone();
3746 tmppar2->next->id(tmppar->id());
3747 // a memory optimization: Just store the layout
3748 // information when only edit
3749 if (kind == Undo::EDIT){
3750 tmppar2->next->text.clear();
3752 tmppar2->next->previous = tmppar2;
3753 tmppar2 = tmppar2->next;
3757 undopar = 0; // nothing to replace (undo of delete maybe)
3759 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3760 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3762 Undo * undo = new Undo(kind,
3763 before_number, behind_number,
3764 cursor_par, cursor_pos,
3767 undo_finished = false;
3772 void LyXText::SetCursorParUndo()
3774 SetUndo(Undo::FINISH,
3775 cursor.par->ParFromPos(cursor.pos)->previous,
3776 cursor.par->ParFromPos(cursor.pos)->next);
3780 void LyXText::RemoveTableRow(LyXCursor * cur) const
3786 // move to the previous row
3787 int cell_act = NumberOfCell(cur->par, cur->pos);
3790 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3793 !cur->par->table->IsFirstCell(cell_act)) {
3795 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3800 // now we have to pay attention if the actual table is the
3801 // main row of TableContRows and if yes to delete all of them
3806 // delete up to the next row
3807 while (cur->pos < cur->par->Last() &&
3809 || !cur->par->table->IsFirstCell(cell_act))) {
3810 while (cur->pos < cur->par->Last() &&
3811 !cur->par->IsNewline(cur->pos))
3812 cur->par->Erase(cur->pos);
3815 if (cur->pos < cur->par->Last())
3816 cur->par->Erase(cur->pos);
3818 if (cur->pos && cur->pos == cur->par->Last()) {
3820 cur->par->Erase(cur->pos); // no newline at very end!
3822 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3823 !cur->par->table->IsContRow(cell_org) &&
3824 cur->par->table->IsContRow(cell));
3825 cur->par->table->DeleteRow(cell_org);
3830 bool LyXText::IsEmptyTableCell() const
3832 LyXParagraph::size_type pos = cursor.pos - 1;
3833 while (pos >= 0 && pos < cursor.par->Last()
3834 && !cursor.par->IsNewline(pos))
3836 return cursor.par->IsNewline(pos + 1);
3840 void LyXText::toggleAppendix(){
3841 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3842 bool start = !par->start_of_appendix;
3844 // ensure that we have only one start_of_appendix in this document
3845 LyXParagraph * tmp = FirstParagraph();
3846 for (; tmp; tmp = tmp->next)
3847 tmp->start_of_appendix = 0;
3848 par->start_of_appendix = start;
3850 // we can set the refreshing parameters now
3851 status = LyXText::NEED_MORE_REFRESH;
3853 refresh_row = 0; // not needed for full update
3855 SetCursor(cursor.par, cursor.pos);