1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
42 #define FIX_DOUBLE_SPACE 1
48 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
56 parameters = &p->params;
60 status = LyXText::UNCHANGED;
61 LyXParagraph * par = p->paragraph;
62 current_font = GetFont(par, 0);
67 InsertParagraph(par, lastrow);
71 // set cursor at the very top position
72 selection = true; /* these setting is necessary
73 because of the delete-empty-
74 paragraph mechanism in
76 SetCursor(firstrow->par, 0);
81 // no rebreak necessary
87 // Default layouttype for copy environment type
94 // Delete all rows, this does not touch the paragraphs!
95 Row * tmprow = firstrow;
97 tmprow = firstrow->next;
104 void LyXText::owner(BufferView * bv)
106 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
110 // Gets the fully instantiated font at a given position in a paragraph
111 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
112 // The difference is that this one is used for displaying, and thus we
113 // are allowed to make cosmetic improvements. For instance make footnotes
115 // If position is -1, we get the layout font of the paragraph.
116 // If position is -2, we get the font of the manual label of the paragraph.
117 LyXFont LyXText::GetFont(LyXParagraph * par,
118 LyXParagraph::size_type pos) const
120 LyXLayout const & layout =
121 textclasslist.Style(parameters->textclass, par->GetLayout());
123 char par_depth = par->GetDepth();
124 // We specialize the 95% common case:
125 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
128 if (layout.labeltype == LABEL_MANUAL
129 && pos < BeginningOfMainBody(par)) {
131 return par->GetFontSettings(pos).
132 realize(layout.reslabelfont);
134 return par->GetFontSettings(pos).
135 realize(layout.resfont);
138 // process layoutfont for pos == -1 and labelfont for pos < -1
140 return layout.resfont;
142 return layout.reslabelfont;
146 // The uncommon case need not be optimized as much
148 LyXFont layoutfont, tmpfont;
152 if (pos < BeginningOfMainBody(par)) {
154 layoutfont = layout.labelfont;
157 layoutfont = layout.font;
159 tmpfont = par->GetFontSettings(pos);
160 tmpfont.realize(layoutfont);
163 // process layoutfont for pos == -1 and labelfont for pos < -1
165 tmpfont = layout.font;
167 tmpfont = layout.labelfont;
170 // Resolve against environment font information
171 while (par && par_depth && !tmpfont.resolved()) {
172 par = par->DepthHook(par_depth - 1);
174 tmpfont.realize(textclasslist.
175 Style(parameters->textclass,
176 par->GetLayout()).font);
177 par_depth = par->GetDepth();
181 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
183 // Cosmetic improvement: If this is an open footnote, make the font
185 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
186 && par->footnotekind == LyXParagraph::FOOTNOTE) {
194 void LyXText::SetCharFont(LyXParagraph * par,
195 LyXParagraph::size_type pos,
199 // Let the insets convert their font
200 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
201 if (par->GetInset(pos))
202 font = par->GetInset(pos)->ConvertFont(font);
205 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
208 // Get concrete layout font to reduce against
211 if (pos < BeginningOfMainBody(par))
212 layoutfont = layout.labelfont;
214 layoutfont = layout.font;
216 // Realize against environment font information
217 if (par->GetDepth()){
218 LyXParagraph * tp = par;
219 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
220 tp = tp->DepthHook(tp->GetDepth()-1);
222 layoutfont.realize(textclasslist.
223 Style(parameters->textclass,
224 tp->GetLayout()).font);
228 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
230 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
231 && par->footnotekind == LyXParagraph::FOOTNOTE) {
232 layoutfont.decSize();
235 // Now, reduce font against full layout font
236 font.reduce(layoutfont);
238 par->SetFont(pos, font);
242 /* inserts a new row behind the specified row, increments
243 * the touched counters */
244 void LyXText::InsertRow(Row * row, LyXParagraph * par,
245 LyXParagraph::size_type pos) const
247 Row * tmprow = new Row;
249 tmprow->previous = 0;
250 tmprow->next = firstrow;
253 tmprow->previous = row;
254 tmprow->next = row->next;
259 tmprow->next->previous = tmprow;
261 if (tmprow->previous)
262 tmprow->previous->next = tmprow;
270 ++number_of_rows; // one more row
274 // removes the row and reset the touched counters
275 void LyXText::RemoveRow(Row * row) const
277 /* this must not happen before the currentrow for clear reasons.
278 so the trick is just to set the current row onto the previous
281 GetRow(row->par, row->pos, unused_y);
282 currentrow = currentrow->previous;
284 currentrow_y -= currentrow->height;
289 row->next->previous = row->previous;
290 if (!row->previous) {
291 firstrow = row->next;
293 row->previous->next = row->next;
296 lastrow = row->previous;
298 height -= row->height; // the text becomes smaller
301 --number_of_rows; // one row less
305 // remove all following rows of the paragraph of the specified row.
306 void LyXText::RemoveParagraph(Row * row) const
308 LyXParagraph * tmppar = row->par;
312 while (row && row->par == tmppar) {
320 // insert the specified paragraph behind the specified row
321 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
323 InsertRow(row, par, 0); /* insert a new row, starting
326 SetCounter(par); // set the counters
328 // and now append the whole paragraph behind the new row
330 firstrow->height = 0;
331 AppendParagraph(firstrow);
333 row->next->height = 0;
334 AppendParagraph(row->next);
339 void LyXText::ToggleFootnote()
341 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
343 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
345 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
347 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
353 void LyXText::OpenStuff()
355 if (cursor.pos == 0 && cursor.par->bibkey){
356 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
358 else if (cursor.pos < cursor.par->Last()
359 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
360 && cursor.par->GetInset(cursor.pos)->Editable()) {
361 owner_->owner()->getMiniBuffer()
362 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
363 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
365 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
372 void LyXText::CloseFootnote()
374 LyXParagraph * tmppar;
375 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
377 // if the cursor is not in an open footnote, or
378 // there is no open footnote in this paragraph, just return.
379 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
382 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
383 owner_->owner()->getMiniBuffer()
384 ->Set(_("Nothing to do"));
388 // ok, move the cursor right before the footnote
389 // just a little faster than using CursorRight()
391 cursor.par->ParFromPos(cursor.pos) != par;
395 // now the cursor is at the beginning of the physical par
396 SetCursor(cursor.par,
398 cursor.par->ParFromPos(cursor.pos)->size());
400 /* we are in a footnote, so let us move at the beginning */
401 /* this is just faster than using just CursorLeft() */
404 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
405 // just a little bit faster than movin the cursor
406 tmppar = tmppar->Previous();
408 SetCursor(tmppar, tmppar->Last());
411 // the cursor must be exactly before the footnote
412 par = cursor.par->ParFromPos(cursor.pos);
414 status = LyXText::NEED_MORE_REFRESH;
415 refresh_row = cursor.row;
416 refresh_y = cursor.y - cursor.row->baseline;
419 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
420 Row * row = cursor.row;
422 tmppar->CloseFootnote(cursor.pos);
424 while (tmppar != endpar) {
425 RemoveRow(row->next);
427 tmppar = row->next->par;
432 AppendParagraph(cursor.row);
434 SetCursor(cursor.par, cursor.pos);
438 if (cursor.row->next)
439 SetHeightOfRow(cursor.row->next);
443 /* used in setlayout */
444 // Asger is not sure we want to do this...
445 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
448 LyXLayout const & layout =
449 textclasslist.Style(parameters->textclass, par->GetLayout());
451 LyXFont layoutfont, tmpfont;
452 for (LyXParagraph::size_type pos = 0;
453 pos < par->Last(); ++pos) {
454 if (pos < BeginningOfMainBody(par))
455 layoutfont = layout.labelfont;
457 layoutfont = layout.font;
459 tmpfont = par->GetFontSettings(pos);
460 tmpfont.reduce(layoutfont);
461 par->SetFont(pos, tmpfont);
466 // set layout over selection and make a total rebreak of those paragraphs
467 void LyXText::SetLayout(LyXTextClass::size_type layout)
471 // if there is no selection just set the layout
472 // of the current paragraph */
474 sel_start_cursor = cursor; // dummy selection
475 sel_end_cursor = cursor;
478 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
479 LyXParagraph * undoendpar = endpar;
481 if (endpar && endpar->GetDepth()) {
482 while (endpar && endpar->GetDepth()) {
483 endpar = endpar->LastPhysicalPar()->Next();
488 endpar = endpar->Next(); // because of parindents etc.
492 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
495 tmpcursor = cursor; /* store the current cursor */
497 /* ok we have a selection. This is always between sel_start_cursor
498 * and sel_end cursor */
499 cursor = sel_start_cursor;
501 LyXLayout const & lyxlayout =
502 textclasslist.Style(parameters->textclass, layout);
504 while (cursor.par != sel_end_cursor.par) {
505 if (cursor.par->footnoteflag ==
506 sel_start_cursor.par->footnoteflag) {
507 cursor.par->SetLayout(layout);
508 MakeFontEntriesLayoutSpecific(cursor.par);
509 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
510 fppar->added_space_top = lyxlayout.fill_top ?
511 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
512 fppar->added_space_bottom = lyxlayout.fill_bottom ?
513 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
514 if (lyxlayout.margintype == MARGIN_MANUAL)
515 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
516 if (lyxlayout.labeltype != LABEL_BIBLIO
518 delete fppar->bibkey;
522 cursor.par = cursor.par->Next();
524 if (cursor.par->footnoteflag ==
525 sel_start_cursor.par->footnoteflag) {
526 cursor.par->SetLayout(layout);
527 MakeFontEntriesLayoutSpecific(cursor.par);
528 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
529 fppar->added_space_top = lyxlayout.fill_top ?
530 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
531 fppar->added_space_bottom = lyxlayout.fill_bottom ?
532 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
533 if (lyxlayout.margintype == MARGIN_MANUAL)
534 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
535 if (lyxlayout.labeltype != LABEL_BIBLIO
537 delete fppar->bibkey;
542 RedoParagraphs(sel_start_cursor, endpar);
544 // we have to reset the selection, because the
545 // geometry could have changed */
546 SetCursor(sel_start_cursor.par, sel_start_cursor.pos, false);
548 SetCursor(sel_end_cursor.par, sel_end_cursor.pos, false);
549 UpdateCounters(cursor.row);
552 SetCursor(tmpcursor.par, tmpcursor.pos, true);
556 // increment depth over selection and
557 // make a total rebreak of those paragraphs
558 void LyXText::IncDepth()
560 // If there is no selection, just use the current paragraph
562 sel_start_cursor = cursor; // dummy selection
563 sel_end_cursor = cursor;
566 // We end at the next paragraph with depth 0
567 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
568 LyXParagraph * undoendpar = endpar;
570 if (endpar && endpar->GetDepth()) {
571 while (endpar && endpar->GetDepth()) {
572 endpar = endpar->LastPhysicalPar()->Next();
577 endpar = endpar->Next(); // because of parindents etc.
582 .par->ParFromPos(sel_start_cursor.pos)->previous,
585 LyXCursor tmpcursor = cursor; // store the current cursor
587 // ok we have a selection. This is always between sel_start_cursor
588 // and sel_end cursor
589 cursor = sel_start_cursor;
591 bool anything_changed = false;
594 // NOTE: you can't change the depth of a bibliography entry
595 if (cursor.par->footnoteflag ==
596 sel_start_cursor.par->footnoteflag
597 && textclasslist.Style(parameters->textclass,
598 cursor.par->GetLayout()
599 ).labeltype != LABEL_BIBLIO) {
600 LyXParagraph * prev =
601 cursor.par->FirstPhysicalPar()->Previous();
603 && (prev->GetDepth() - cursor.par->GetDepth() > 0
604 || (prev->GetDepth() == cursor.par->GetDepth()
605 && textclasslist.Style(parameters->textclass,
606 prev->GetLayout()).isEnvironment()))) {
607 cursor.par->FirstPhysicalPar()->depth++;
608 anything_changed = true;
611 if (cursor.par == sel_end_cursor.par)
613 cursor.par = cursor.par->Next();
616 // if nothing changed set all depth to 0
617 if (!anything_changed) {
618 cursor = sel_start_cursor;
619 while (cursor.par != sel_end_cursor.par) {
620 cursor.par->FirstPhysicalPar()->depth = 0;
621 cursor.par = cursor.par->Next();
623 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
624 cursor.par->FirstPhysicalPar()->depth = 0;
627 RedoParagraphs(sel_start_cursor, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
633 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
634 UpdateCounters(cursor.row);
637 SetCursor(tmpcursor.par, tmpcursor.pos);
641 // decrement depth over selection and
642 // make a total rebreak of those paragraphs
643 void LyXText::DecDepth()
645 // if there is no selection just set the layout
646 // of the current paragraph
648 sel_start_cursor = cursor; // dummy selection
649 sel_end_cursor = cursor;
652 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
653 LyXParagraph * undoendpar = endpar;
655 if (endpar && endpar->GetDepth()) {
656 while (endpar && endpar->GetDepth()) {
657 endpar = endpar->LastPhysicalPar()->Next();
662 endpar = endpar->Next(); // because of parindents etc.
667 .par->ParFromPos(sel_start_cursor.pos)->previous,
670 LyXCursor tmpcursor = cursor; // store the current cursor
672 // ok we have a selection. This is always between sel_start_cursor
673 // and sel_end cursor
674 cursor = sel_start_cursor;
677 if (cursor.par->footnoteflag ==
678 sel_start_cursor.par->footnoteflag) {
679 if (cursor.par->FirstPhysicalPar()->depth)
680 cursor.par->FirstPhysicalPar()->depth--;
682 if (cursor.par == sel_end_cursor.par)
684 cursor.par = cursor.par->Next();
687 RedoParagraphs(sel_start_cursor, endpar);
689 // we have to reset the selection, because the
690 // geometry could have changed
691 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
693 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
694 UpdateCounters(cursor.row);
697 SetCursor(tmpcursor.par, tmpcursor.pos);
701 // set font over selection and make a total rebreak of those paragraphs
702 void LyXText::SetFont(LyXFont const & font, bool toggleall)
704 // if there is no selection just set the current_font
706 // Determine basis font
708 if (cursor.pos < BeginningOfMainBody(cursor.par))
709 layoutfont = GetFont(cursor.par, -2);
711 layoutfont = GetFont(cursor.par, -1);
712 // Update current font
713 real_current_font.update(font, parameters->language_info, toggleall);
715 // Reduce to implicit settings
716 current_font = real_current_font;
717 current_font.reduce(layoutfont);
718 // And resolve it completely
719 real_current_font.realize(layoutfont);
723 LyXCursor tmpcursor = cursor; // store the current cursor
725 // ok we have a selection. This is always between sel_start_cursor
726 // and sel_end cursor
729 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
730 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
731 cursor = sel_start_cursor;
732 while (cursor.par != sel_end_cursor.par ||
733 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
734 && cursor.pos < sel_end_cursor.pos))
736 if (cursor.pos < cursor.par->Last()
737 && cursor.par->footnoteflag
738 == sel_start_cursor.par->footnoteflag) {
739 // an open footnote should behave
741 LyXFont newfont = GetFont(cursor.par, cursor.pos);
742 newfont.update(font, parameters->language_info, toggleall);
743 SetCharFont(cursor.par, cursor.pos, newfont);
747 cursor.par = cursor.par->Next();
751 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
753 // we have to reset the selection, because the
754 // geometry could have changed
755 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
757 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
760 SetCursor(tmpcursor.par, tmpcursor.pos);
764 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
766 Row * tmprow = cur.row;
767 long y = cur.y - tmprow->baseline;
769 SetHeightOfRow(tmprow);
770 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
771 // find the first row of the paragraph
772 if (first_phys_par != tmprow->par)
773 while (tmprow->previous
774 && tmprow->previous->par != first_phys_par) {
775 tmprow = tmprow->previous;
777 SetHeightOfRow(tmprow);
779 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
780 tmprow = tmprow->previous;
782 SetHeightOfRow(tmprow);
785 // we can set the refreshing parameters now
786 status = LyXText::NEED_MORE_REFRESH;
788 refresh_row = tmprow;
789 SetCursor(cur.par, cur.pos);
793 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
795 Row * tmprow = cur.row;
797 long y = cur.y - tmprow->baseline;
798 SetHeightOfRow(tmprow);
799 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
800 // find the first row of the paragraph
801 if (first_phys_par != tmprow->par)
802 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
803 tmprow = tmprow->previous;
806 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
807 tmprow = tmprow->previous;
811 // we can set the refreshing parameters now
812 if (status == LyXText::UNCHANGED || y < refresh_y) {
814 refresh_row = tmprow;
816 status = LyXText::NEED_MORE_REFRESH;
817 SetCursor(cur.par, cur.pos);
821 /* deletes and inserts again all paragaphs between the cursor
822 * and the specified par
823 * This function is needed after SetLayout and SetFont etc. */
824 void LyXText::RedoParagraphs(LyXCursor const & cur,
825 LyXParagraph const * endpar) const
828 LyXParagraph * tmppar, * first_phys_par;
830 Row * tmprow = cur.row;
832 long y = cur.y - tmprow->baseline;
834 if (!tmprow->previous){
835 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
837 first_phys_par = tmprow->par->FirstPhysicalPar();
838 // find the first row of the paragraph
839 if (first_phys_par != tmprow->par)
840 while (tmprow->previous &&
841 (tmprow->previous->par != first_phys_par)) {
842 tmprow = tmprow->previous;
845 while (tmprow->previous
846 && tmprow->previous->par == first_phys_par) {
847 tmprow = tmprow->previous;
852 // we can set the refreshing parameters now
853 status = LyXText::NEED_MORE_REFRESH;
855 refresh_row = tmprow->previous; /* the real refresh row will
856 be deleted, so I store
860 tmppar = tmprow->next->par;
863 while (tmppar != endpar) {
864 RemoveRow(tmprow->next);
866 tmppar = tmprow->next->par;
871 // remove the first one
872 tmprow2 = tmprow; /* this is because tmprow->previous
874 tmprow = tmprow->previous;
877 tmppar = first_phys_par;
881 InsertParagraph(tmppar, tmprow);
884 while (tmprow->next && tmprow->next->par == tmppar)
885 tmprow = tmprow->next;
886 tmppar = tmppar->Next();
888 } while (tmppar != endpar);
890 // this is because of layout changes
892 refresh_y -= refresh_row->height;
893 SetHeightOfRow(refresh_row);
895 refresh_row = firstrow;
897 SetHeightOfRow(refresh_row);
900 if (tmprow && tmprow->next)
901 SetHeightOfRow(tmprow->next);
905 int LyXText::FullRebreak()
907 if (need_break_row) {
908 BreakAgain(need_break_row);
916 /* important for the screen */
919 /* the cursor set functions have a special mechanism. When they
920 * realize, that you left an empty paragraph, they will delete it.
921 * They also delet the corresponding row */
923 // need the selection cursor:
924 void LyXText::SetSelection()
927 last_sel_cursor = sel_cursor;
928 sel_start_cursor = sel_cursor;
929 sel_end_cursor = sel_cursor;
934 // first the toggling area
935 if (cursor.y < last_sel_cursor.y ||
936 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
937 toggle_end_cursor = last_sel_cursor;
938 toggle_cursor = cursor;
941 toggle_end_cursor = cursor;
942 toggle_cursor = last_sel_cursor;
945 last_sel_cursor = cursor;
947 // and now the whole selection
949 if (sel_cursor.par == cursor.par)
950 if (sel_cursor.pos < cursor.pos) {
951 sel_end_cursor = cursor;
952 sel_start_cursor = sel_cursor;
954 sel_end_cursor = sel_cursor;
955 sel_start_cursor = cursor;
957 else if (sel_cursor.y < cursor.y ||
958 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
959 sel_end_cursor = cursor;
960 sel_start_cursor = sel_cursor;
963 sel_end_cursor = sel_cursor;
964 sel_start_cursor = cursor;
967 // a selection with no contents is not a selection
968 if (sel_start_cursor.x == sel_end_cursor.x &&
969 sel_start_cursor.y == sel_end_cursor.y)
974 void LyXText::ClearSelection() const
981 void LyXText::CursorHome() const
983 SetCursor(cursor.par, cursor.row->pos);
987 void LyXText::CursorEnd() const
989 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
990 SetCursor(cursor.par, RowLast(cursor.row) + 1);
992 if (cursor.par->Last() &&
993 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
994 || cursor.par->IsNewline(RowLast(cursor.row))))
995 SetCursor(cursor.par, RowLast(cursor.row));
997 SetCursor(cursor.par, RowLast(cursor.row) + 1);
999 if (cursor.par->table) {
1000 int cell = NumberOfCell(cursor.par, cursor.pos);
1001 if (cursor.par->table->RowHasContRow(cell) &&
1002 cursor.par->table->CellHasContRow(cell)<0) {
1003 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1004 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1006 if (cursor.par->Last() &&
1007 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1008 || cursor.par->IsNewline(RowLast(cursor.row))))
1009 SetCursor(cursor.par, RowLast(cursor.row));
1011 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1018 void LyXText::CursorTop() const
1020 while (cursor.par->Previous())
1021 cursor.par = cursor.par->Previous();
1022 SetCursor(cursor.par, 0);
1026 void LyXText::CursorBottom() const
1028 while (cursor.par->Next())
1029 cursor.par = cursor.par->Next();
1030 SetCursor(cursor.par, cursor.par->Last());
1034 /* returns a pointer to the row near the specified y-coordinate
1035 * (relative to the whole text). y is set to the real beginning
1037 Row * LyXText::GetRowNearY(long & y) const
1043 tmprow = currentrow;
1044 tmpy = currentrow_y;
1051 while (tmprow->next && tmpy + tmprow->height <= y) {
1052 tmpy += tmprow->height;
1053 tmprow = tmprow->next;
1056 while (tmprow->previous && tmpy > y) {
1057 tmprow = tmprow->previous;
1058 tmpy -= tmprow->height;
1061 currentrow = tmprow;
1062 currentrow_y = tmpy;
1064 y = tmpy; // return the real y
1069 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1071 // If the mask is completely neutral, tell user
1072 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1073 // Could only happen with user style
1074 owner_->owner()->getMiniBuffer()
1075 ->Set(_("No font change defined. Use Character under"
1076 " the Layout menu to define font change."));
1080 // Try implicit word selection
1081 LyXCursor resetCursor = cursor;
1082 int implicitSelection = SelectWordWhenUnderCursor();
1085 SetFont(font, toggleall);
1087 /* Implicit selections are cleared afterwards and cursor is set to the
1088 original position. */
1089 if (implicitSelection) {
1091 cursor = resetCursor;
1092 SetCursor( cursor.par, cursor.pos );
1093 sel_cursor = cursor;
1098 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1100 if (textclasslist.Style(parameters->textclass,
1101 par->GetLayout()).labeltype != LABEL_MANUAL)
1104 return par->BeginningOfMainBody();
1108 /* if there is a selection, reset every environment you can find
1109 * in the selection, otherwise just the environment you are in */
1110 void LyXText::MeltFootnoteEnvironment()
1112 LyXParagraph * tmppar, * firsttmppar;
1116 /* is is only allowed, if the cursor is IN an open footnote.
1117 * Otherwise it is too dangerous */
1118 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1121 SetUndo(Undo::FINISH,
1122 cursor.par->PreviousBeforeFootnote()->previous,
1123 cursor.par->NextAfterFootnote()->next);
1125 /* ok, move to the beginning of the footnote. */
1126 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1127 cursor.par = cursor.par->Previous();
1129 SetCursor(cursor.par, cursor.par->Last());
1130 /* this is just faster than using CursorLeft(); */
1132 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1133 tmppar = firsttmppar;
1134 /* tmppar is now the paragraph right before the footnote */
1136 bool first_footnote_par_is_not_empty = tmppar->next->size();
1139 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1140 tmppar = tmppar->next; /* I use next instead of Next(),
1141 * because there cannot be any
1142 * footnotes in a footnote
1144 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1146 /* remember the captions and empty paragraphs */
1147 if ((textclasslist.Style(parameters->textclass,
1148 tmppar->GetLayout())
1149 .labeltype == LABEL_SENSITIVE)
1151 tmppar->SetLayout(0);
1154 // now we will paste the ex-footnote, if the layouts allow it
1155 // first restore the layout of the paragraph right behind
1158 tmppar->next->MakeSameLayout(cursor.par);
1161 if ((!tmppar->GetLayout() && !tmppar->table)
1163 && (!tmppar->Next()->Last()
1164 || tmppar->Next()->HasSameLayout(tmppar)))) {
1165 if (tmppar->Next()->Last()
1166 && tmppar->Next()->IsLineSeparator(0))
1167 tmppar->Next()->Erase(0);
1168 tmppar->PasteParagraph();
1171 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1172 * by the pasting of the beginning */
1174 /* then the beginning */
1175 /* if there is no space between the text and the footnote, so we insert
1177 * (only if the previous par and the footnotepar are not empty!) */
1178 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1179 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1180 if (firsttmppar->size()
1181 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1182 && first_footnote_par_is_not_empty) {
1183 firsttmppar->next->InsertChar(0, ' ');
1185 firsttmppar->PasteParagraph();
1188 /* now redo the paragaphs */
1189 RedoParagraphs(cursor, tmppar);
1191 SetCursor(cursor.par, cursor.pos);
1193 /* sometimes it can happen, that there is a counter change */
1194 Row * row = cursor.row;
1195 while (row->next && row->par != tmppar && row->next->par != tmppar)
1197 UpdateCounters(row);
1204 /* the DTP switches for paragraphs. LyX will store them in the
1205 * first physicla paragraph. When a paragraph is broken, the top settings
1206 * rest, the bottom settings are given to the new one. So I can make shure,
1207 * they do not duplicate themself and you cannnot make dirty things with
1210 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1211 bool pagebreak_top, bool pagebreak_bottom,
1212 VSpace const & space_top,
1213 VSpace const & space_bottom,
1215 string labelwidthstring,
1218 LyXCursor tmpcursor = cursor;
1220 sel_start_cursor = cursor;
1221 sel_end_cursor = cursor;
1224 // make sure that the depth behind the selection are restored, too
1225 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1226 LyXParagraph * undoendpar = endpar;
1228 if (endpar && endpar->GetDepth()) {
1229 while (endpar && endpar->GetDepth()) {
1230 endpar = endpar->LastPhysicalPar()->Next();
1231 undoendpar = endpar;
1235 endpar = endpar->Next(); // because of parindents etc.
1240 .par->ParFromPos(sel_start_cursor.pos)->previous,
1244 LyXParagraph * tmppar = sel_end_cursor.par;
1245 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1246 SetCursor(tmppar->FirstPhysicalPar(), 0);
1247 status = LyXText::NEED_MORE_REFRESH;
1248 refresh_row = cursor.row;
1249 refresh_y = cursor.y - cursor.row->baseline;
1250 if (cursor.par->footnoteflag ==
1251 sel_start_cursor.par->footnoteflag) {
1252 cursor.par->line_top = line_top;
1253 cursor.par->line_bottom = line_bottom;
1254 cursor.par->pagebreak_top = pagebreak_top;
1255 cursor.par->pagebreak_bottom = pagebreak_bottom;
1256 cursor.par->added_space_top = space_top;
1257 cursor.par->added_space_bottom = space_bottom;
1258 // does the layout allow the new alignment?
1259 if (align == LYX_ALIGN_LAYOUT)
1260 align = textclasslist
1261 .Style(parameters->textclass,
1262 cursor.par->GetLayout()).align;
1263 if (align & textclasslist
1264 .Style(parameters->textclass,
1265 cursor.par->GetLayout()).alignpossible) {
1266 if (align == textclasslist
1267 .Style(parameters->textclass,
1268 cursor.par->GetLayout()).align)
1269 cursor.par->align = LYX_ALIGN_LAYOUT;
1271 cursor.par->align = align;
1273 cursor.par->SetLabelWidthString(labelwidthstring);
1274 cursor.par->noindent = noindent;
1277 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1280 RedoParagraphs(sel_start_cursor, endpar);
1283 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1284 sel_cursor = cursor;
1285 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1287 SetCursor(tmpcursor.par, tmpcursor.pos);
1291 void LyXText::SetParagraphExtraOpt(int type,
1293 char const * widthp,
1294 int alignment, bool hfill,
1295 bool start_minipage)
1297 LyXCursor tmpcursor = cursor;
1298 LyXParagraph * tmppar;
1300 sel_start_cursor = cursor;
1301 sel_end_cursor = cursor;
1304 // make sure that the depth behind the selection are restored, too
1305 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1306 LyXParagraph * undoendpar = endpar;
1308 if (endpar && endpar->GetDepth()) {
1309 while (endpar && endpar->GetDepth()) {
1310 endpar = endpar->LastPhysicalPar()->Next();
1311 undoendpar = endpar;
1315 endpar = endpar->Next(); // because of parindents etc.
1320 .par->ParFromPos(sel_start_cursor.pos)->previous,
1323 tmppar = sel_end_cursor.par;
1324 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1325 SetCursor(tmppar->FirstPhysicalPar(), 0);
1326 status = LyXText::NEED_MORE_REFRESH;
1327 refresh_row = cursor.row;
1328 refresh_y = cursor.y - cursor.row->baseline;
1329 if (cursor.par->footnoteflag ==
1330 sel_start_cursor.par->footnoteflag) {
1331 if (type == LyXParagraph::PEXTRA_NONE) {
1332 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1333 cursor.par->UnsetPExtraType();
1334 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1337 cursor.par->SetPExtraType(type, width, widthp);
1338 cursor.par->pextra_hfill = hfill;
1339 cursor.par->pextra_start_minipage = start_minipage;
1340 cursor.par->pextra_alignment = alignment;
1343 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1345 RedoParagraphs(sel_start_cursor, endpar);
1347 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1348 sel_cursor = cursor;
1349 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1351 SetCursor(tmpcursor.par, tmpcursor.pos);
1355 char loweralphaCounter(int n)
1357 if (n < 1 || n > 26)
1363 char alphaCounter(int n)
1365 if (n < 1 || n > 26)
1371 char hebrewCounter(int n)
1373 static const char hebrew[22] = {
1374 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1375 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1376 '÷', 'ø', 'ù', 'ú'
1378 if (n < 1 || n > 22)
1384 static char const * romanCounter(int n)
1386 static char const * roman[20] = {
1387 "i", "ii", "iii", "iv", "v",
1388 "vi", "vii", "viii", "ix", "x",
1389 "xi", "xii", "xiii", "xiv", "xv",
1390 "xvi", "xvii", "xviii", "xix", "xx"
1392 if (n < 1 || n > 20)
1398 // set the counter of a paragraph. This includes the labels
1399 void LyXText::SetCounter(LyXParagraph * par) const
1401 // this is only relevant for the beginning of paragraph
1402 par = par->FirstPhysicalPar();
1404 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1407 LyXTextClass const & textclass =
1408 textclasslist.TextClass(parameters->textclass);
1410 /* copy the prev-counters to this one, unless this is the start of a
1411 footnote or of a bibliography or the very first paragraph */
1413 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1414 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1415 && par->footnotekind == LyXParagraph::FOOTNOTE)
1416 && !(textclasslist.Style(parameters->textclass,
1417 par->Previous()->GetLayout()
1418 ).labeltype != LABEL_BIBLIO
1419 && layout.labeltype == LABEL_BIBLIO)) {
1420 for (int i = 0; i < 10; ++i) {
1421 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1423 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1424 if (!par->appendix && par->start_of_appendix){
1425 par->appendix = true;
1426 for (int i = 0; i < 10; ++i) {
1427 par->setCounter(i, 0);
1430 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1431 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1434 for (int i = 0; i < 10; ++i) {
1435 par->setCounter(i, 0);
1437 par->appendix = par->start_of_appendix;
1442 // if this is an open marginnote and this is the first
1443 // entry in the marginnote and the enclosing
1444 // environment is an enum/item then correct for the
1445 // LaTeX behaviour (ARRae)
1446 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1447 && par->footnotekind == LyXParagraph::MARGIN
1449 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1450 && (par->PreviousBeforeFootnote()
1451 && textclasslist.Style(parameters->textclass,
1452 par->PreviousBeforeFootnote()->GetLayout()
1453 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1454 // Any itemize or enumerate environment in a marginnote
1455 // that is embedded in an itemize or enumerate
1456 // paragraph is seen by LaTeX as being at a deeper
1457 // level within that enclosing itemization/enumeration
1458 // even if there is a "standard" layout at the start of
1464 /* Maybe we have to increment the enumeration depth.
1465 * BUT, enumeration in a footnote is considered in isolation from its
1466 * surrounding paragraph so don't increment if this is the
1467 * first line of the footnote
1468 * AND, bibliographies can't have their depth changed ie. they
1469 * are always of depth 0
1472 && par->Previous()->GetDepth() < par->GetDepth()
1473 && textclasslist.Style(parameters->textclass,
1474 par->Previous()->GetLayout()
1475 ).labeltype == LABEL_COUNTER_ENUMI
1476 && par->enumdepth < 3
1477 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1478 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1479 && par->footnotekind == LyXParagraph::FOOTNOTE)
1480 && layout.labeltype != LABEL_BIBLIO) {
1484 /* Maybe we have to decrement the enumeration depth, see note above */
1486 && par->Previous()->GetDepth() > par->GetDepth()
1487 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1488 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1489 && par->footnotekind == LyXParagraph::FOOTNOTE)
1490 && layout.labeltype != LABEL_BIBLIO) {
1491 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1492 par->setCounter(6 + par->enumdepth,
1493 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1494 /* reset the counters.
1495 * A depth change is like a breaking layout
1497 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1498 par->setCounter(i, 0);
1501 if (!par->labelstring.empty()) {
1502 par->labelstring.clear();
1505 if (layout.margintype == MARGIN_MANUAL) {
1506 if (par->labelwidthstring.empty()) {
1507 par->SetLabelWidthString(layout.labelstring());
1510 par->SetLabelWidthString(string());
1513 /* is it a layout that has an automatic label ? */
1514 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1516 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1517 if (i >= 0 && i<= parameters->secnumdepth) {
1518 par->incCounter(i); // increment the counter
1520 // Is there a label? Useful for Chapter layout
1521 if (!par->appendix){
1522 if (!layout.labelstring().empty())
1523 par->labelstring = layout.labelstring();
1525 par->labelstring.clear();
1527 if (!layout.labelstring_appendix().empty())
1528 par->labelstring = layout.labelstring_appendix();
1530 par->labelstring.clear();
1534 std::ostringstream s;
1538 if (!par->appendix) {
1539 switch (2 * LABEL_FIRST_COUNTER -
1540 textclass.maxcounter() + i) {
1541 case LABEL_COUNTER_CHAPTER:
1542 s << par->getCounter(i);
1544 case LABEL_COUNTER_SECTION:
1545 s << par->getCounter(i - 1) << '.'
1546 << par->getCounter(i);
1548 case LABEL_COUNTER_SUBSECTION:
1549 s << par->getCounter(i - 2) << '.'
1550 << par->getCounter(i - 1) << '.'
1551 << par->getCounter(i);
1553 case LABEL_COUNTER_SUBSUBSECTION:
1554 s << par->getCounter(i - 3) << '.'
1555 << par->getCounter(i - 2) << '.'
1556 << par->getCounter(i - 1) << '.'
1557 << par->getCounter(i);
1560 case LABEL_COUNTER_PARAGRAPH:
1561 s << par->getCounter(i - 4) << '.'
1562 << par->getCounter(i - 3) << '.'
1563 << par->getCounter(i - 2) << '.'
1564 << par->getCounter(i - 1) << '.'
1565 << par->getCounter(i);
1567 case LABEL_COUNTER_SUBPARAGRAPH:
1568 s << par->getCounter(i - 5) << '.'
1569 << par->getCounter(i - 4) << '.'
1570 << par->getCounter(i - 3) << '.'
1571 << par->getCounter(i - 2) << '.'
1572 << par->getCounter(i - 1) << '.'
1573 << par->getCounter(i);
1577 s << par->getCounter(i) << '.';
1580 } else { // appendix
1581 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1582 case LABEL_COUNTER_CHAPTER:
1583 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1584 s << alphaCounter(par->getCounter(i));
1586 s << hebrewCounter(par->getCounter(i));
1588 case LABEL_COUNTER_SECTION:
1589 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1590 s << alphaCounter(par->getCounter(i - 1));
1592 s << hebrewCounter(par->getCounter(i - 1));
1595 << par->getCounter(i);
1598 case LABEL_COUNTER_SUBSECTION:
1599 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1600 s << alphaCounter(par->getCounter(i - 2));
1602 s << hebrewCounter(par->getCounter(i - 2));
1605 << par->getCounter(i-1) << '.'
1606 << par->getCounter(i);
1609 case LABEL_COUNTER_SUBSUBSECTION:
1610 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1611 s << alphaCounter(par->getCounter(i-3));
1613 s << hebrewCounter(par->getCounter(i-3));
1616 << par->getCounter(i-2) << '.'
1617 << par->getCounter(i-1) << '.'
1618 << par->getCounter(i);
1621 case LABEL_COUNTER_PARAGRAPH:
1622 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1623 s << alphaCounter(par->getCounter(i-4));
1625 s << hebrewCounter(par->getCounter(i-4));
1628 << par->getCounter(i-3) << '.'
1629 << par->getCounter(i-2) << '.'
1630 << par->getCounter(i-1) << '.'
1631 << par->getCounter(i);
1634 case LABEL_COUNTER_SUBPARAGRAPH:
1635 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1636 s << alphaCounter(par->getCounter(i-5));
1638 s << hebrewCounter(par->getCounter(i-5));
1641 << par->getCounter(i-4) << '.'
1642 << par->getCounter(i-3) << '.'
1643 << par->getCounter(i-2) << '.'
1644 << par->getCounter(i-1) << '.'
1645 << par->getCounter(i);
1649 // Can this ever be reached? And in the
1650 // case it is, how can this be correct?
1652 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1658 par->labelstring += s.str().c_str();
1659 // We really want to remove the c_str as soon as
1663 char * tmps = s.str();
1664 par->labelstring += tmps;
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 for (i++; i < 10; ++i) {
1674 // reset the following counters
1675 par->setCounter(i, 0);
1677 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1678 par->incCounter(i + par->enumdepth);
1679 int number = par->getCounter(i + par->enumdepth);
1682 std::ostringstream s;
1686 switch (par->enumdepth) {
1688 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1690 << loweralphaCounter(number)
1694 << hebrewCounter(number)
1698 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1699 s << romanCounter(number) << '.';
1701 s << '.' << romanCounter(number);
1704 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1705 s << alphaCounter(number)
1709 << alphaCounter(number);
1712 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1719 par->labelstring = s.str().c_str();
1720 // we really want to get rid of that c_str()
1723 char * tmps = s.str();
1724 par->labelstring = tmps;
1728 for (i += par->enumdepth + 1; i < 10; ++i)
1729 par->setCounter(i, 0); /* reset the following counters */
1732 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1733 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1735 int number = par->getCounter(i);
1737 par->bibkey = new InsetBibKey();
1738 par->bibkey->setCounter(number);
1739 par->labelstring = layout.labelstring();
1741 // In biblio should't be following counters but...
1743 string s = layout.labelstring();
1745 // the caption hack:
1747 if (layout.labeltype == LABEL_SENSITIVE) {
1748 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1749 && (par->footnotekind == LyXParagraph::FIG
1750 || par->footnotekind == LyXParagraph::WIDE_FIG))
1751 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1755 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1756 && (par->footnotekind == LyXParagraph::TAB
1757 || par->footnotekind == LyXParagraph::WIDE_TAB))
1758 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1762 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1763 && par->footnotekind == LyXParagraph::ALGORITHM)
1764 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1767 s = ":Ãúéøåâìà ";
1769 /* par->SetLayout(0);
1770 s = layout->labelstring; */
1771 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1774 s = " :úåòîùî øñç";
1778 par->labelstring = s;
1780 /* reset the enumeration counter. They are always resetted
1781 * when there is any other layout between */
1782 for (int i = 6 + par->enumdepth; i < 10; ++i)
1783 par->setCounter(i, 0);
1788 /* Updates all counters BEHIND the row. Changed paragraphs
1789 * with a dynamic left margin will be rebroken. */
1790 void LyXText::UpdateCounters(Row * row) const
1799 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1800 par = row->par->LastPhysicalPar()->Next();
1802 par = row->par->next;
1807 while (row->par != par)
1812 /* now check for the headline layouts. remember that they
1813 * have a dynamic left margin */
1815 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1816 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1819 /* Rebreak the paragraph */
1820 RemoveParagraph(row);
1821 AppendParagraph(row);
1823 /* think about the damned open footnotes! */
1824 while (par->Next() &&
1825 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1826 || par->Next()->IsDummy())){
1828 if (par->IsDummy()) {
1829 while (row->par != par)
1831 RemoveParagraph(row);
1832 AppendParagraph(row);
1837 par = par->LastPhysicalPar()->Next();
1843 /* insets an inset. */
1844 void LyXText::InsertInset(Inset *inset)
1846 SetUndo(Undo::INSERT,
1847 cursor.par->ParFromPos(cursor.pos)->previous,
1848 cursor.par->ParFromPos(cursor.pos)->next);
1849 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1850 cursor.par->InsertInset(cursor.pos, inset);
1851 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1852 * The character will not be inserted a
1857 // this is for the simple cut and paste mechanism
1858 static LyXParagraph * simple_cut_buffer = 0;
1859 static char simple_cut_buffer_textclass = 0;
1861 void DeleteSimpleCutBuffer()
1863 if (!simple_cut_buffer)
1865 LyXParagraph * tmppar;
1867 while (simple_cut_buffer) {
1868 tmppar = simple_cut_buffer;
1869 simple_cut_buffer = simple_cut_buffer->next;
1872 simple_cut_buffer = 0;
1876 void LyXText::copyEnvironmentType()
1878 copylayouttype = cursor.par->GetLayout();
1882 void LyXText::pasteEnvironmentType()
1884 SetLayout(copylayouttype);
1888 void LyXText::CutSelection(bool doclear)
1890 // This doesn't make sense, if there is no selection
1894 // OK, we have a selection. This is always between sel_start_cursor
1895 // and sel_end cursor
1896 LyXParagraph * tmppar;
1898 // Check whether there are half footnotes in the selection
1899 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1900 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1901 tmppar = sel_start_cursor.par;
1902 while (tmppar != sel_end_cursor.par){
1903 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1904 WriteAlert(_("Impossible operation"),
1905 _("Don't know what to do with half floats."),
1909 tmppar = tmppar->Next();
1913 /* table stuff -- begin */
1914 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1915 if ( sel_start_cursor.par != sel_end_cursor.par) {
1916 WriteAlert(_("Impossible operation"),
1917 _("Don't know what to do with half tables."),
1921 sel_start_cursor.par->table->Reinit();
1923 /* table stuff -- end */
1925 // make sure that the depth behind the selection are restored, too
1926 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1927 LyXParagraph * undoendpar = endpar;
1929 if (endpar && endpar->GetDepth()) {
1930 while (endpar && endpar->GetDepth()) {
1931 endpar = endpar->LastPhysicalPar()->Next();
1932 undoendpar = endpar;
1934 } else if (endpar) {
1935 endpar = endpar->Next(); // because of parindents etc.
1938 SetUndo(Undo::DELETE,
1940 .par->ParFromPos(sel_start_cursor.pos)->previous,
1943 // clear the simple_cut_buffer
1944 DeleteSimpleCutBuffer();
1946 // set the textclass
1947 simple_cut_buffer_textclass = parameters->textclass;
1949 #ifdef WITH_WARNINGS
1950 #warning Asger: Make cut more intelligent here.
1953 White paper for "intelligent" cutting:
1955 Example: "This is our text."
1956 Using " our " as selection, cutting will give "This istext.".
1957 Using "our" as selection, cutting will give "This is text.".
1958 Using " our" as selection, cutting will give "This is text.".
1959 Using "our " as selection, cutting will give "This is text.".
1961 All those four selections will (however) paste identically:
1962 Pasting with the cursor right after the "is" will give the
1963 original text with all four selections.
1965 The rationale is to be intelligent such that words are copied,
1966 cut and pasted in a functional manner.
1968 This is not implemented yet. (Asger)
1970 The changes below sees to do a lot of what you want. However
1971 I have not verified that all cases work as they should:
1973 - cut in multiple row
1975 - cut across footnotes and paragraph
1976 My simplistic tests show that the idea are basically sound but
1977 there are some items to fix up...we only need to find them
1980 As do redo Asger's example above (with | beeing the cursor in the
1981 result after cutting.):
1983 Example: "This is our text."
1984 Using " our " as selection, cutting will give "This is|text.".
1985 Using "our" as selection, cutting will give "This is | text.".
1986 Using " our" as selection, cutting will give "This is| text.".
1987 Using "our " as selection, cutting will give "This is |text.".
1992 #ifndef FIX_DOUBLE_SPACE
1993 bool space_wrapped =
1994 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1995 if (sel_end_cursor.pos > 0
1996 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1997 // please break before a space at the end
1998 sel_end_cursor.pos--;
1999 space_wrapped = true;
2001 // cut behind a space if there is one
2002 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2003 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2004 && (sel_start_cursor.par != sel_end_cursor.par
2005 || sel_start_cursor.pos < sel_end_cursor.pos))
2006 sel_start_cursor.pos++;
2008 // there are two cases: cut only within one paragraph or
2009 // more than one paragraph
2011 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2012 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2013 // only within one paragraph
2014 simple_cut_buffer = new LyXParagraph;
2015 LyXParagraph::size_type i =
2016 sel_start_cursor.pos;
2017 for (; i < sel_end_cursor.pos; ++i) {
2018 /* table stuff -- begin */
2019 if (sel_start_cursor.par->table
2020 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2021 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2022 sel_start_cursor.pos++;
2024 /* table stuff -- end */
2025 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2026 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2028 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2030 #ifndef FIX_DOUBLE_SPACE
2031 // check for double spaces
2032 if (sel_start_cursor.pos &&
2033 sel_start_cursor.par->Last() > sel_start_cursor.pos
2034 && sel_start_cursor.par
2035 ->IsLineSeparator(sel_start_cursor.pos - 1)
2036 && sel_start_cursor.par
2037 ->IsLineSeparator(sel_start_cursor.pos)) {
2038 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2041 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
2044 endpar = sel_end_cursor.par->Next();
2046 // cut more than one paragraph
2049 ->BreakParagraphConservative(sel_end_cursor.pos);
2050 #ifndef FIX_DOUBLE_SPACE
2051 // insert a space at the end if there was one
2054 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2056 sel_end_cursor.par = sel_end_cursor.par->Next();
2057 sel_end_cursor.pos = 0;
2059 cursor = sel_end_cursor;
2061 #ifndef FIX_DOUBLE_SPACE
2062 // please break behind a space, if there is one.
2063 // The space should be copied too
2064 if (sel_start_cursor.par
2065 ->IsLineSeparator(sel_start_cursor.pos))
2066 sel_start_cursor.pos++;
2068 sel_start_cursor.par
2069 ->BreakParagraphConservative(sel_start_cursor.pos);
2070 #ifndef FIX_DOUBLE_SPACE
2071 if (!sel_start_cursor.pos
2072 || sel_start_cursor.par
2073 ->IsLineSeparator(sel_start_cursor.pos - 1)
2074 || sel_start_cursor.par
2075 ->IsNewline(sel_start_cursor.pos - 1)) {
2076 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2079 // store the endparagraph for redoing later
2080 endpar = sel_end_cursor.par->Next(); /* needed because
2085 // store the selection
2086 simple_cut_buffer = sel_start_cursor.par
2087 ->ParFromPos(sel_start_cursor.pos)->next;
2088 simple_cut_buffer->previous = 0;
2089 sel_end_cursor.par->previous->next = 0;
2091 // cut the selection
2092 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2093 = sel_end_cursor.par;
2095 sel_end_cursor.par->previous
2096 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2098 // care about footnotes
2099 if (simple_cut_buffer->footnoteflag) {
2100 LyXParagraph * tmppar = simple_cut_buffer;
2102 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2103 tmppar = tmppar->next;
2107 // the cut selection should begin with standard layout
2108 simple_cut_buffer->Clear();
2110 // paste the paragraphs again, if possible
2112 sel_start_cursor.par->Next()->ClearParagraph();
2113 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2115 !sel_start_cursor.par->Next()->Last())
2116 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2118 #ifndef FIX_DOUBLE_SPACE
2119 // maybe a forgotten blank
2120 if (sel_start_cursor.pos
2121 && sel_start_cursor.par
2122 ->IsLineSeparator(sel_start_cursor.pos)
2123 && sel_start_cursor.par
2124 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2125 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2130 // sometimes necessary
2132 sel_start_cursor.par->ClearParagraph();
2134 RedoParagraphs(sel_start_cursor, endpar);
2137 cursor = sel_start_cursor;
2138 SetCursor(cursor.par, cursor.pos);
2139 sel_cursor = cursor;
2140 UpdateCounters(cursor.row);
2144 void LyXText::CopySelection()
2146 // this doesnt make sense, if there is no selection
2150 // ok we have a selection. This is always between sel_start_cursor
2151 // and sel_end cursor
2152 LyXParagraph * tmppar;
2154 /* check wether there are half footnotes in the selection */
2155 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2156 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2157 tmppar = sel_start_cursor.par;
2158 while (tmppar != sel_end_cursor.par) {
2159 if (tmppar->footnoteflag !=
2160 sel_end_cursor.par->footnoteflag) {
2161 WriteAlert(_("Impossible operation"),
2162 _("Don't know what to do"
2163 " with half floats."),
2167 tmppar = tmppar->Next();
2171 /* table stuff -- begin */
2172 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2173 if ( sel_start_cursor.par != sel_end_cursor.par){
2174 WriteAlert(_("Impossible operation"),
2175 _("Don't know what to do with half tables."),
2180 /* table stuff -- end */
2182 // delete the simple_cut_buffer
2183 DeleteSimpleCutBuffer();
2185 // set the textclass
2186 simple_cut_buffer_textclass = parameters->textclass;
2188 #ifdef FIX_DOUBLE_SPACE
2189 // copy behind a space if there is one
2190 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2191 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2192 && (sel_start_cursor.par != sel_end_cursor.par
2193 || sel_start_cursor.pos < sel_end_cursor.pos))
2194 sel_start_cursor.pos++;
2196 // there are two cases: copy only within one paragraph
2197 // or more than one paragraph
2198 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2199 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2200 // only within one paragraph
2201 simple_cut_buffer = new LyXParagraph;
2202 LyXParagraph::size_type i = 0;
2203 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2204 sel_start_cursor.par->CopyIntoMinibuffer(i);
2205 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2208 // copy more than one paragraph
2209 // clone the paragraphs within the selection
2211 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2212 simple_cut_buffer = tmppar->Clone();
2213 LyXParagraph *tmppar2 = simple_cut_buffer;
2215 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2217 tmppar = tmppar->next;
2218 tmppar2->next = tmppar->Clone();
2219 tmppar2->next->previous = tmppar2;
2220 tmppar2 = tmppar2->next;
2224 // care about footnotes
2225 if (simple_cut_buffer->footnoteflag) {
2226 tmppar = simple_cut_buffer;
2228 tmppar->footnoteflag =
2229 LyXParagraph::NO_FOOTNOTE;
2230 tmppar = tmppar->next;
2234 // the simple_cut_buffer paragraph is too big
2235 LyXParagraph::size_type tmpi2 =
2236 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2237 for (; tmpi2; --tmpi2)
2238 simple_cut_buffer->Erase(0);
2240 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2242 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2243 while (tmppar2->size() > tmpi2) {
2244 tmppar2->Erase(tmppar2->size() - 1);
2250 void LyXText::PasteSelection()
2252 // this does not make sense, if there is nothing to paste
2253 if (!simple_cut_buffer)
2256 LyXParagraph * tmppar;
2257 LyXParagraph * endpar;
2259 LyXCursor tmpcursor;
2261 // be carefull with footnotes in footnotes
2262 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2264 // check whether the cut_buffer includes a footnote
2265 tmppar = simple_cut_buffer;
2267 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2268 tmppar = tmppar->next;
2271 WriteAlert(_("Impossible operation"),
2272 _("Can't paste float into float!"),
2278 /* table stuff -- begin */
2279 if (cursor.par->table) {
2280 if (simple_cut_buffer->next) {
2281 WriteAlert(_("Impossible operation"),
2282 _("Table cell cannot include more than one paragraph!"),
2287 /* table stuff -- end */
2289 SetUndo(Undo::INSERT,
2290 cursor.par->ParFromPos(cursor.pos)->previous,
2291 cursor.par->ParFromPos(cursor.pos)->next);
2295 // There are two cases: cutbuffer only one paragraph or many
2296 if (!simple_cut_buffer->next) {
2297 // only within a paragraph
2299 #ifndef FIX_DOUBLE_SPACE
2300 // please break behind a space, if there is one
2301 while (tmpcursor.par->Last() > tmpcursor.pos
2302 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2305 tmppar = simple_cut_buffer->Clone();
2306 /* table stuff -- begin */
2307 bool table_too_small = false;
2308 if (tmpcursor.par->table) {
2309 while (simple_cut_buffer->size()
2310 && !table_too_small) {
2311 if (simple_cut_buffer->IsNewline(0)){
2312 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2314 simple_cut_buffer->Erase(0);
2315 if (tmpcursor.pos < tmpcursor.par->Last())
2318 table_too_small = true;
2320 #ifdef FIX_DOUBLE_SPACE
2321 // This is an attempt to fix the
2322 // "never insert a space at the
2323 // beginning of a paragraph" problem.
2324 if (tmpcursor.pos == 0
2325 && simple_cut_buffer->IsLineSeparator(0)) {
2326 simple_cut_buffer->Erase(0);
2328 simple_cut_buffer->CutIntoMinibuffer(0);
2329 simple_cut_buffer->Erase(0);
2330 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2334 simple_cut_buffer->CutIntoMinibuffer(0);
2335 simple_cut_buffer->Erase(0);
2336 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2342 /* table stuff -- end */
2343 // Some provisions should be done here for checking
2344 // if we are inserting at the beginning of a
2345 // paragraph. If there are a space at the beginning
2346 // of the text to insert and we are inserting at
2347 // the beginning of the paragraph the space should
2349 while (simple_cut_buffer->size()) {
2350 #ifdef FIX_DOUBLE_SPACE
2351 // This is an attempt to fix the
2352 // "never insert a space at the
2353 // beginning of a paragraph" problem.
2354 if (tmpcursor.pos == 0
2355 && simple_cut_buffer->IsLineSeparator(0)) {
2356 simple_cut_buffer->Erase(0);
2358 simple_cut_buffer->CutIntoMinibuffer(0);
2359 simple_cut_buffer->Erase(0);
2360 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2364 simple_cut_buffer->CutIntoMinibuffer(0);
2365 simple_cut_buffer->Erase(0);
2366 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2371 delete simple_cut_buffer;
2372 simple_cut_buffer = tmppar;
2373 endpar = tmpcursor.par->Next();
2377 // make a copy of the simple cut_buffer
2378 tmppar = simple_cut_buffer;
2379 LyXParagraph * simple_cut_clone = tmppar->Clone();
2380 LyXParagraph * tmppar2 = simple_cut_clone;
2381 if (cursor.par->footnoteflag){
2382 tmppar->footnoteflag = cursor.par->footnoteflag;
2383 tmppar->footnotekind = cursor.par->footnotekind;
2385 while (tmppar->next) {
2386 tmppar = tmppar->next;
2387 tmppar2->next = tmppar->Clone();
2388 tmppar2->next->previous = tmppar2;
2389 tmppar2 = tmppar2->next;
2390 if (cursor.par->footnoteflag){
2391 tmppar->footnoteflag = cursor.par->footnoteflag;
2392 tmppar->footnotekind = cursor.par->footnotekind;
2396 // make sure there is no class difference
2397 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2398 parameters->textclass,
2401 // make the simple_cut_buffer exactly the same layout than
2402 // the cursor paragraph
2403 simple_cut_buffer->MakeSameLayout(cursor.par);
2405 // find the end of the buffer
2406 LyXParagraph * lastbuffer = simple_cut_buffer;
2407 while (lastbuffer->Next())
2408 lastbuffer = lastbuffer->Next();
2410 #ifndef FIX_DOUBLE_SPACE
2411 // Please break behind a space, if there is one. The space
2412 // should be copied too.
2413 if (cursor.par->Last() > cursor.pos
2414 && cursor.par->IsLineSeparator(cursor.pos))
2417 bool paste_the_end = false;
2419 // open the paragraph for inserting the simple_cut_buffer
2421 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2422 cursor.par->BreakParagraphConservative(cursor.pos);
2423 paste_the_end = true;
2426 #ifndef FIX_DOUBLE_SPACE
2427 // be careful with double spaces
2428 if ((!cursor.par->Last()
2429 || cursor.par->IsLineSeparator(cursor.pos - 1)
2430 || cursor.par->IsNewline(cursor.pos - 1))
2431 && simple_cut_buffer->text.size()
2432 && simple_cut_buffer->IsLineSeparator(0))
2433 simple_cut_buffer->Erase(0);
2435 // set the end for redoing later
2436 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2439 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2440 cursor.par->ParFromPos(cursor.pos)->next;
2441 cursor.par->ParFromPos(cursor.pos)->next->previous =
2442 lastbuffer->ParFromPos(lastbuffer->Last());
2444 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2445 simple_cut_buffer->previous =
2446 cursor.par->ParFromPos(cursor.pos);
2448 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2449 lastbuffer = cursor.par;
2451 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2453 // store the new cursor position
2454 tmpcursor.par = lastbuffer;
2455 tmpcursor.pos = lastbuffer->Last();
2457 // maybe some pasting
2458 if (lastbuffer->Next() && paste_the_end) {
2459 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2460 #ifndef FIX_DOUBLE_SPACE
2461 // be careful with double spaces
2462 if ((!lastbuffer->Last()
2463 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2464 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2465 && lastbuffer->Next()->Last()
2466 && lastbuffer->Next()->IsLineSeparator(0))
2467 lastbuffer->Next()->Erase(0);
2469 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2471 } else if (!lastbuffer->Next()->Last()) {
2472 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2473 #ifndef FIX_DOUBLE_SPACE
2474 // be careful witth double spaces
2475 if ((!lastbuffer->Last()
2476 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2477 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2478 && lastbuffer->Next()->Last()
2479 && lastbuffer->Next()->IsLineSeparator(0))
2480 lastbuffer->Next()->Erase(0);
2482 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2484 } else if (!lastbuffer->Last()) {
2485 lastbuffer->MakeSameLayout(lastbuffer->next);
2486 #ifndef FIX_DOUBLE_SPACE
2487 // be careful witth double spaces
2488 if ((!lastbuffer->Last()
2489 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2490 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2491 && lastbuffer->Next()->Last()
2492 && lastbuffer->Next()->IsLineSeparator(0))
2493 lastbuffer->Next()->Erase(0);
2495 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2498 lastbuffer->Next()->ClearParagraph();
2501 // restore the simple cut buffer
2502 simple_cut_buffer = simple_cut_clone;
2505 RedoParagraphs(cursor, endpar);
2507 SetCursor(cursor.par, cursor.pos);
2510 sel_cursor = cursor;
2511 SetCursor(tmpcursor.par, tmpcursor.pos);
2513 UpdateCounters(cursor.row);
2517 // returns a pointer to the very first LyXParagraph
2518 LyXParagraph * LyXText::FirstParagraph() const
2520 return params->paragraph;
2524 // returns true if the specified string is at the specified position
2525 bool LyXText::IsStringInText(LyXParagraph * par,
2526 LyXParagraph::size_type pos,
2527 char const * str) const
2531 while (pos + i < par->Last() && str[i] &&
2532 str[i] == par->GetChar(pos + i)) {
2542 // sets the selection over the number of characters of string, no check!!
2543 void LyXText::SetSelectionOverString(char const * string)
2545 sel_cursor = cursor;
2546 for (int i = 0; string[i]; ++i)
2552 // simple replacing. The font of the first selected character is used
2553 void LyXText::ReplaceSelectionWithString(char const * str)
2558 if (!selection) { // create a dummy selection
2559 sel_end_cursor = cursor;
2560 sel_start_cursor = cursor;
2563 // Get font setting before we cut
2564 LyXParagraph::size_type pos = sel_end_cursor.pos;
2565 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2567 // Insert the new string
2568 for (int i = 0; str[i]; ++i) {
2569 sel_end_cursor.par->InsertChar(pos, str[i]);
2570 sel_end_cursor.par->SetFont(pos, font);
2574 // Cut the selection
2581 // if the string can be found: return true and set the cursor to
2583 bool LyXText::SearchForward(char const * str) const
2585 LyXParagraph * par = cursor.par;
2586 LyXParagraph::size_type pos = cursor.pos;
2587 while (par && !IsStringInText(par, pos, str)) {
2588 if (pos < par->Last() - 1)
2596 SetCursor(par, pos);
2604 bool LyXText::SearchBackward(char const * string) const
2606 LyXParagraph * par = cursor.par;
2607 int pos = cursor.pos;
2613 // We skip empty paragraphs (Asger)
2615 par = par->Previous();
2617 pos = par->Last() - 1;
2618 } while (par && pos < 0);
2620 } while (par && !IsStringInText(par, pos, string));
2623 SetCursor(par, pos);
2630 // needed to insert the selection
2631 void LyXText::InsertStringA(string const & str)
2633 LyXParagraph * par = cursor.par;
2634 LyXParagraph::size_type pos = cursor.pos;
2635 LyXParagraph::size_type a = 0;
2637 LyXParagraph * endpar = cursor.par->Next();
2642 textclasslist.Style(parameters->textclass,
2643 cursor.par->GetLayout()).isEnvironment();
2644 // only to be sure, should not be neccessary
2647 // insert the string, don't insert doublespace
2648 string::size_type i = 0;
2649 while (i < str.length()) {
2650 if (str[i] != '\n') {
2652 && i + 1 < str.length() && str[i + 1] != ' '
2653 && pos && par->GetChar(pos - 1)!= ' ') {
2654 par->InsertChar(pos,' ');
2655 par->SetFont(pos, current_font);
2657 } else if (par->table) {
2658 if (str[i] == '\t') {
2659 while((pos < par->size()) &&
2660 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2662 if (pos < par->size())
2664 else // no more fields to fill skip the rest
2666 } else if ((str[i] != 13) &&
2667 ((str[i] & 127) >= ' ')) {
2668 par->InsertChar(pos, str[i]);
2669 par->SetFont(pos, current_font);
2672 } else if (str[i] == ' ') {
2674 InsetSpecialChar * new_inset =
2675 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2676 par->InsertChar(pos, LyXParagraph::META_INSET);
2677 par->SetFont(pos, current_font);
2678 par->InsertInset(pos, new_inset);
2680 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2681 par->SetFont(pos, current_font);
2684 } else if (str[i] == '\t') {
2685 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2687 InsetSpecialChar * new_inset =
2688 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2689 par->InsertChar(pos, LyXParagraph::META_INSET);
2690 par->SetFont(pos, current_font);
2691 par->InsertInset(pos, new_inset);
2693 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2694 par->SetFont(a, current_font);
2698 } else if (str[i] != 13 &&
2699 // Ignore unprintables
2700 (str[i] & 127) >= ' ') {
2701 par->InsertChar(pos, str[i]);
2702 par->SetFont(pos, current_font);
2707 if (i + 1 >= str.length()) {
2711 while((pos < par->size()) &&
2712 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2715 cell = NumberOfCell(par, pos);
2716 while((pos < par->size()) &&
2717 !(par->table->IsFirstCell(cell))) {
2719 while((pos < par->size()) &&
2720 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2723 cell = NumberOfCell(par, pos);
2725 if (pos >= par->size())
2726 // no more fields to fill skip the rest
2729 if (!par->size()) { // par is empty
2731 InsetSpecialChar * new_inset =
2732 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2733 par->InsertChar(pos, LyXParagraph::META_INSET);
2734 par->SetFont(pos, current_font);
2735 par->InsertInset(pos, new_inset);
2737 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2738 par->SetFont(pos, current_font);
2742 par->BreakParagraph(pos, flag);
2750 RedoParagraphs(cursor, endpar);
2751 SetCursor(cursor.par, cursor.pos);
2752 sel_cursor = cursor;
2753 SetCursor(par, pos);
2758 /* turns double-CR to single CR, others where converted into one blank and 13s
2759 * that are ignored .Double spaces are also converted into one. Spaces at
2760 * the beginning of a paragraph are forbidden. tabs are converted into one
2761 * space. then InsertStringA is called */
2762 void LyXText::InsertStringB(string const & s)
2765 LyXParagraph * par = cursor.par;
2766 string::size_type i = 1;
2767 while (i < str.length()) {
2768 if (str[i] == '\t' && !par->table)
2770 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2772 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2773 if (str[i + 1] != '\n') {
2774 if (str[i - 1] != ' ')
2779 while (i + 1 < str.length()
2780 && (str[i + 1] == ' '
2781 || str[i + 1] == '\t'
2782 || str[i + 1] == '\n'
2783 || str[i + 1] == 13)) {
2794 bool LyXText::GotoNextError() const
2796 LyXCursor res = cursor;
2798 if (res.pos < res.par->Last() - 1) {
2802 res.par = res.par->Next();
2807 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2808 && res.par->GetInset(res.pos)->AutoDelete()));
2811 SetCursor(res.par, res.pos);
2818 bool LyXText::GotoNextNote() const
2820 LyXCursor res = cursor;
2822 if (res.pos < res.par->Last() - 1) {
2825 res.par = res.par->Next();
2830 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2831 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2834 SetCursor(res.par, res.pos);
2841 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2842 LyXTextClassList::size_type class2,
2846 if (!par || class1 == class2)
2848 par = par->FirstPhysicalPar();
2850 string name = textclasslist.NameOfLayout(class1, par->layout);
2852 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2853 textclasslist.NumberOfLayout(class2, name);
2856 } else { // layout not found
2857 // use default layout "Standard" (0)
2862 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2864 string s = "Layout had to be changed from\n"
2865 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2866 + "\nbecause of class conversion from\n"
2867 + textclasslist.NameOfClass(class1) + " to "
2868 + textclasslist.NameOfClass(class2);
2869 InsetError * new_inset = new InsetError(s);
2870 par->InsertChar(0, LyXParagraph::META_INSET);
2871 par->InsertInset(0, new_inset);
2880 void LyXText::CheckParagraph(LyXParagraph * par,
2881 LyXParagraph::size_type pos)
2884 LyXCursor tmpcursor;
2886 /* table stuff -- begin*/
2889 CheckParagraphInTable(par, pos);
2892 /* table stuff -- end*/
2895 LyXParagraph::size_type z;
2896 Row * row = GetRow(par, pos, y);
2898 // is there a break one row above
2899 if (row->previous && row->previous->par == row->par) {
2900 z = NextBreakPoint(row->previous, paperwidth);
2901 if ( z >= row->pos) {
2902 // set the dimensions of the row above
2903 y -= row->previous->height;
2905 refresh_row = row->previous;
2906 status = LyXText::NEED_MORE_REFRESH;
2908 BreakAgain(row->previous);
2910 // set the cursor again. Otherwise
2911 // dangling pointers are possible
2912 SetCursor(cursor.par, cursor.pos);
2913 sel_cursor = cursor;
2918 int tmpheight = row->height;
2919 LyXParagraph::size_type tmplast = RowLast(row);
2924 if (row->height == tmpheight && RowLast(row) == tmplast)
2925 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2927 status = LyXText::NEED_MORE_REFRESH;
2929 // check the special right address boxes
2930 if (textclasslist.Style(parameters->textclass,
2931 par->GetLayout()).margintype
2932 == MARGIN_RIGHT_ADDRESS_BOX) {
2933 tmpcursor.par = par;
2934 tmpcursor.row = row;
2937 tmpcursor.x_fix = 0;
2938 tmpcursor.pos = pos;
2939 RedoDrawingOfParagraph(tmpcursor);
2944 // set the cursor again. Otherwise dangling pointers are possible
2945 // also set the selection
2949 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2950 sel_cursor = cursor;
2951 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2952 sel_start_cursor = cursor;
2953 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2954 sel_end_cursor = cursor;
2955 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2956 last_sel_cursor = cursor;
2959 SetCursorIntern(cursor.par, cursor.pos);
2963 // returns 0 if inset wasn't found
2964 int LyXText::UpdateInset(Inset * inset)
2966 // first check the current paragraph
2967 int pos = cursor.par->GetPositionOfInset(inset);
2969 CheckParagraph(cursor.par, pos);
2973 // check every paragraph
2975 LyXParagraph * par = FirstParagraph();
2977 // make sure the paragraph is open
2978 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2979 pos = par->GetPositionOfInset(inset);
2981 CheckParagraph(par, pos);
2992 void LyXText::SetCursor(LyXParagraph * par,
2993 LyXParagraph::size_type pos, bool setfont) const
2995 LyXCursor old_cursor = cursor;
2996 SetCursorIntern(par, pos, setfont);
2997 DeleteEmptyParagraphMechanism(old_cursor);
3001 void LyXText::SetCursorIntern(LyXParagraph * par,
3002 LyXParagraph::size_type pos, bool setfont) const
3004 // correct the cursor position if impossible
3005 if (pos > par->Last()){
3006 LyXParagraph * tmppar = par->ParFromPos(pos);
3007 pos = par->PositionInParFromPos(pos);
3010 if (par->IsDummy() && par->previous &&
3011 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3012 while (par->previous &&
3013 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3014 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3015 par = par->previous ;
3016 if (par->IsDummy() &&
3017 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3018 pos += par->size() + 1;
3020 if (par->previous) {
3021 par = par->previous;
3023 pos += par->size() + 1;
3031 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3032 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3033 && !cursor.par->IsSeparator(cursor.pos))
3034 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3036 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3037 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3039 current_font = cursor.par->GetFontSettings(cursor.pos);
3040 real_current_font = GetFont(cursor.par, cursor.pos);
3043 /* get the cursor y position in text */
3045 Row * row = GetRow(par, pos, y);
3046 /* y is now the beginning of the cursor row */
3048 /* y is now the cursor baseline */
3051 /* now get the cursors x position */
3053 float fill_separator, fill_hfill, fill_label_hfill;
3054 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3055 LyXParagraph::size_type cursor_vpos;
3056 LyXParagraph::size_type last = RowLast(row);
3057 if (row->pos > last)
3059 else if (pos > last)
3060 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3061 ? log2vis(last)+1 : log2vis(last);
3063 LyXDirection letter_direction =
3064 row->par->getLetterDirection(pos);
3065 LyXDirection font_direction =
3066 (real_current_font.isVisibleRightToLeft())
3067 ? LYX_DIR_RIGHT_TO_LEFT : LYX_DIR_LEFT_TO_RIGHT;
3068 if (letter_direction == font_direction
3070 || (row->par->table && row->par->IsNewline(pos-1)))
3071 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3072 ? log2vis(pos) : log2vis(pos) + 1;
3074 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3075 ? log2vis(pos-1) + 1 : log2vis(pos - 1);
3078 /* table stuff -- begin*/
3079 if (row->par->table) {
3080 int cell = NumberOfCell(row->par, row->pos);
3082 x += row->par->table->GetBeginningOfTextInCell(cell);
3083 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3084 pos = vis2log(vpos);
3085 if (row->par->IsNewline(pos)) {
3086 x = x_old + row->par->table->WidthOfColumn(cell);
3089 x += row->par->table->GetBeginningOfTextInCell(cell);
3091 x += SingleWidth(row->par, pos);
3095 /* table stuff -- end*/
3096 LyXParagraph::size_type main_body =
3097 BeginningOfMainBody(row->par);
3098 if (main_body > 0 &&
3099 (main_body-1 > last ||
3100 !row->par->IsLineSeparator(main_body-1)))
3103 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3104 pos = vis2log(vpos);
3105 if (main_body > 0 && pos == main_body-1) {
3106 x += fill_label_hfill +
3107 lyxfont::width(textclasslist
3108 .Style(parameters->textclass,
3109 row->par->GetLayout())
3111 GetFont(row->par, -2));
3112 if (row->par->IsLineSeparator(main_body-1))
3113 x -= SingleWidth(row->par, main_body-1);
3115 if (HfillExpansion(row, pos)) {
3116 x += SingleWidth(row->par, pos);
3117 if (pos >= main_body)
3120 x += fill_label_hfill;
3122 else if (row->par->IsSeparator(pos)) {
3125 row->next->par != row->par ||
3126 row->par->getParDirection() ==
3127 row->par->getLetterDirection(last)) {
3128 x += SingleWidth(row->par, pos);
3129 if (pos >= main_body)
3130 x += fill_separator;
3133 x += SingleWidth(row->par, pos);
3139 cursor.x_fix = cursor.x;
3144 void LyXText::SetCursorFromCoordinates(int x, long y) const
3146 LyXCursor old_cursor = cursor;
3148 /* get the row first */
3150 Row * row = GetRowNearY(y);
3152 cursor.par = row->par;
3154 int column = GetColumnNearX(row, x);
3155 cursor.pos = row->pos + column;
3157 cursor.y = y + row->baseline;
3162 (cursor.pos == cursor.par->Last()
3163 || cursor.par->IsSeparator(cursor.pos)
3164 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3165 && !cursor.par->IsSeparator(cursor.pos))
3166 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3168 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3169 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3171 current_font = cursor.par->GetFontSettings(cursor.pos);
3172 real_current_font = GetFont(cursor.par, cursor.pos);
3174 DeleteEmptyParagraphMechanism(old_cursor);
3177 void LyXText::SetCursorFromCoordinates(LyXCursor & cur, int x, long y) const
3179 /* get the row first */
3181 Row * row = GetRowNearY(y);
3182 int column = GetColumnNearX(row, x);
3185 cur.pos = row->pos + column;
3187 cur.y = y + row->baseline;
3192 void LyXText::CursorLeft() const
3195 if (cursor.par->table) {
3196 int cell = NumberOfCell(cursor.par, cursor.pos);
3197 if (cursor.par->table->IsContRow(cell) &&
3198 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell)) < 0) {
3205 void LyXText::CursorLeftIntern() const
3207 if (cursor.pos > 0) {
3208 SetCursor(cursor.par, cursor.pos - 1);
3210 else if (cursor.par->Previous()) { // steps into the above paragraph.
3211 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3216 void LyXText::CursorRight() const
3218 CursorRightIntern();
3219 if (cursor.par->table) {
3220 int cell = NumberOfCell(cursor.par, cursor.pos);
3221 if (cursor.par->table->IsContRow(cell) &&
3222 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3229 void LyXText::CursorRightIntern() const
3231 if (cursor.pos < cursor.par->Last()) {
3232 SetCursor(cursor.par, cursor.pos + 1);
3234 else if (cursor.par->Next()) {
3235 SetCursor(cursor.par->Next(), 0);
3240 void LyXText::CursorUp() const
3242 SetCursorFromCoordinates(cursor.x_fix,
3243 cursor.y - cursor.row->baseline - 1);
3244 if (cursor.par->table) {
3245 int cell = NumberOfCell(cursor.par, cursor.pos);
3246 if (cursor.par->table->IsContRow(cell) &&
3247 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3254 void LyXText::CursorDown() const
3256 if (cursor.par->table &&
3257 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3260 SetCursorFromCoordinates(cursor.x_fix,
3261 cursor.y - cursor.row->baseline
3262 + cursor.row->height + 1);
3263 if (cursor.par->table) {
3264 int cell = NumberOfCell(cursor.par, cursor.pos);
3265 int cell_above = cursor.par->table->GetCellAbove(cell);
3266 while(cursor.par->table &&
3267 cursor.par->table->IsContRow(cell) &&
3268 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3269 SetCursorFromCoordinates(cursor.x_fix,
3270 cursor.y - cursor.row->baseline
3271 + cursor.row->height + 1);
3272 if (cursor.par->table) {
3273 cell = NumberOfCell(cursor.par, cursor.pos);
3274 cell_above = cursor.par->table->GetCellAbove(cell);
3281 void LyXText::CursorUpParagraph() const
3283 if (cursor.pos > 0) {
3284 SetCursor(cursor.par, 0);
3286 else if (cursor.par->Previous()) {
3287 SetCursor(cursor.par->Previous(), 0);
3292 void LyXText::CursorDownParagraph() const
3294 if (cursor.par->Next()) {
3295 SetCursor(cursor.par->Next(), 0);
3297 SetCursor(cursor.par, cursor.par->Last());
3303 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3305 // Would be wrong to delete anything if we have a selection.
3306 if (selection) return;
3308 // We allow all kinds of "mumbo-jumbo" when freespacing.
3309 if (textclasslist.Style(parameters->textclass,
3310 old_cursor.par->GetLayout()).free_spacing)
3313 bool deleted = false;
3315 #ifdef FIX_DOUBLE_SPACE
3316 /* Ok I'll put some comments here about what is missing.
3317 I have fixed BackSpace (and thus Delete) to not delete
3318 double-spaces automagically. I have also changed Cut,
3319 Copy and Paste to hopefully do some sensible things.
3320 There are still some small problems that can lead to
3321 double spaces stored in the document file or space at
3322 the beginning of paragraphs. This happens if you have
3323 the cursor betwenn to spaces and then save. Or if you
3324 cut and paste and the selection have a space at the
3325 beginning and then save right after the paste. I am
3326 sure none of these are very hard to fix, but I will
3327 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3328 that I can get some feedback. (Lgb)
3331 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3332 // delete the LineSeparator.
3335 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3336 // delete the LineSeparator.
3339 // If the pos around the old_cursor were spaces, delete one of them.
3340 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3342 if (old_cursor.pos > 0
3343 && old_cursor.pos < old_cursor.par->Last()
3344 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3345 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3346 old_cursor.par->Erase(old_cursor.pos - 1);
3347 status = LyXText::NEED_MORE_REFRESH;
3349 if (old_cursor.par == cursor.par &&
3350 cursor.pos > old_cursor.pos) {
3351 SetCursorIntern(cursor.par, cursor.pos - 1);
3358 // Do not delete empty paragraphs with keepempty set.
3359 if ((textclasslist.Style(parameters->textclass,
3360 old_cursor.par->GetLayout())).keepempty)
3363 LyXCursor tmpcursor;
3365 if (old_cursor.par != cursor.par) {
3366 if ( (old_cursor.par->Last() == 0
3367 || (old_cursor.par->Last() == 1
3368 && old_cursor.par->IsLineSeparator(0)))
3369 && old_cursor.par->FirstPhysicalPar()
3370 == old_cursor.par->LastPhysicalPar()) {
3371 // ok, we will delete anything
3373 // make sure that you do not delete any environments
3374 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3375 !(old_cursor.row->previous
3376 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3377 && !(old_cursor.row->next
3378 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3379 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3380 && ((old_cursor.row->previous
3381 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3382 || (old_cursor.row->next
3383 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3385 status = LyXText::NEED_MORE_REFRESH;
3388 if (old_cursor.row->previous) {
3389 refresh_row = old_cursor.row->previous;
3390 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3392 cursor = old_cursor; // that undo can restore the right cursor position
3393 LyXParagraph * endpar = old_cursor.par->next;
3394 if (endpar && endpar->GetDepth()) {
3395 while (endpar && endpar->GetDepth()) {
3396 endpar = endpar->LastPhysicalPar()->Next();
3399 SetUndo(Undo::DELETE,
3400 old_cursor.par->previous,
3405 RemoveRow(old_cursor.row);
3406 if (params->paragraph == old_cursor.par) {
3407 params->paragraph = params->paragraph->next;
3410 delete old_cursor.par;
3412 /* Breakagain the next par. Needed
3413 * because of the parindent that
3414 * can occur or dissappear. The
3415 * next row can change its height,
3416 * if there is another layout before */
3417 if (refresh_row->next) {
3418 BreakAgain(refresh_row->next);
3419 UpdateCounters(refresh_row);
3421 SetHeightOfRow(refresh_row);
3423 refresh_row = old_cursor.row->next;
3424 refresh_y = old_cursor.y - old_cursor.row->baseline;
3427 cursor = old_cursor; // that undo can restore the right cursor position
3428 LyXParagraph *endpar = old_cursor.par->next;
3429 if (endpar && endpar->GetDepth()) {
3430 while (endpar && endpar->GetDepth()) {
3431 endpar = endpar->LastPhysicalPar()->Next();
3434 SetUndo(Undo::DELETE,
3435 old_cursor.par->previous,
3440 RemoveRow(old_cursor.row);
3442 if (params->paragraph == old_cursor.par) {
3443 params->paragraph = params->paragraph->next;
3445 delete old_cursor.par;
3447 /* Breakagain the next par. Needed
3448 because of the parindent that can
3449 occur or dissappear.
3450 The next row can change its height,
3451 if there is another layout before
3454 BreakAgain(refresh_row);
3455 UpdateCounters(refresh_row->previous);
3461 SetCursorIntern(cursor.par, cursor.pos);
3463 SetCursor(cursor.par, cursor.pos);
3465 /* if (cursor.y > old_cursor.y)
3466 cursor.y -= old_cursor.row->height; */
3468 if (sel_cursor.par == old_cursor.par
3469 && sel_cursor.pos == sel_cursor.pos) {
3470 // correct selection
3471 sel_cursor = cursor;
3476 if (old_cursor.par->ClearParagraph()) {
3477 RedoParagraphs(old_cursor, old_cursor.par->Next());
3480 SetCursorIntern(cursor.par, cursor.pos);
3482 SetCursor(cursor.par, cursor.pos);
3484 sel_cursor = cursor;
3492 LyXParagraph * LyXText::GetParFromID(int id)
3494 LyXParagraph * result = FirstParagraph();
3495 while (result && result->id() != id)
3496 result = result->next;
3502 bool LyXText::TextUndo()
3504 // returns false if no undo possible
3505 Undo * undo = params->undostack.pop();
3510 .push(CreateUndo(undo->kind,
3511 GetParFromID(undo->number_of_before_par),
3512 GetParFromID(undo->number_of_behind_par)));
3514 return TextHandleUndo(undo);
3518 bool LyXText::TextRedo()
3520 // returns false if no redo possible
3521 Undo * undo = params->redostack.pop();
3526 .push(CreateUndo(undo->kind,
3527 GetParFromID(undo->number_of_before_par),
3528 GetParFromID(undo->number_of_behind_par)));
3530 return TextHandleUndo(undo);
3534 bool LyXText::TextHandleUndo(Undo * undo)
3536 // returns false if no undo possible
3537 bool result = false;
3539 LyXParagraph * before =
3540 GetParFromID(undo->number_of_before_par);
3541 LyXParagraph * behind =
3542 GetParFromID(undo->number_of_behind_par);
3543 LyXParagraph * tmppar;
3544 LyXParagraph * tmppar2;
3545 LyXParagraph * endpar;
3546 LyXParagraph * tmppar5;
3548 // if there's no before take the beginning
3549 // of the document for redoing
3551 SetCursorIntern(FirstParagraph(), 0);
3553 // replace the paragraphs with the undo informations
3555 LyXParagraph * tmppar3 = undo->par;
3556 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3557 LyXParagraph * tmppar4 = tmppar3;
3559 while (tmppar4->next)
3560 tmppar4 = tmppar4->next;
3561 } // get last undo par
3563 // now remove the old text if there is any
3564 if (before != behind || (!behind && !before)){
3566 tmppar5 = before->next;
3568 tmppar5 = params->paragraph;
3570 while (tmppar5 && tmppar5 != behind){
3572 tmppar5 = tmppar5->next;
3573 // a memory optimization for edit: Only layout information
3574 // is stored in the undo. So restore the text informations.
3575 if (undo->kind == Undo::EDIT) {
3576 tmppar2->setContentsFromPar(tmppar);
3577 tmppar->clearContents();
3578 //tmppar2->text = tmppar->text;
3579 //tmppar->text.clear();
3580 tmppar2 = tmppar2->next;
3582 if ( currentrow && currentrow->par == tmppar )
3583 currentrow = currentrow -> previous;
3584 // Commenting out this might remove the error
3585 // reported by Purify, but it might also
3586 // introduce a memory leak. We need to
3592 // put the new stuff in the list if there is one
3595 before->next = tmppar3;
3597 params->paragraph = tmppar3;
3598 tmppar3->previous = before;
3602 params->paragraph = behind;
3605 tmppar4->next = behind;
3607 behind->previous = tmppar4;
3611 // Set the cursor for redoing
3613 SetCursorIntern(before->FirstSelfrowPar(), 0);
3614 // check wether before points to a closed float and open it if necessary
3615 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3616 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3618 while (tmppar4->previous &&
3619 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3620 tmppar4 = tmppar4->previous;
3621 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3622 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3623 tmppar4 = tmppar4->next;
3628 // open a cosed footnote at the end if necessary
3629 if (behind && behind->previous &&
3630 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3631 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3632 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3633 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3634 behind = behind->next;
3638 // calculate the endpar for redoing the paragraphs.
3640 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3641 endpar = behind->LastPhysicalPar()->Next();
3643 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3648 tmppar = GetParFromID(undo->number_of_cursor_par);
3649 RedoParagraphs(cursor, endpar);
3651 SetCursorIntern(tmppar, undo->cursor_pos);
3652 UpdateCounters(cursor.row);
3662 void LyXText::FinishUndo()
3664 // makes sure the next operation will be stored
3665 undo_finished = True;
3669 void LyXText::FreezeUndo()
3671 // this is dangerous and for internal use only
3676 void LyXText::UnFreezeUndo()
3678 // this is dangerous and for internal use only
3679 undo_frozen = false;
3683 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3684 LyXParagraph const * behind) const
3687 params->undostack.push(CreateUndo(kind, before, behind));
3688 params->redostack.clear();
3692 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3693 LyXParagraph const * behind)
3695 params->redostack.push(CreateUndo(kind, before, behind));
3699 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3700 LyXParagraph const * behind) const
3702 int before_number = -1;
3703 int behind_number = -1;
3705 before_number = before->id();
3707 behind_number = behind->id();
3708 // Undo::EDIT and Undo::FINISH are
3709 // always finished. (no overlapping there)
3710 // overlapping only with insert and delete inside one paragraph:
3711 // Nobody wants all removed character
3712 // appear one by one when undoing.
3713 // EDIT is special since only layout information, not the
3714 // contents of a paragaph are stored.
3715 if (!undo_finished && kind != Undo::EDIT &&
3716 kind != Undo::FINISH){
3717 // check wether storing is needed
3718 if (!params->undostack.empty() &&
3719 params->undostack.top()->kind == kind &&
3720 params->undostack.top()->number_of_before_par == before_number &&
3721 params->undostack.top()->number_of_behind_par == behind_number ){
3726 // create a new Undo
3727 LyXParagraph * undopar;
3728 LyXParagraph * tmppar;
3729 LyXParagraph * tmppar2;
3731 LyXParagraph * start = 0;
3732 LyXParagraph * end = 0;
3735 start = before->next;
3737 start = FirstParagraph();
3739 end = behind->previous;
3741 end = FirstParagraph();
3747 && start != end->next
3748 && (before != behind || (!before && !behind))) {
3750 tmppar2 = tmppar->Clone();
3751 tmppar2->id(tmppar->id());
3753 // a memory optimization: Just store the layout information
3755 if (kind == Undo::EDIT){
3756 //tmppar2->text.clear();
3757 tmppar2->clearContents();
3762 while (tmppar != end && tmppar->next) {
3763 tmppar = tmppar->next;
3764 tmppar2->next = tmppar->Clone();
3765 tmppar2->next->id(tmppar->id());
3766 // a memory optimization: Just store the layout
3767 // information when only edit
3768 if (kind == Undo::EDIT){
3769 //tmppar2->next->text.clear();
3770 tmppar2->clearContents();
3772 tmppar2->next->previous = tmppar2;
3773 tmppar2 = tmppar2->next;
3777 undopar = 0; // nothing to replace (undo of delete maybe)
3779 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3780 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3782 Undo * undo = new Undo(kind,
3783 before_number, behind_number,
3784 cursor_par, cursor_pos,
3787 undo_finished = false;
3792 void LyXText::SetCursorParUndo()
3794 SetUndo(Undo::FINISH,
3795 cursor.par->ParFromPos(cursor.pos)->previous,
3796 cursor.par->ParFromPos(cursor.pos)->next);
3800 void LyXText::RemoveTableRow(LyXCursor * cur) const
3806 // move to the previous row
3807 int cell_act = NumberOfCell(cur->par, cur->pos);
3810 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3813 !cur->par->table->IsFirstCell(cell_act)) {
3815 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3820 // now we have to pay attention if the actual table is the
3821 // main row of TableContRows and if yes to delete all of them
3826 // delete up to the next row
3827 while (cur->pos < cur->par->Last() &&
3829 || !cur->par->table->IsFirstCell(cell_act))) {
3830 while (cur->pos < cur->par->Last() &&
3831 !cur->par->IsNewline(cur->pos))
3832 cur->par->Erase(cur->pos);
3835 if (cur->pos < cur->par->Last())
3836 cur->par->Erase(cur->pos);
3838 if (cur->pos && cur->pos == cur->par->Last()) {
3840 cur->par->Erase(cur->pos); // no newline at very end!
3842 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3843 !cur->par->table->IsContRow(cell_org) &&
3844 cur->par->table->IsContRow(cell));
3845 cur->par->table->DeleteRow(cell_org);
3850 bool LyXText::IsEmptyTableCell() const
3852 LyXParagraph::size_type pos = cursor.pos - 1;
3853 while (pos >= 0 && pos < cursor.par->Last()
3854 && !cursor.par->IsNewline(pos))
3856 return cursor.par->IsNewline(pos + 1);
3860 void LyXText::toggleAppendix(){
3861 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3862 bool start = !par->start_of_appendix;
3864 // ensure that we have only one start_of_appendix in this document
3865 LyXParagraph * tmp = FirstParagraph();
3866 for (; tmp; tmp = tmp->next)
3867 tmp->start_of_appendix = 0;
3868 par->start_of_appendix = start;
3870 // we can set the refreshing parameters now
3871 status = LyXText::NEED_MORE_REFRESH;
3873 refresh_row = 0; // not needed for full update
3875 SetCursor(cursor.par, cursor.pos);