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"
40 #define FIX_DOUBLE_SPACE 1
44 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
52 parameters = &p->params;
56 status = LyXText::UNCHANGED;
57 LyXParagraph * par = p->paragraph;
58 current_font = GetFont(par, 0);
63 InsertParagraph(par, lastrow);
67 // set cursor at the very top position
68 selection = true; /* these setting is necessary
69 because of the delete-empty-
70 paragraph mechanism in
72 SetCursor(firstrow->par, 0);
77 // no rebreak necessary
83 // Default layouttype for copy environment type
90 // Delete all rows, this does not touch the paragraphs!
91 Row * tmprow = firstrow;
93 tmprow = firstrow->next;
100 void LyXText::owner(BufferView * bv)
102 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
106 // Gets the fully instantiated font at a given position in a paragraph
107 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
108 // The difference is that this one is used for displaying, and thus we
109 // are allowed to make cosmetic improvements. For instance make footnotes
111 // If position is -1, we get the layout font of the paragraph.
112 // If position is -2, we get the font of the manual label of the paragraph.
113 LyXFont LyXText::GetFont(LyXParagraph * par,
114 LyXParagraph::size_type pos) const
116 LyXLayout const & layout =
117 textclasslist.Style(parameters->textclass, par->GetLayout());
119 char par_depth = par->GetDepth();
120 // We specialize the 95% common case:
121 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
124 if (layout.labeltype == LABEL_MANUAL
125 && pos < BeginningOfMainBody(par)) {
127 return par->GetFontSettings(pos).
128 realize(layout.reslabelfont);
130 return par->GetFontSettings(pos).
131 realize(layout.resfont);
134 // process layoutfont for pos == -1 and labelfont for pos < -1
136 return layout.resfont;
138 return layout.reslabelfont;
142 // The uncommon case need not be optimized as much
144 LyXFont layoutfont, tmpfont;
148 if (pos < BeginningOfMainBody(par)) {
150 layoutfont = layout.labelfont;
153 layoutfont = layout.font;
155 tmpfont = par->GetFontSettings(pos);
156 tmpfont.realize(layoutfont);
159 // process layoutfont for pos == -1 and labelfont for pos < -1
161 tmpfont = layout.font;
163 tmpfont = layout.labelfont;
166 // Resolve against environment font information
167 while (par && par_depth && !tmpfont.resolved()) {
168 par = par->DepthHook(par_depth - 1);
170 tmpfont.realize(textclasslist.
171 Style(parameters->textclass,
172 par->GetLayout()).font);
173 par_depth = par->GetDepth();
177 tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
179 // Cosmetic improvement: If this is an open footnote, make the font
181 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
182 && par->footnotekind == LyXParagraph::FOOTNOTE) {
190 void LyXText::SetCharFont(LyXParagraph * par,
191 LyXParagraph::size_type pos,
195 // Let the insets convert their font
196 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
197 if (par->GetInset(pos))
198 font = par->GetInset(pos)->ConvertFont(font);
201 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
204 // Get concrete layout font to reduce against
207 if (pos < BeginningOfMainBody(par))
208 layoutfont = layout.labelfont;
210 layoutfont = layout.font;
212 // Realize against environment font information
213 if (par->GetDepth()){
214 LyXParagraph * tp = par;
215 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
216 tp = tp->DepthHook(tp->GetDepth()-1);
218 layoutfont.realize(textclasslist.
219 Style(parameters->textclass,
220 tp->GetLayout()).font);
224 layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
226 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
227 && par->footnotekind == LyXParagraph::FOOTNOTE) {
228 layoutfont.decSize();
231 // Now, reduce font against full layout font
232 font.reduce(layoutfont);
234 par->SetFont(pos, font);
238 /* inserts a new row behind the specified row, increments
239 * the touched counters */
240 void LyXText::InsertRow(Row * row, LyXParagraph * par,
241 LyXParagraph::size_type pos) const
243 Row * tmprow = new Row;
245 tmprow->previous = 0;
246 tmprow->next = firstrow;
249 tmprow->previous = row;
250 tmprow->next = row->next;
255 tmprow->next->previous = tmprow;
257 if (tmprow->previous)
258 tmprow->previous->next = tmprow;
266 ++number_of_rows; // one more row
270 // removes the row and reset the touched counters
271 void LyXText::RemoveRow(Row * row) const
273 /* this must not happen before the currentrow for clear reasons.
274 so the trick is just to set the current row onto the previous
277 GetRow(row->par, row->pos, unused_y);
278 currentrow = currentrow->previous;
280 currentrow_y -= currentrow->height;
285 row->next->previous = row->previous;
286 if (!row->previous) {
287 firstrow = row->next;
289 row->previous->next = row->next;
292 lastrow = row->previous;
294 height -= row->height; // the text becomes smaller
297 --number_of_rows; // one row less
301 // remove all following rows of the paragraph of the specified row.
302 void LyXText::RemoveParagraph(Row * row) const
304 LyXParagraph * tmppar = row->par;
308 while (row && row->par == tmppar) {
316 // insert the specified paragraph behind the specified row
317 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
319 InsertRow(row, par, 0); /* insert a new row, starting
322 SetCounter(par); // set the counters
324 // and now append the whole paragraph behind the new row
326 firstrow->height = 0;
327 AppendParagraph(firstrow);
329 row->next->height = 0;
330 AppendParagraph(row->next);
335 void LyXText::ToggleFootnote()
337 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
339 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
341 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
343 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
349 void LyXText::OpenStuff()
351 if (cursor.pos == 0 && cursor.par->bibkey){
352 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
354 else if (cursor.pos < cursor.par->Last()
355 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
356 && cursor.par->GetInset(cursor.pos)->Editable()) {
357 owner_->owner()->getMiniBuffer()
358 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
359 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
361 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
368 void LyXText::CloseFootnote()
370 LyXParagraph * tmppar;
371 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
373 // if the cursor is not in an open footnote, or
374 // there is no open footnote in this paragraph, just return.
375 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
378 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
379 owner_->owner()->getMiniBuffer()
380 ->Set(_("Nothing to do"));
384 // ok, move the cursor right before the footnote
385 // just a little faster than using CursorRight()
387 cursor.par->ParFromPos(cursor.pos) != par;
391 // now the cursor is at the beginning of the physical par
392 SetCursor(cursor.par,
394 cursor.par->ParFromPos(cursor.pos)->size());
396 /* we are in a footnote, so let us move at the beginning */
397 /* this is just faster than using just CursorLeft() */
400 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
401 // just a little bit faster than movin the cursor
402 tmppar = tmppar->Previous();
404 SetCursor(tmppar, tmppar->Last());
407 // the cursor must be exactly before the footnote
408 par = cursor.par->ParFromPos(cursor.pos);
410 status = LyXText::NEED_MORE_REFRESH;
411 refresh_row = cursor.row;
412 refresh_y = cursor.y - cursor.row->baseline;
415 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
416 Row * row = cursor.row;
418 tmppar->CloseFootnote(cursor.pos);
420 while (tmppar != endpar) {
421 RemoveRow(row->next);
423 tmppar = row->next->par;
428 AppendParagraph(cursor.row);
430 SetCursor(cursor.par, cursor.pos);
434 if (cursor.row->next)
435 SetHeightOfRow(cursor.row->next);
439 /* used in setlayout */
440 // Asger is not sure we want to do this...
441 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
444 LyXLayout const & layout =
445 textclasslist.Style(parameters->textclass, par->GetLayout());
447 LyXFont layoutfont, tmpfont;
448 for (LyXParagraph::size_type pos = 0;
449 pos < par->Last(); ++pos) {
450 if (pos < BeginningOfMainBody(par))
451 layoutfont = layout.labelfont;
453 layoutfont = layout.font;
455 tmpfont = par->GetFontSettings(pos);
456 tmpfont.reduce(layoutfont);
457 par->SetFont(pos, tmpfont);
462 // set layout over selection and make a total rebreak of those paragraphs
463 void LyXText::SetLayout(LyXTextClass::size_type layout)
467 // if there is no selection just set the layout
468 // of the current paragraph */
470 sel_start_cursor = cursor; // dummy selection
471 sel_end_cursor = cursor;
474 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
475 LyXParagraph * undoendpar = endpar;
477 if (endpar && endpar->GetDepth()) {
478 while (endpar && endpar->GetDepth()) {
479 endpar = endpar->LastPhysicalPar()->Next();
484 endpar = endpar->Next(); // because of parindents etc.
488 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
491 tmpcursor = cursor; /* store the current cursor */
493 /* ok we have a selection. This is always between sel_start_cursor
494 * and sel_end cursor */
495 cursor = sel_start_cursor;
497 LyXLayout const & lyxlayout =
498 textclasslist.Style(parameters->textclass, layout);
500 while (cursor.par != sel_end_cursor.par) {
501 if (cursor.par->footnoteflag ==
502 sel_start_cursor.par->footnoteflag) {
503 cursor.par->SetLayout(layout);
504 MakeFontEntriesLayoutSpecific(cursor.par);
505 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
506 fppar->added_space_top = lyxlayout.fill_top ?
507 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
508 fppar->added_space_bottom = lyxlayout.fill_bottom ?
509 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
510 if (lyxlayout.margintype == MARGIN_MANUAL)
511 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
512 if (lyxlayout.labeltype != LABEL_BIBLIO
514 delete fppar->bibkey;
518 cursor.par = cursor.par->Next();
520 if (cursor.par->footnoteflag ==
521 sel_start_cursor.par->footnoteflag) {
522 cursor.par->SetLayout(layout);
523 MakeFontEntriesLayoutSpecific(cursor.par);
524 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
525 fppar->added_space_top = lyxlayout.fill_top ?
526 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
527 fppar->added_space_bottom = lyxlayout.fill_bottom ?
528 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
529 if (lyxlayout.margintype == MARGIN_MANUAL)
530 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
531 if (lyxlayout.labeltype != LABEL_BIBLIO
533 delete fppar->bibkey;
538 RedoParagraphs(sel_start_cursor, endpar);
540 // we have to reset the selection, because the
541 // geometry could have changed */
542 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
544 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
545 UpdateCounters(cursor.row);
548 SetCursor(tmpcursor.par, tmpcursor.pos);
552 // increment depth over selection and
553 // make a total rebreak of those paragraphs
554 void LyXText::IncDepth()
556 // If there is no selection, just use the current paragraph
558 sel_start_cursor = cursor; // dummy selection
559 sel_end_cursor = cursor;
562 // We end at the next paragraph with depth 0
563 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
564 LyXParagraph * undoendpar = endpar;
566 if (endpar && endpar->GetDepth()) {
567 while (endpar && endpar->GetDepth()) {
568 endpar = endpar->LastPhysicalPar()->Next();
573 endpar = endpar->Next(); // because of parindents etc.
578 .par->ParFromPos(sel_start_cursor.pos)->previous,
581 LyXCursor tmpcursor = cursor; // store the current cursor
583 // ok we have a selection. This is always between sel_start_cursor
584 // and sel_end cursor
585 cursor = sel_start_cursor;
587 bool anything_changed = false;
590 // NOTE: you can't change the depth of a bibliography entry
591 if (cursor.par->footnoteflag ==
592 sel_start_cursor.par->footnoteflag
593 && textclasslist.Style(parameters->textclass,
594 cursor.par->GetLayout()
595 ).labeltype != LABEL_BIBLIO) {
596 LyXParagraph * prev =
597 cursor.par->FirstPhysicalPar()->Previous();
599 && (prev->GetDepth() - cursor.par->GetDepth() > 0
600 || (prev->GetDepth() == cursor.par->GetDepth()
601 && textclasslist.Style(parameters->textclass,
602 prev->GetLayout()).isEnvironment()))) {
603 cursor.par->FirstPhysicalPar()->depth++;
604 anything_changed = true;
607 if (cursor.par == sel_end_cursor.par)
609 cursor.par = cursor.par->Next();
612 // if nothing changed set all depth to 0
613 if (!anything_changed) {
614 cursor = sel_start_cursor;
615 while (cursor.par != sel_end_cursor.par) {
616 cursor.par->FirstPhysicalPar()->depth = 0;
617 cursor.par = cursor.par->Next();
619 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
620 cursor.par->FirstPhysicalPar()->depth = 0;
623 RedoParagraphs(sel_start_cursor, endpar);
625 // we have to reset the selection, because the
626 // geometry could have changed
627 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
629 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
630 UpdateCounters(cursor.row);
633 SetCursor(tmpcursor.par, tmpcursor.pos);
637 // decrement depth over selection and
638 // make a total rebreak of those paragraphs
639 void LyXText::DecDepth()
641 // if there is no selection just set the layout
642 // of the current paragraph
644 sel_start_cursor = cursor; // dummy selection
645 sel_end_cursor = cursor;
648 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
649 LyXParagraph * undoendpar = endpar;
651 if (endpar && endpar->GetDepth()) {
652 while (endpar && endpar->GetDepth()) {
653 endpar = endpar->LastPhysicalPar()->Next();
658 endpar = endpar->Next(); // because of parindents etc.
663 .par->ParFromPos(sel_start_cursor.pos)->previous,
666 LyXCursor tmpcursor = cursor; // store the current cursor
668 // ok we have a selection. This is always between sel_start_cursor
669 // and sel_end cursor
670 cursor = sel_start_cursor;
673 if (cursor.par->footnoteflag ==
674 sel_start_cursor.par->footnoteflag) {
675 if (cursor.par->FirstPhysicalPar()->depth)
676 cursor.par->FirstPhysicalPar()->depth--;
678 if (cursor.par == sel_end_cursor.par)
680 cursor.par = cursor.par->Next();
683 RedoParagraphs(sel_start_cursor, endpar);
685 // we have to reset the selection, because the
686 // geometry could have changed
687 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
689 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
690 UpdateCounters(cursor.row);
693 SetCursor(tmpcursor.par, tmpcursor.pos);
697 // set font over selection and make a total rebreak of those paragraphs
698 void LyXText::SetFont(LyXFont const & font, bool toggleall)
700 // if there is no selection just set the current_font
702 // Determine basis font
704 if (cursor.pos < BeginningOfMainBody(cursor.par))
705 layoutfont = GetFont(cursor.par, -2);
707 layoutfont = GetFont(cursor.par, -1);
708 // Update current font
709 real_current_font.update(font, parameters->language_info, toggleall);
711 // Reduce to implicit settings
712 current_font = real_current_font;
713 current_font.reduce(layoutfont);
714 // And resolve it completely
715 real_current_font.realize(layoutfont);
719 LyXCursor tmpcursor = cursor; // store the current cursor
721 // ok we have a selection. This is always between sel_start_cursor
722 // and sel_end cursor
725 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
726 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
727 cursor = sel_start_cursor;
728 while (cursor.par != sel_end_cursor.par ||
729 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
730 && cursor.pos < sel_end_cursor.pos))
732 if (cursor.pos < cursor.par->Last()
733 && cursor.par->footnoteflag
734 == sel_start_cursor.par->footnoteflag) {
735 // an open footnote should behave
737 LyXFont newfont = GetFont(cursor.par, cursor.pos);
738 newfont.update(font, parameters->language_info, toggleall);
739 SetCharFont(cursor.par, cursor.pos, newfont);
743 cursor.par = cursor.par->Next();
747 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
749 // we have to reset the selection, because the
750 // geometry could have changed
751 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
753 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
756 SetCursor(tmpcursor.par, tmpcursor.pos);
760 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
762 Row * tmprow = cur.row;
763 long y = cur.y - tmprow->baseline;
765 SetHeightOfRow(tmprow);
766 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
767 // find the first row of the paragraph
768 if (first_phys_par != tmprow->par)
769 while (tmprow->previous
770 && tmprow->previous->par != first_phys_par) {
771 tmprow = tmprow->previous;
773 SetHeightOfRow(tmprow);
775 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
776 tmprow = tmprow->previous;
778 SetHeightOfRow(tmprow);
781 // we can set the refreshing parameters now
782 status = LyXText::NEED_MORE_REFRESH;
784 refresh_row = tmprow;
785 SetCursor(cur.par, cur.pos);
789 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
791 Row * tmprow = cur.row;
793 long y = cur.y - tmprow->baseline;
794 SetHeightOfRow(tmprow);
795 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
796 // find the first row of the paragraph
797 if (first_phys_par != tmprow->par)
798 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
799 tmprow = tmprow->previous;
802 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
803 tmprow = tmprow->previous;
807 // we can set the refreshing parameters now
808 if (status == LyXText::UNCHANGED || y < refresh_y) {
810 refresh_row = tmprow;
812 status = LyXText::NEED_MORE_REFRESH;
813 SetCursor(cur.par, cur.pos);
817 /* deletes and inserts again all paragaphs between the cursor
818 * and the specified par
819 * This function is needed after SetLayout and SetFont etc. */
820 void LyXText::RedoParagraphs(LyXCursor const & cur,
821 LyXParagraph const * endpar) const
824 LyXParagraph * tmppar, * first_phys_par;
826 Row * tmprow = cur.row;
828 long y = cur.y - tmprow->baseline;
830 if (!tmprow->previous){
831 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
833 first_phys_par = tmprow->par->FirstPhysicalPar();
834 // find the first row of the paragraph
835 if (first_phys_par != tmprow->par)
836 while (tmprow->previous
837 && tmprow->previous->par != first_phys_par) {
838 tmprow = tmprow->previous;
841 while (tmprow->previous
842 && tmprow->previous->par == first_phys_par) {
843 tmprow = tmprow->previous;
848 // we can set the refreshing parameters now
849 status = LyXText::NEED_MORE_REFRESH;
851 refresh_row = tmprow->previous; /* the real refresh row will
852 be deleted, so I store
856 tmppar = tmprow->next->par;
859 while (tmppar != endpar) {
860 RemoveRow(tmprow->next);
862 tmppar = tmprow->next->par;
867 // remove the first one
868 tmprow2 = tmprow; /* this is because tmprow->previous
870 tmprow = tmprow->previous;
873 tmppar = first_phys_par;
877 InsertParagraph(tmppar, tmprow);
880 while (tmprow->next && tmprow->next->par == tmppar)
881 tmprow = tmprow->next;
882 tmppar = tmppar->Next();
884 } while (tmppar != endpar);
886 // this is because of layout changes
888 refresh_y -= refresh_row->height;
889 SetHeightOfRow(refresh_row);
891 refresh_row = firstrow;
893 SetHeightOfRow(refresh_row);
896 if (tmprow && tmprow->next)
897 SetHeightOfRow(tmprow->next);
901 int LyXText::FullRebreak()
903 if (need_break_row) {
904 BreakAgain(need_break_row);
912 /* important for the screen */
915 /* the cursor set functions have a special mechanism. When they
916 * realize, that you left an empty paragraph, they will delete it.
917 * They also delet the corresponding row */
919 // need the selection cursor:
920 void LyXText::SetSelection()
923 last_sel_cursor = sel_cursor;
924 sel_start_cursor = sel_cursor;
925 sel_end_cursor = sel_cursor;
930 // first the toggling area
931 if (cursor.y < last_sel_cursor.y ||
932 (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
933 toggle_end_cursor = last_sel_cursor;
934 toggle_cursor = cursor;
937 toggle_end_cursor = cursor;
938 toggle_cursor = last_sel_cursor;
941 last_sel_cursor = cursor;
943 // and now the whole selection
945 if (sel_cursor.par == cursor.par)
946 if (sel_cursor.pos < cursor.pos) {
947 sel_end_cursor = cursor;
948 sel_start_cursor = sel_cursor;
950 sel_end_cursor = sel_cursor;
951 sel_start_cursor = cursor;
953 else if (sel_cursor.y < cursor.y ||
954 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
955 sel_end_cursor = cursor;
956 sel_start_cursor = sel_cursor;
959 sel_end_cursor = sel_cursor;
960 sel_start_cursor = cursor;
963 // a selection with no contents is not a selection
964 if (sel_start_cursor.x == sel_end_cursor.x &&
965 sel_start_cursor.y == sel_end_cursor.y)
970 void LyXText::ClearSelection() const
977 void LyXText::CursorHome() const
979 SetCursor(cursor.par, cursor.row->pos);
983 void LyXText::CursorEnd() const
985 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
986 SetCursor(cursor.par, RowLast(cursor.row) + 1);
988 if (cursor.par->Last() &&
989 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
990 || cursor.par->IsNewline(RowLast(cursor.row))))
991 SetCursor(cursor.par, RowLast(cursor.row));
993 SetCursor(cursor.par, RowLast(cursor.row) + 1);
995 if (cursor.par->table) {
996 int cell = NumberOfCell(cursor.par, cursor.pos);
997 if (cursor.par->table->RowHasContRow(cell) &&
998 cursor.par->table->CellHasContRow(cell)<0) {
999 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1000 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1002 if (cursor.par->Last() &&
1003 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1004 || cursor.par->IsNewline(RowLast(cursor.row))))
1005 SetCursor(cursor.par, RowLast(cursor.row));
1007 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1014 void LyXText::CursorTop() const
1016 while (cursor.par->Previous())
1017 cursor.par = cursor.par->Previous();
1018 SetCursor(cursor.par, 0);
1022 void LyXText::CursorBottom() const
1024 while (cursor.par->Next())
1025 cursor.par = cursor.par->Next();
1026 SetCursor(cursor.par, cursor.par->Last());
1030 /* returns a pointer to the row near the specified y-coordinate
1031 * (relative to the whole text). y is set to the real beginning
1033 Row * LyXText::GetRowNearY(long & y) const
1039 tmprow = currentrow;
1040 tmpy = currentrow_y;
1047 while (tmprow->next && tmpy + tmprow->height <= y) {
1048 tmpy += tmprow->height;
1049 tmprow = tmprow->next;
1052 while (tmprow->previous && tmpy > y) {
1053 tmprow = tmprow->previous;
1054 tmpy -= tmprow->height;
1057 currentrow = tmprow;
1058 currentrow_y = tmpy;
1060 y = tmpy; // return the real y
1065 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1067 // If the mask is completely neutral, tell user
1068 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1069 // Could only happen with user style
1070 owner_->owner()->getMiniBuffer()
1071 ->Set(_("No font change defined. Use Character under"
1072 " the Layout menu to define font change."));
1076 // Try implicit word selection
1077 LyXCursor resetCursor = cursor;
1078 int implicitSelection = SelectWordWhenUnderCursor();
1081 SetFont(font, toggleall);
1083 /* Implicit selections are cleared afterwards and cursor is set to the
1084 original position. */
1085 if (implicitSelection) {
1087 cursor = resetCursor;
1088 SetCursor( cursor.par, cursor.pos );
1089 sel_cursor = cursor;
1094 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1096 if (textclasslist.Style(parameters->textclass,
1097 par->GetLayout()).labeltype != LABEL_MANUAL)
1100 return par->BeginningOfMainBody();
1104 /* if there is a selection, reset every environment you can find
1105 * in the selection, otherwise just the environment you are in */
1106 void LyXText::MeltFootnoteEnvironment()
1108 LyXParagraph * tmppar, * firsttmppar;
1112 /* is is only allowed, if the cursor is IN an open footnote.
1113 * Otherwise it is too dangerous */
1114 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1117 SetUndo(Undo::FINISH,
1118 cursor.par->PreviousBeforeFootnote()->previous,
1119 cursor.par->NextAfterFootnote()->next);
1121 /* ok, move to the beginning of the footnote. */
1122 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1123 cursor.par = cursor.par->Previous();
1125 SetCursor(cursor.par, cursor.par->Last());
1126 /* this is just faster than using CursorLeft(); */
1128 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1129 tmppar = firsttmppar;
1130 /* tmppar is now the paragraph right before the footnote */
1132 bool first_footnote_par_is_not_empty = tmppar->next->size();
1135 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1136 tmppar = tmppar->next; /* I use next instead of Next(),
1137 * because there cannot be any
1138 * footnotes in a footnote
1140 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1142 /* remember the captions and empty paragraphs */
1143 if ((textclasslist.Style(parameters->textclass,
1144 tmppar->GetLayout())
1145 .labeltype == LABEL_SENSITIVE)
1147 tmppar->SetLayout(0);
1150 // now we will paste the ex-footnote, if the layouts allow it
1151 // first restore the layout of the paragraph right behind
1154 tmppar->next->MakeSameLayout(cursor.par);
1157 if ((!tmppar->GetLayout() && !tmppar->table)
1159 && (!tmppar->Next()->Last()
1160 || tmppar->Next()->HasSameLayout(tmppar)))) {
1161 if (tmppar->Next()->Last()
1162 && tmppar->Next()->IsLineSeparator(0))
1163 tmppar->Next()->Erase(0);
1164 tmppar->PasteParagraph();
1167 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1168 * by the pasting of the beginning */
1170 /* then the beginning */
1171 /* if there is no space between the text and the footnote, so we insert
1173 * (only if the previous par and the footnotepar are not empty!) */
1174 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1175 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1176 if (firsttmppar->size()
1177 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1178 && first_footnote_par_is_not_empty) {
1179 firsttmppar->next->InsertChar(0, ' ');
1181 firsttmppar->PasteParagraph();
1184 /* now redo the paragaphs */
1185 RedoParagraphs(cursor, tmppar);
1187 SetCursor(cursor.par, cursor.pos);
1189 /* sometimes it can happen, that there is a counter change */
1190 Row * row = cursor.row;
1191 while (row->next && row->par != tmppar && row->next->par != tmppar)
1193 UpdateCounters(row);
1200 /* the DTP switches for paragraphs. LyX will store them in the
1201 * first physicla paragraph. When a paragraph is broken, the top settings
1202 * rest, the bottom settings are given to the new one. So I can make shure,
1203 * they do not duplicate themself and you cannnot make dirty things with
1206 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1207 bool pagebreak_top, bool pagebreak_bottom,
1208 VSpace const & space_top,
1209 VSpace const & space_bottom,
1211 string labelwidthstring,
1214 LyXCursor tmpcursor = cursor;
1216 sel_start_cursor = cursor;
1217 sel_end_cursor = cursor;
1220 // make sure that the depth behind the selection are restored, too
1221 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1222 LyXParagraph * undoendpar = endpar;
1224 if (endpar && endpar->GetDepth()) {
1225 while (endpar && endpar->GetDepth()) {
1226 endpar = endpar->LastPhysicalPar()->Next();
1227 undoendpar = endpar;
1231 endpar = endpar->Next(); // because of parindents etc.
1236 .par->ParFromPos(sel_start_cursor.pos)->previous,
1240 LyXParagraph * tmppar = sel_end_cursor.par;
1241 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1242 SetCursor(tmppar->FirstPhysicalPar(), 0);
1243 status = LyXText::NEED_MORE_REFRESH;
1244 refresh_row = cursor.row;
1245 refresh_y = cursor.y - cursor.row->baseline;
1246 if (cursor.par->footnoteflag ==
1247 sel_start_cursor.par->footnoteflag) {
1248 cursor.par->line_top = line_top;
1249 cursor.par->line_bottom = line_bottom;
1250 cursor.par->pagebreak_top = pagebreak_top;
1251 cursor.par->pagebreak_bottom = pagebreak_bottom;
1252 cursor.par->added_space_top = space_top;
1253 cursor.par->added_space_bottom = space_bottom;
1254 // does the layout allow the new alignment?
1255 if (align == LYX_ALIGN_LAYOUT)
1256 align = textclasslist
1257 .Style(parameters->textclass,
1258 cursor.par->GetLayout()).align;
1259 if (align & textclasslist
1260 .Style(parameters->textclass,
1261 cursor.par->GetLayout()).alignpossible) {
1262 if (align == textclasslist
1263 .Style(parameters->textclass,
1264 cursor.par->GetLayout()).align)
1265 cursor.par->align = LYX_ALIGN_LAYOUT;
1267 cursor.par->align = align;
1269 cursor.par->SetLabelWidthString(labelwidthstring);
1270 cursor.par->noindent = noindent;
1273 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1276 RedoParagraphs(sel_start_cursor, endpar);
1279 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1280 sel_cursor = cursor;
1281 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1283 SetCursor(tmpcursor.par, tmpcursor.pos);
1287 void LyXText::SetParagraphExtraOpt(int type,
1289 char const * widthp,
1290 int alignment, bool hfill,
1291 bool start_minipage)
1293 LyXCursor tmpcursor = cursor;
1294 LyXParagraph * tmppar;
1296 sel_start_cursor = cursor;
1297 sel_end_cursor = cursor;
1300 // make sure that the depth behind the selection are restored, too
1301 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1302 LyXParagraph * undoendpar = endpar;
1304 if (endpar && endpar->GetDepth()) {
1305 while (endpar && endpar->GetDepth()) {
1306 endpar = endpar->LastPhysicalPar()->Next();
1307 undoendpar = endpar;
1311 endpar = endpar->Next(); // because of parindents etc.
1316 .par->ParFromPos(sel_start_cursor.pos)->previous,
1319 tmppar = sel_end_cursor.par;
1320 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1321 SetCursor(tmppar->FirstPhysicalPar(), 0);
1322 status = LyXText::NEED_MORE_REFRESH;
1323 refresh_row = cursor.row;
1324 refresh_y = cursor.y - cursor.row->baseline;
1325 if (cursor.par->footnoteflag ==
1326 sel_start_cursor.par->footnoteflag) {
1327 if (type == LyXParagraph::PEXTRA_NONE) {
1328 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1329 cursor.par->UnsetPExtraType();
1330 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1333 cursor.par->SetPExtraType(type, width, widthp);
1334 cursor.par->pextra_hfill = hfill;
1335 cursor.par->pextra_start_minipage = start_minipage;
1336 cursor.par->pextra_alignment = alignment;
1339 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1341 RedoParagraphs(sel_start_cursor, endpar);
1343 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1344 sel_cursor = cursor;
1345 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1347 SetCursor(tmpcursor.par, tmpcursor.pos);
1351 char loweralphaCounter(int n)
1353 if (n < 1 || n > 26)
1359 char alphaCounter(int n)
1361 if (n < 1 || n > 26)
1367 char hebrewCounter(int n)
1369 static const char hebrew[22] = {
1370 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1371 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1372 '÷', 'ø', 'ù', 'ú'
1374 if (n < 1 || n > 22)
1380 static char const * romanCounter(int n)
1382 static char const * roman[20] = {
1383 "i", "ii", "iii", "iv", "v",
1384 "vi", "vii", "viii", "ix", "x",
1385 "xi", "xii", "xiii", "xiv", "xv",
1386 "xvi", "xvii", "xviii", "xix", "xx"
1388 if (n < 1 || n > 20)
1394 // set the counter of a paragraph. This includes the labels
1395 void LyXText::SetCounter(LyXParagraph * par) const
1397 // this is only relevant for the beginning of paragraph
1398 par = par->FirstPhysicalPar();
1400 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1403 LyXTextClass const & textclass =
1404 textclasslist.TextClass(parameters->textclass);
1406 /* copy the prev-counters to this one, unless this is the start of a
1407 footnote or of a bibliography or the very first paragraph */
1409 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1410 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1411 && par->footnotekind == LyXParagraph::FOOTNOTE)
1412 && !(textclasslist.Style(parameters->textclass,
1413 par->Previous()->GetLayout()
1414 ).labeltype != LABEL_BIBLIO
1415 && layout.labeltype == LABEL_BIBLIO)) {
1416 for (int i = 0; i < 10; ++i) {
1417 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1419 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1420 if (!par->appendix && par->start_of_appendix){
1421 par->appendix = true;
1422 for (int i = 0; i < 10; ++i) {
1423 par->setCounter(i, 0);
1426 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1427 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1430 for (int i = 0; i < 10; ++i) {
1431 par->setCounter(i, 0);
1433 par->appendix = par->start_of_appendix;
1438 // if this is an open marginnote and this is the first
1439 // entry in the marginnote and the enclosing
1440 // environment is an enum/item then correct for the
1441 // LaTeX behaviour (ARRae)
1442 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1443 && par->footnotekind == LyXParagraph::MARGIN
1445 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1446 && (par->PreviousBeforeFootnote()
1447 && textclasslist.Style(parameters->textclass,
1448 par->PreviousBeforeFootnote()->GetLayout()
1449 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1450 // Any itemize or enumerate environment in a marginnote
1451 // that is embedded in an itemize or enumerate
1452 // paragraph is seen by LaTeX as being at a deeper
1453 // level within that enclosing itemization/enumeration
1454 // even if there is a "standard" layout at the start of
1460 /* Maybe we have to increment the enumeration depth.
1461 * BUT, enumeration in a footnote is considered in isolation from its
1462 * surrounding paragraph so don't increment if this is the
1463 * first line of the footnote
1464 * AND, bibliographies can't have their depth changed ie. they
1465 * are always of depth 0
1468 && par->Previous()->GetDepth() < par->GetDepth()
1469 && textclasslist.Style(parameters->textclass,
1470 par->Previous()->GetLayout()
1471 ).labeltype == LABEL_COUNTER_ENUMI
1472 && par->enumdepth < 3
1473 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1474 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1475 && par->footnotekind == LyXParagraph::FOOTNOTE)
1476 && layout.labeltype != LABEL_BIBLIO) {
1480 /* Maybe we have to decrement the enumeration depth, see note above */
1482 && par->Previous()->GetDepth() > par->GetDepth()
1483 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1484 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1485 && par->footnotekind == LyXParagraph::FOOTNOTE)
1486 && layout.labeltype != LABEL_BIBLIO) {
1487 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1488 par->setCounter(6 + par->enumdepth,
1489 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1490 /* reset the counters.
1491 * A depth change is like a breaking layout
1493 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1494 par->setCounter(i, 0);
1497 if (!par->labelstring.empty()) {
1498 par->labelstring.clear();
1501 if (layout.margintype == MARGIN_MANUAL) {
1502 if (par->labelwidthstring.empty()) {
1503 par->SetLabelWidthString(layout.labelstring());
1506 par->SetLabelWidthString(string());
1509 /* is it a layout that has an automatic label ? */
1510 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1512 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1513 if (i >= 0 && i<= parameters->secnumdepth) {
1514 par->incCounter(i); // increment the counter
1516 // Is there a label? Useful for Chapter layout
1517 if (!par->appendix){
1518 if (!layout.labelstring().empty())
1519 par->labelstring = layout.labelstring();
1521 par->labelstring.clear();
1523 if (!layout.labelstring_appendix().empty())
1524 par->labelstring = layout.labelstring_appendix();
1526 par->labelstring.clear();
1534 if (!par->appendix) {
1535 switch (2 * LABEL_FIRST_COUNTER -
1536 textclass.maxcounter() + i) {
1537 case LABEL_COUNTER_CHAPTER:
1538 s << par->getCounter(i);
1540 case LABEL_COUNTER_SECTION:
1541 s << par->getCounter(i - 1) << '.'
1542 << par->getCounter(i);
1544 case LABEL_COUNTER_SUBSECTION:
1545 s << par->getCounter(i - 2) << '.'
1546 << par->getCounter(i - 1) << '.'
1547 << par->getCounter(i);
1549 case LABEL_COUNTER_SUBSUBSECTION:
1550 s << par->getCounter(i - 3) << '.'
1551 << par->getCounter(i - 2) << '.'
1552 << par->getCounter(i - 1) << '.'
1553 << par->getCounter(i);
1556 case LABEL_COUNTER_PARAGRAPH:
1557 s << par->getCounter(i - 4) << '.'
1558 << par->getCounter(i - 3) << '.'
1559 << par->getCounter(i - 2) << '.'
1560 << par->getCounter(i - 1) << '.'
1561 << par->getCounter(i);
1563 case LABEL_COUNTER_SUBPARAGRAPH:
1564 s << par->getCounter(i - 5) << '.'
1565 << par->getCounter(i - 4) << '.'
1566 << par->getCounter(i - 3) << '.'
1567 << par->getCounter(i - 2) << '.'
1568 << par->getCounter(i - 1) << '.'
1569 << par->getCounter(i);
1573 s << par->getCounter(i) << '.';
1576 } else { // appendix
1577 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1578 case LABEL_COUNTER_CHAPTER:
1579 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1580 s << alphaCounter(par->getCounter(i));
1582 s << hebrewCounter(par->getCounter(i));
1584 case LABEL_COUNTER_SECTION:
1585 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1586 s << alphaCounter(par->getCounter(i - 1));
1588 s << hebrewCounter(par->getCounter(i - 1));
1591 << par->getCounter(i);
1594 case LABEL_COUNTER_SUBSECTION:
1595 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1596 s << alphaCounter(par->getCounter(i - 2));
1598 s << hebrewCounter(par->getCounter(i - 2));
1601 << par->getCounter(i-1) << '.'
1602 << par->getCounter(i);
1605 case LABEL_COUNTER_SUBSUBSECTION:
1606 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1607 s << alphaCounter(par->getCounter(i-3));
1609 s << hebrewCounter(par->getCounter(i-3));
1612 << par->getCounter(i-2) << '.'
1613 << par->getCounter(i-1) << '.'
1614 << par->getCounter(i);
1617 case LABEL_COUNTER_PARAGRAPH:
1618 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1619 s << alphaCounter(par->getCounter(i-4));
1621 s << hebrewCounter(par->getCounter(i-4));
1624 << par->getCounter(i-3) << '.'
1625 << par->getCounter(i-2) << '.'
1626 << par->getCounter(i-1) << '.'
1627 << par->getCounter(i);
1630 case LABEL_COUNTER_SUBPARAGRAPH:
1631 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1632 s << alphaCounter(par->getCounter(i-5));
1634 s << hebrewCounter(par->getCounter(i-5));
1637 << par->getCounter(i-4) << '.'
1638 << par->getCounter(i-3) << '.'
1639 << par->getCounter(i-2) << '.'
1640 << par->getCounter(i-1) << '.'
1641 << par->getCounter(i);
1645 // Can this ever be reached? And in the
1646 // case it is, how can this be correct?
1648 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1654 par->labelstring += s.str().c_str();
1655 // We really want to remove the c_str as soon as
1659 char * tmps = s.str();
1660 par->labelstring += tmps;
1664 for (i++; i < 10; ++i) {
1665 // reset the following counters
1666 par->setCounter(i, 0);
1668 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1669 for (i++; i < 10; ++i) {
1670 // reset the following counters
1671 par->setCounter(i, 0);
1673 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1674 par->incCounter(i + par->enumdepth);
1675 int number = par->getCounter(i + par->enumdepth);
1682 switch (par->enumdepth) {
1684 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1686 << loweralphaCounter(number)
1690 << hebrewCounter(number)
1694 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1695 s << romanCounter(number) << '.';
1697 s << '.' << romanCounter(number);
1700 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1701 s << alphaCounter(number)
1705 << alphaCounter(number);
1708 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1715 par->labelstring = s.str().c_str();
1716 // we really want to get rid of that c_str()
1719 char * tmps = s.str();
1720 par->labelstring = tmps;
1724 for (i += par->enumdepth + 1; i < 10; ++i)
1725 par->setCounter(i, 0); /* reset the following counters */
1728 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1729 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1731 int number = par->getCounter(i);
1733 par->bibkey = new InsetBibKey();
1734 par->bibkey->setCounter(number);
1735 par->labelstring = layout.labelstring();
1737 // In biblio should't be following counters but...
1739 string s = layout.labelstring();
1741 // the caption hack:
1743 if (layout.labeltype == LABEL_SENSITIVE) {
1744 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1745 && (par->footnotekind == LyXParagraph::FIG
1746 || par->footnotekind == LyXParagraph::WIDE_FIG))
1747 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1751 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1752 && (par->footnotekind == LyXParagraph::TAB
1753 || par->footnotekind == LyXParagraph::WIDE_TAB))
1754 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1758 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1759 && par->footnotekind == LyXParagraph::ALGORITHM)
1760 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1763 s = ":Ãúéøåâìà ";
1765 /* par->SetLayout(0);
1766 s = layout->labelstring; */
1767 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1770 s = " :úåòîùî øñç";
1774 par->labelstring = s;
1776 /* reset the enumeration counter. They are always resetted
1777 * when there is any other layout between */
1778 for (int i = 6 + par->enumdepth; i < 10; ++i)
1779 par->setCounter(i, 0);
1784 /* Updates all counters BEHIND the row. Changed paragraphs
1785 * with a dynamic left margin will be rebroken. */
1786 void LyXText::UpdateCounters(Row * row) const
1795 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1796 par = row->par->LastPhysicalPar()->Next();
1798 par = row->par->next;
1803 while (row->par != par)
1808 /* now check for the headline layouts. remember that they
1809 * have a dynamic left margin */
1811 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1812 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1815 /* Rebreak the paragraph */
1816 RemoveParagraph(row);
1817 AppendParagraph(row);
1819 /* think about the damned open footnotes! */
1820 while (par->Next() &&
1821 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1822 || par->Next()->IsDummy())){
1824 if (par->IsDummy()) {
1825 while (row->par != par)
1827 RemoveParagraph(row);
1828 AppendParagraph(row);
1833 par = par->LastPhysicalPar()->Next();
1839 /* insets an inset. */
1840 void LyXText::InsertInset(Inset *inset)
1842 SetUndo(Undo::INSERT,
1843 cursor.par->ParFromPos(cursor.pos)->previous,
1844 cursor.par->ParFromPos(cursor.pos)->next);
1845 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1846 cursor.par->InsertInset(cursor.pos, inset);
1847 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1848 * The character will not be inserted a
1853 // this is for the simple cut and paste mechanism
1854 static LyXParagraph * simple_cut_buffer = 0;
1855 static char simple_cut_buffer_textclass = 0;
1857 void DeleteSimpleCutBuffer()
1859 if (!simple_cut_buffer)
1861 LyXParagraph * tmppar;
1863 while (simple_cut_buffer) {
1864 tmppar = simple_cut_buffer;
1865 simple_cut_buffer = simple_cut_buffer->next;
1868 simple_cut_buffer = 0;
1872 void LyXText::copyEnvironmentType()
1874 copylayouttype = cursor.par->GetLayout();
1878 void LyXText::pasteEnvironmentType()
1880 SetLayout(copylayouttype);
1884 void LyXText::CutSelection(bool doclear)
1886 // This doesn't make sense, if there is no selection
1890 // OK, we have a selection. This is always between sel_start_cursor
1891 // and sel_end cursor
1892 LyXParagraph * tmppar;
1894 // Check whether there are half footnotes in the selection
1895 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1896 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1897 tmppar = sel_start_cursor.par;
1898 while (tmppar != sel_end_cursor.par){
1899 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1900 WriteAlert(_("Impossible operation"),
1901 _("Don't know what to do with half floats."),
1905 tmppar = tmppar->Next();
1909 /* table stuff -- begin */
1910 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1911 if ( sel_start_cursor.par != sel_end_cursor.par) {
1912 WriteAlert(_("Impossible operation"),
1913 _("Don't know what to do with half tables."),
1917 sel_start_cursor.par->table->Reinit();
1919 /* table stuff -- end */
1921 // make sure that the depth behind the selection are restored, too
1922 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1923 LyXParagraph * undoendpar = endpar;
1925 if (endpar && endpar->GetDepth()) {
1926 while (endpar && endpar->GetDepth()) {
1927 endpar = endpar->LastPhysicalPar()->Next();
1928 undoendpar = endpar;
1930 } else if (endpar) {
1931 endpar = endpar->Next(); // because of parindents etc.
1934 SetUndo(Undo::DELETE,
1936 .par->ParFromPos(sel_start_cursor.pos)->previous,
1939 // clear the simple_cut_buffer
1940 DeleteSimpleCutBuffer();
1942 // set the textclass
1943 simple_cut_buffer_textclass = parameters->textclass;
1945 #ifdef WITH_WARNINGS
1946 #warning Asger: Make cut more intelligent here.
1949 White paper for "intelligent" cutting:
1951 Example: "This is our text."
1952 Using " our " as selection, cutting will give "This istext.".
1953 Using "our" as selection, cutting will give "This is text.".
1954 Using " our" as selection, cutting will give "This is text.".
1955 Using "our " as selection, cutting will give "This is text.".
1957 All those four selections will (however) paste identically:
1958 Pasting with the cursor right after the "is" will give the
1959 original text with all four selections.
1961 The rationale is to be intelligent such that words are copied,
1962 cut and pasted in a functional manner.
1964 This is not implemented yet. (Asger)
1966 The changes below sees to do a lot of what you want. However
1967 I have not verified that all cases work as they should:
1969 - cut in multiple row
1971 - cut across footnotes and paragraph
1972 My simplistic tests show that the idea are basically sound but
1973 there are some items to fix up...we only need to find them
1976 As do redo Asger's example above (with | beeing the cursor in the
1977 result after cutting.):
1979 Example: "This is our text."
1980 Using " our " as selection, cutting will give "This is|text.".
1981 Using "our" as selection, cutting will give "This is | text.".
1982 Using " our" as selection, cutting will give "This is| text.".
1983 Using "our " as selection, cutting will give "This is |text.".
1988 #ifndef FIX_DOUBLE_SPACE
1989 bool space_wrapped =
1990 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1991 if (sel_end_cursor.pos > 0
1992 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1993 // please break before a space at the end
1994 sel_end_cursor.pos--;
1995 space_wrapped = true;
1997 // cut behind a space if there is one
1998 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1999 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2000 && (sel_start_cursor.par != sel_end_cursor.par
2001 || sel_start_cursor.pos < sel_end_cursor.pos))
2002 sel_start_cursor.pos++;
2004 // there are two cases: cut only within one paragraph or
2005 // more than one paragraph
2007 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2008 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2009 // only within one paragraph
2010 simple_cut_buffer = new LyXParagraph;
2011 LyXParagraph::size_type i =
2012 sel_start_cursor.pos;
2013 for (; i < sel_end_cursor.pos; ++i) {
2014 /* table stuff -- begin */
2015 if (sel_start_cursor.par->table
2016 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2017 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2018 sel_start_cursor.pos++;
2020 /* table stuff -- end */
2021 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2022 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2024 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2026 #ifndef FIX_DOUBLE_SPACE
2027 // check for double spaces
2028 if (sel_start_cursor.pos &&
2029 sel_start_cursor.par->Last() > sel_start_cursor.pos
2030 && sel_start_cursor.par
2031 ->IsLineSeparator(sel_start_cursor.pos - 1)
2032 && sel_start_cursor.par
2033 ->IsLineSeparator(sel_start_cursor.pos)) {
2034 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2037 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
2040 endpar = sel_end_cursor.par->Next();
2042 // cut more than one paragraph
2045 ->BreakParagraphConservative(sel_end_cursor.pos);
2046 #ifndef FIX_DOUBLE_SPACE
2047 // insert a space at the end if there was one
2050 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2052 sel_end_cursor.par = sel_end_cursor.par->Next();
2053 sel_end_cursor.pos = 0;
2055 cursor = sel_end_cursor;
2057 #ifndef FIX_DOUBLE_SPACE
2058 // please break behind a space, if there is one.
2059 // The space should be copied too
2060 if (sel_start_cursor.par
2061 ->IsLineSeparator(sel_start_cursor.pos))
2062 sel_start_cursor.pos++;
2064 sel_start_cursor.par
2065 ->BreakParagraphConservative(sel_start_cursor.pos);
2066 #ifndef FIX_DOUBLE_SPACE
2067 if (!sel_start_cursor.pos
2068 || sel_start_cursor.par
2069 ->IsLineSeparator(sel_start_cursor.pos - 1)
2070 || sel_start_cursor.par
2071 ->IsNewline(sel_start_cursor.pos - 1)) {
2072 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2075 // store the endparagraph for redoing later
2076 endpar = sel_end_cursor.par->Next(); /* needed because
2081 // store the selection
2082 simple_cut_buffer = sel_start_cursor.par
2083 ->ParFromPos(sel_start_cursor.pos)->next;
2084 simple_cut_buffer->previous = 0;
2085 sel_end_cursor.par->previous->next = 0;
2087 // cut the selection
2088 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2089 = sel_end_cursor.par;
2091 sel_end_cursor.par->previous
2092 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2094 // care about footnotes
2095 if (simple_cut_buffer->footnoteflag) {
2096 LyXParagraph * tmppar = simple_cut_buffer;
2098 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2099 tmppar = tmppar->next;
2103 // the cut selection should begin with standard layout
2104 simple_cut_buffer->Clear();
2106 // paste the paragraphs again, if possible
2108 sel_start_cursor.par->Next()->ClearParagraph();
2109 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2111 !sel_start_cursor.par->Next()->Last())
2112 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2114 #ifndef FIX_DOUBLE_SPACE
2115 // maybe a forgotten blank
2116 if (sel_start_cursor.pos
2117 && sel_start_cursor.par
2118 ->IsLineSeparator(sel_start_cursor.pos)
2119 && sel_start_cursor.par
2120 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2121 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2126 // sometimes necessary
2128 sel_start_cursor.par->ClearParagraph();
2130 RedoParagraphs(sel_start_cursor, endpar);
2133 cursor = sel_start_cursor;
2134 SetCursor(cursor.par, cursor.pos);
2135 sel_cursor = cursor;
2136 UpdateCounters(cursor.row);
2140 void LyXText::CopySelection()
2142 // this doesnt make sense, if there is no selection
2146 // ok we have a selection. This is always between sel_start_cursor
2147 // and sel_end cursor
2148 LyXParagraph * tmppar;
2150 /* check wether there are half footnotes in the selection */
2151 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2152 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2153 tmppar = sel_start_cursor.par;
2154 while (tmppar != sel_end_cursor.par) {
2155 if (tmppar->footnoteflag !=
2156 sel_end_cursor.par->footnoteflag) {
2157 WriteAlert(_("Impossible operation"),
2158 _("Don't know what to do"
2159 " with half floats."),
2163 tmppar = tmppar->Next();
2167 /* table stuff -- begin */
2168 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2169 if ( sel_start_cursor.par != sel_end_cursor.par){
2170 WriteAlert(_("Impossible operation"),
2171 _("Don't know what to do with half tables."),
2176 /* table stuff -- end */
2178 // delete the simple_cut_buffer
2179 DeleteSimpleCutBuffer();
2181 // set the textclass
2182 simple_cut_buffer_textclass = parameters->textclass;
2184 #ifdef FIX_DOUBLE_SPACE
2185 // copy behind a space if there is one
2186 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2187 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2188 && (sel_start_cursor.par != sel_end_cursor.par
2189 || sel_start_cursor.pos < sel_end_cursor.pos))
2190 sel_start_cursor.pos++;
2192 // there are two cases: copy only within one paragraph
2193 // or more than one paragraph
2194 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2195 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2196 // only within one paragraph
2197 simple_cut_buffer = new LyXParagraph;
2198 LyXParagraph::size_type i = 0;
2199 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2200 sel_start_cursor.par->CopyIntoMinibuffer(i);
2201 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2204 // copy more than one paragraph
2205 // clone the paragraphs within the selection
2207 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2208 simple_cut_buffer = tmppar->Clone();
2209 LyXParagraph *tmppar2 = simple_cut_buffer;
2211 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2213 tmppar = tmppar->next;
2214 tmppar2->next = tmppar->Clone();
2215 tmppar2->next->previous = tmppar2;
2216 tmppar2 = tmppar2->next;
2220 // care about footnotes
2221 if (simple_cut_buffer->footnoteflag) {
2222 tmppar = simple_cut_buffer;
2224 tmppar->footnoteflag =
2225 LyXParagraph::NO_FOOTNOTE;
2226 tmppar = tmppar->next;
2230 // the simple_cut_buffer paragraph is too big
2231 LyXParagraph::size_type tmpi2 =
2232 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2233 for (; tmpi2; --tmpi2)
2234 simple_cut_buffer->Erase(0);
2236 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2238 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2239 while (tmppar2->size() > tmpi2) {
2240 tmppar2->Erase(tmppar2->size() - 1);
2246 void LyXText::PasteSelection()
2248 // this does not make sense, if there is nothing to paste
2249 if (!simple_cut_buffer)
2252 LyXParagraph * tmppar;
2253 LyXParagraph * endpar;
2255 LyXCursor tmpcursor;
2257 // be carefull with footnotes in footnotes
2258 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2260 // check whether the cut_buffer includes a footnote
2261 tmppar = simple_cut_buffer;
2263 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2264 tmppar = tmppar->next;
2267 WriteAlert(_("Impossible operation"),
2268 _("Can't paste float into float!"),
2274 /* table stuff -- begin */
2275 if (cursor.par->table) {
2276 if (simple_cut_buffer->next) {
2277 WriteAlert(_("Impossible operation"),
2278 _("Table cell cannot include more than one paragraph!"),
2283 /* table stuff -- end */
2285 SetUndo(Undo::INSERT,
2286 cursor.par->ParFromPos(cursor.pos)->previous,
2287 cursor.par->ParFromPos(cursor.pos)->next);
2291 // There are two cases: cutbuffer only one paragraph or many
2292 if (!simple_cut_buffer->next) {
2293 // only within a paragraph
2295 #ifndef FIX_DOUBLE_SPACE
2296 // please break behind a space, if there is one
2297 while (tmpcursor.par->Last() > tmpcursor.pos
2298 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2301 tmppar = simple_cut_buffer->Clone();
2302 /* table stuff -- begin */
2303 bool table_too_small = false;
2304 if (tmpcursor.par->table) {
2305 while (simple_cut_buffer->size()
2306 && !table_too_small) {
2307 if (simple_cut_buffer->IsNewline(0)){
2308 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2310 simple_cut_buffer->Erase(0);
2311 if (tmpcursor.pos < tmpcursor.par->Last())
2314 table_too_small = true;
2316 #ifdef FIX_DOUBLE_SPACE
2317 // This is an attempt to fix the
2318 // "never insert a space at the
2319 // beginning of a paragraph" problem.
2320 if (tmpcursor.pos == 0
2321 && simple_cut_buffer->IsLineSeparator(0)) {
2322 simple_cut_buffer->Erase(0);
2324 simple_cut_buffer->CutIntoMinibuffer(0);
2325 simple_cut_buffer->Erase(0);
2326 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2330 simple_cut_buffer->CutIntoMinibuffer(0);
2331 simple_cut_buffer->Erase(0);
2332 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2338 /* table stuff -- end */
2339 // Some provisions should be done here for checking
2340 // if we are inserting at the beginning of a
2341 // paragraph. If there are a space at the beginning
2342 // of the text to insert and we are inserting at
2343 // the beginning of the paragraph the space should
2345 while (simple_cut_buffer->size()) {
2346 #ifdef FIX_DOUBLE_SPACE
2347 // This is an attempt to fix the
2348 // "never insert a space at the
2349 // beginning of a paragraph" problem.
2350 if (tmpcursor.pos == 0
2351 && simple_cut_buffer->IsLineSeparator(0)) {
2352 simple_cut_buffer->Erase(0);
2354 simple_cut_buffer->CutIntoMinibuffer(0);
2355 simple_cut_buffer->Erase(0);
2356 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2360 simple_cut_buffer->CutIntoMinibuffer(0);
2361 simple_cut_buffer->Erase(0);
2362 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2367 delete simple_cut_buffer;
2368 simple_cut_buffer = tmppar;
2369 endpar = tmpcursor.par->Next();
2373 // make a copy of the simple cut_buffer
2374 tmppar = simple_cut_buffer;
2375 LyXParagraph * simple_cut_clone = tmppar->Clone();
2376 LyXParagraph * tmppar2 = simple_cut_clone;
2377 if (cursor.par->footnoteflag){
2378 tmppar->footnoteflag = cursor.par->footnoteflag;
2379 tmppar->footnotekind = cursor.par->footnotekind;
2381 while (tmppar->next) {
2382 tmppar = tmppar->next;
2383 tmppar2->next = tmppar->Clone();
2384 tmppar2->next->previous = tmppar2;
2385 tmppar2 = tmppar2->next;
2386 if (cursor.par->footnoteflag){
2387 tmppar->footnoteflag = cursor.par->footnoteflag;
2388 tmppar->footnotekind = cursor.par->footnotekind;
2392 // make sure there is no class difference
2393 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2394 parameters->textclass,
2397 // make the simple_cut_buffer exactly the same layout than
2398 // the cursor paragraph
2399 simple_cut_buffer->MakeSameLayout(cursor.par);
2401 // find the end of the buffer
2402 LyXParagraph * lastbuffer = simple_cut_buffer;
2403 while (lastbuffer->Next())
2404 lastbuffer = lastbuffer->Next();
2406 #ifndef FIX_DOUBLE_SPACE
2407 // Please break behind a space, if there is one. The space
2408 // should be copied too.
2409 if (cursor.par->Last() > cursor.pos
2410 && cursor.par->IsLineSeparator(cursor.pos))
2413 bool paste_the_end = false;
2415 // open the paragraph for inserting the simple_cut_buffer
2417 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2418 cursor.par->BreakParagraphConservative(cursor.pos);
2419 paste_the_end = true;
2422 #ifndef FIX_DOUBLE_SPACE
2423 // be careful with double spaces
2424 if ((!cursor.par->Last()
2425 || cursor.par->IsLineSeparator(cursor.pos - 1)
2426 || cursor.par->IsNewline(cursor.pos - 1))
2427 && simple_cut_buffer->text.size()
2428 && simple_cut_buffer->IsLineSeparator(0))
2429 simple_cut_buffer->Erase(0);
2431 // set the end for redoing later
2432 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2435 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2436 cursor.par->ParFromPos(cursor.pos)->next;
2437 cursor.par->ParFromPos(cursor.pos)->next->previous =
2438 lastbuffer->ParFromPos(lastbuffer->Last());
2440 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2441 simple_cut_buffer->previous =
2442 cursor.par->ParFromPos(cursor.pos);
2444 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2445 lastbuffer = cursor.par;
2447 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2449 // store the new cursor position
2450 tmpcursor.par = lastbuffer;
2451 tmpcursor.pos = lastbuffer->Last();
2453 // maybe some pasting
2454 if (lastbuffer->Next() && paste_the_end) {
2455 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2456 #ifndef FIX_DOUBLE_SPACE
2457 // be careful with double spaces
2458 if ((!lastbuffer->Last()
2459 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2460 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2461 && lastbuffer->Next()->Last()
2462 && lastbuffer->Next()->IsLineSeparator(0))
2463 lastbuffer->Next()->Erase(0);
2465 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2467 } else if (!lastbuffer->Next()->Last()) {
2468 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2469 #ifndef FIX_DOUBLE_SPACE
2470 // be careful witth double spaces
2471 if ((!lastbuffer->Last()
2472 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2473 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2474 && lastbuffer->Next()->Last()
2475 && lastbuffer->Next()->IsLineSeparator(0))
2476 lastbuffer->Next()->Erase(0);
2478 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2480 } else if (!lastbuffer->Last()) {
2481 lastbuffer->MakeSameLayout(lastbuffer->next);
2482 #ifndef FIX_DOUBLE_SPACE
2483 // be careful witth double spaces
2484 if ((!lastbuffer->Last()
2485 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2486 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2487 && lastbuffer->Next()->Last()
2488 && lastbuffer->Next()->IsLineSeparator(0))
2489 lastbuffer->Next()->Erase(0);
2491 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2494 lastbuffer->Next()->ClearParagraph();
2497 // restore the simple cut buffer
2498 simple_cut_buffer = simple_cut_clone;
2501 RedoParagraphs(cursor, endpar);
2503 SetCursor(cursor.par, cursor.pos);
2506 sel_cursor = cursor;
2507 SetCursor(tmpcursor.par, tmpcursor.pos);
2509 UpdateCounters(cursor.row);
2513 // returns a pointer to the very first LyXParagraph
2514 LyXParagraph * LyXText::FirstParagraph() const
2516 return params->paragraph;
2520 // returns true if the specified string is at the specified position
2521 bool LyXText::IsStringInText(LyXParagraph * par,
2522 LyXParagraph::size_type pos,
2523 char const * str) const
2527 while (pos + i < par->Last() && str[i] &&
2528 str[i] == par->GetChar(pos + i)) {
2538 // sets the selection over the number of characters of string, no check!!
2539 void LyXText::SetSelectionOverString(char const * string)
2541 sel_cursor = cursor;
2542 for (int i = 0; string[i]; ++i)
2548 // simple replacing. The font of the first selected character is used
2549 void LyXText::ReplaceSelectionWithString(char const * str)
2554 if (!selection) { // create a dummy selection
2555 sel_end_cursor = cursor;
2556 sel_start_cursor = cursor;
2559 // Get font setting before we cut
2560 LyXParagraph::size_type pos = sel_end_cursor.pos;
2561 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2563 // Insert the new string
2564 for (int i = 0; str[i]; ++i) {
2565 sel_end_cursor.par->InsertChar(pos, str[i]);
2566 sel_end_cursor.par->SetFont(pos, font);
2570 // Cut the selection
2577 // if the string can be found: return true and set the cursor to
2579 bool LyXText::SearchForward(char const * str) const
2581 LyXParagraph * par = cursor.par;
2582 LyXParagraph::size_type pos = cursor.pos;
2583 while (par && !IsStringInText(par, pos, str)) {
2584 if (pos < par->Last() - 1)
2592 SetCursor(par, pos);
2600 bool LyXText::SearchBackward(char const * string) const
2602 LyXParagraph * par = cursor.par;
2603 int pos = cursor.pos;
2609 // We skip empty paragraphs (Asger)
2611 par = par->Previous();
2613 pos = par->Last() - 1;
2614 } while (par && pos < 0);
2616 } while (par && !IsStringInText(par, pos, string));
2619 SetCursor(par, pos);
2626 // needed to insert the selection
2627 void LyXText::InsertStringA(string const & str)
2629 LyXParagraph * par = cursor.par;
2630 LyXParagraph::size_type pos = cursor.pos;
2631 LyXParagraph::size_type a = 0;
2633 LyXParagraph * endpar = cursor.par->Next();
2638 textclasslist.Style(parameters->textclass,
2639 cursor.par->GetLayout()).isEnvironment();
2640 // only to be sure, should not be neccessary
2643 // insert the string, don't insert doublespace
2644 string::size_type i = 0;
2645 while (i < str.length()) {
2646 if (str[i] != '\n') {
2648 && i + 1 < str.length() && str[i + 1] != ' '
2649 && pos && par->GetChar(pos - 1)!= ' ') {
2650 par->InsertChar(pos,' ');
2651 par->SetFont(pos, current_font);
2653 } else if (par->table) {
2654 if (str[i] == '\t') {
2655 while((pos < par->size()) &&
2656 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2658 if (pos < par->size())
2660 else // no more fields to fill skip the rest
2662 } else if ((str[i] != 13) &&
2663 ((str[i] & 127) >= ' ')) {
2664 par->InsertChar(pos, str[i]);
2665 par->SetFont(pos, current_font);
2668 } else if (str[i] == ' ') {
2670 InsetSpecialChar * new_inset =
2671 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2672 par->InsertChar(pos, LyXParagraph::META_INSET);
2673 par->SetFont(pos, current_font);
2674 par->InsertInset(pos, new_inset);
2676 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2677 par->SetFont(pos, current_font);
2680 } else if (str[i] == '\t') {
2681 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2683 InsetSpecialChar * new_inset =
2684 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2685 par->InsertChar(pos, LyXParagraph::META_INSET);
2686 par->SetFont(pos, current_font);
2687 par->InsertInset(pos, new_inset);
2689 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2690 par->SetFont(a, current_font);
2694 } else if (str[i] != 13 &&
2695 // Ignore unprintables
2696 (str[i] & 127) >= ' ') {
2697 par->InsertChar(pos, str[i]);
2698 par->SetFont(pos, current_font);
2703 if (i + 1 >= str.length()) {
2707 while((pos < par->size()) &&
2708 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2711 cell = NumberOfCell(par, pos);
2712 while((pos < par->size()) &&
2713 !(par->table->IsFirstCell(cell))) {
2715 while((pos < par->size()) &&
2716 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2719 cell = NumberOfCell(par, pos);
2721 if (pos >= par->size())
2722 // no more fields to fill skip the rest
2725 if (!par->size()) { // par is empty
2727 InsetSpecialChar * new_inset =
2728 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2729 par->InsertChar(pos, LyXParagraph::META_INSET);
2730 par->SetFont(pos, current_font);
2731 par->InsertInset(pos, new_inset);
2733 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2734 par->SetFont(pos, current_font);
2738 par->BreakParagraph(pos, flag);
2746 RedoParagraphs(cursor, endpar);
2747 SetCursor(cursor.par, cursor.pos);
2748 sel_cursor = cursor;
2749 SetCursor(par, pos);
2754 /* turns double-CR to single CR, others where converted into one blank and 13s
2755 * that are ignored .Double spaces are also converted into one. Spaces at
2756 * the beginning of a paragraph are forbidden. tabs are converted into one
2757 * space. then InsertStringA is called */
2758 void LyXText::InsertStringB(string const & s)
2761 LyXParagraph * par = cursor.par;
2762 string::size_type i = 1;
2763 while (i < str.length()) {
2764 if (str[i] == '\t' && !par->table)
2766 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2768 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2769 if (str[i + 1] != '\n') {
2770 if (str[i - 1] != ' ')
2775 while (i + 1 < str.length()
2776 && (str[i + 1] == ' '
2777 || str[i + 1] == '\t'
2778 || str[i + 1] == '\n'
2779 || str[i + 1] == 13)) {
2790 bool LyXText::GotoNextError() const
2792 LyXCursor res = cursor;
2794 if (res.pos < res.par->Last() - 1) {
2798 res.par = res.par->Next();
2803 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2804 && res.par->GetInset(res.pos)->AutoDelete()));
2807 SetCursor(res.par, res.pos);
2814 bool LyXText::GotoNextNote() const
2816 LyXCursor res = cursor;
2818 if (res.pos < res.par->Last() - 1) {
2821 res.par = res.par->Next();
2826 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2827 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2830 SetCursor(res.par, res.pos);
2837 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2838 LyXTextClassList::size_type class2,
2842 if (!par || class1 == class2)
2844 par = par->FirstPhysicalPar();
2846 string name = textclasslist.NameOfLayout(class1, par->layout);
2848 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2849 textclasslist.NumberOfLayout(class2, name);
2852 } else { // layout not found
2853 // use default layout "Standard" (0)
2858 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2860 string s = "Layout had to be changed from\n"
2861 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2862 + "\nbecause of class conversion from\n"
2863 + textclasslist.NameOfClass(class1) + " to "
2864 + textclasslist.NameOfClass(class2);
2865 InsetError * new_inset = new InsetError(s);
2866 par->InsertChar(0, LyXParagraph::META_INSET);
2867 par->InsertInset(0, new_inset);
2876 void LyXText::CheckParagraph(LyXParagraph * par,
2877 LyXParagraph::size_type pos)
2880 LyXCursor tmpcursor;
2882 /* table stuff -- begin*/
2885 CheckParagraphInTable(par, pos);
2888 /* table stuff -- end*/
2891 LyXParagraph::size_type z;
2892 Row * row = GetRow(par, pos, y);
2894 // is there a break one row above
2895 if (row->previous && row->previous->par == row->par) {
2896 z = NextBreakPoint(row->previous, paperwidth);
2897 if ( z >= row->pos) {
2898 // set the dimensions of the row above
2899 y -= row->previous->height;
2901 refresh_row = row->previous;
2902 status = LyXText::NEED_MORE_REFRESH;
2904 BreakAgain(row->previous);
2906 // set the cursor again. Otherwise
2907 // dangling pointers are possible
2908 SetCursor(cursor.par, cursor.pos);
2909 sel_cursor = cursor;
2914 int tmpheight = row->height;
2915 LyXParagraph::size_type tmplast = RowLast(row);
2920 if (row->height == tmpheight && RowLast(row) == tmplast)
2921 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2923 status = LyXText::NEED_MORE_REFRESH;
2925 // check the special right address boxes
2926 if (textclasslist.Style(parameters->textclass,
2927 par->GetLayout()).margintype
2928 == MARGIN_RIGHT_ADDRESS_BOX) {
2929 tmpcursor.par = par;
2930 tmpcursor.row = row;
2933 tmpcursor.x_fix = 0;
2934 tmpcursor.pos = pos;
2935 RedoDrawingOfParagraph(tmpcursor);
2940 // set the cursor again. Otherwise dangling pointers are possible
2941 // also set the selection
2945 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2946 sel_cursor = cursor;
2947 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2948 sel_start_cursor = cursor;
2949 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2950 sel_end_cursor = cursor;
2951 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2952 last_sel_cursor = cursor;
2955 SetCursorIntern(cursor.par, cursor.pos);
2959 // returns 0 if inset wasn't found
2960 int LyXText::UpdateInset(Inset * inset)
2962 // first check the current paragraph
2963 int pos = cursor.par->GetPositionOfInset(inset);
2965 CheckParagraph(cursor.par, pos);
2969 // check every paragraph
2971 LyXParagraph * par = FirstParagraph();
2973 // make sure the paragraph is open
2974 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2975 pos = par->GetPositionOfInset(inset);
2977 CheckParagraph(par, pos);
2988 void LyXText::SetCursor(LyXParagraph * par,
2989 LyXParagraph::size_type pos, bool setfont) const
2991 LyXCursor old_cursor = cursor;
2992 SetCursorIntern(par, pos, setfont);
2993 DeleteEmptyParagraphMechanism(old_cursor);
2997 void LyXText::SetCursorIntern(LyXParagraph * par,
2998 LyXParagraph::size_type pos, bool setfont) const
3000 // correct the cursor position if impossible
3001 if (pos > par->Last()){
3002 LyXParagraph * tmppar = par->ParFromPos(pos);
3003 pos = par->PositionInParFromPos(pos);
3006 if (par->IsDummy() && par->previous &&
3007 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3008 while (par->previous &&
3009 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3010 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3011 par = par->previous ;
3012 if (par->IsDummy() &&
3013 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3014 pos += par->size() + 1;
3016 if (par->previous) {
3017 par = par->previous;
3019 pos += par->size() + 1;
3027 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3028 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3029 && !cursor.par->IsSeparator(cursor.pos))
3030 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3032 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3033 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3035 current_font = cursor.par->GetFontSettings(cursor.pos);
3036 real_current_font = GetFont(cursor.par, cursor.pos);
3039 /* get the cursor y position in text */
3041 Row * row = GetRow(par, pos, y);
3042 /* y is now the beginning of the cursor row */
3044 /* y is now the cursor baseline */
3047 /* now get the cursors x position */
3049 float fill_separator, fill_hfill, fill_label_hfill;
3050 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3051 LyXParagraph::size_type cursor_vpos;
3052 LyXParagraph::size_type last = RowLast(row);
3053 if (row->pos > last)
3055 else if (pos > last)
3056 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3057 ? log2vis(last)+1 : log2vis(last);
3059 LyXDirection letter_direction =
3060 row->par->getLetterDirection(pos);
3061 LyXDirection font_direction =
3062 (real_current_font.isVisibleRightToLeft())
3063 ? LYX_DIR_RIGHT_TO_LEFT : LYX_DIR_LEFT_TO_RIGHT;
3064 if (letter_direction == font_direction
3066 || (row->par->table && row->par->IsNewline(pos-1)))
3067 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3068 ? log2vis(pos) : log2vis(pos) + 1;
3070 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3071 ? log2vis(pos-1) + 1 : log2vis(pos - 1);
3074 /* table stuff -- begin*/
3075 if (row->par->table) {
3076 int cell = NumberOfCell(row->par, row->pos);
3078 x += row->par->table->GetBeginningOfTextInCell(cell);
3079 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3080 pos = vis2log(vpos);
3081 if (row->par->IsNewline(pos)) {
3082 x = x_old + row->par->table->WidthOfColumn(cell);
3085 x += row->par->table->GetBeginningOfTextInCell(cell);
3087 x += SingleWidth(row->par, pos);
3091 /* table stuff -- end*/
3092 LyXParagraph::size_type main_body =
3093 BeginningOfMainBody(row->par);
3094 if (main_body > 0 &&
3095 (main_body-1 > last ||
3096 !row->par->IsLineSeparator(main_body-1)))
3099 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3100 pos = vis2log(vpos);
3101 if (main_body > 0 && pos == main_body-1) {
3102 x += fill_label_hfill +
3103 GetFont(row->par, -2).stringWidth(
3104 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3105 if (row->par->IsLineSeparator(main_body-1))
3106 x -= SingleWidth(row->par, main_body-1);
3109 x += SingleWidth(row->par, pos);
3110 if (HfillExpansion(row, pos)) {
3111 if (pos >= main_body)
3114 x += fill_label_hfill;
3116 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3124 cursor.x_fix = cursor.x;
3129 void LyXText::SetCursorFromCoordinates(int x, long y) const
3131 LyXCursor old_cursor = cursor;
3133 /* get the row first */
3135 Row * row = GetRowNearY(y);
3137 cursor.par = row->par;
3139 int column = GetColumnNearX(row, x);
3140 cursor.pos = row->pos + column;
3142 cursor.y = y + row->baseline;
3147 (cursor.pos == cursor.par->Last()
3148 || cursor.par->IsSeparator(cursor.pos)
3149 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3150 && !cursor.par->IsSeparator(cursor.pos))
3151 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3153 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3154 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3156 current_font = cursor.par->GetFontSettings(cursor.pos);
3157 real_current_font = GetFont(cursor.par, cursor.pos);
3159 DeleteEmptyParagraphMechanism(old_cursor);
3163 void LyXText::CursorLeft() const
3166 if (cursor.par->table) {
3167 int cell = NumberOfCell(cursor.par, cursor.pos);
3168 if (cursor.par->table->IsContRow(cell) &&
3169 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3176 void LyXText::CursorLeftIntern() const
3178 if (cursor.pos > 0) {
3179 SetCursor(cursor.par, cursor.pos - 1);
3181 else if (cursor.par->Previous()) { // steps into the above paragraph.
3182 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3187 void LyXText::CursorRight() const
3189 CursorRightIntern();
3190 if (cursor.par->table) {
3191 int cell = NumberOfCell(cursor.par, cursor.pos);
3192 if (cursor.par->table->IsContRow(cell) &&
3193 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3200 void LyXText::CursorRightIntern() const
3202 if (cursor.pos < cursor.par->Last()) {
3203 SetCursor(cursor.par, cursor.pos + 1);
3205 else if (cursor.par->Next()) {
3206 SetCursor(cursor.par->Next(), 0);
3211 void LyXText::CursorUp() const
3213 SetCursorFromCoordinates(cursor.x_fix,
3214 cursor.y - cursor.row->baseline - 1);
3215 if (cursor.par->table) {
3216 int cell = NumberOfCell(cursor.par, cursor.pos);
3217 if (cursor.par->table->IsContRow(cell) &&
3218 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3225 void LyXText::CursorDown() const
3227 if (cursor.par->table &&
3228 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3231 SetCursorFromCoordinates(cursor.x_fix,
3232 cursor.y - cursor.row->baseline
3233 + cursor.row->height + 1);
3234 if (cursor.par->table) {
3235 int cell = NumberOfCell(cursor.par, cursor.pos);
3236 int cell_above = cursor.par->table->GetCellAbove(cell);
3237 while(cursor.par->table &&
3238 cursor.par->table->IsContRow(cell) &&
3239 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3240 SetCursorFromCoordinates(cursor.x_fix,
3241 cursor.y - cursor.row->baseline
3242 + cursor.row->height + 1);
3243 if (cursor.par->table) {
3244 cell = NumberOfCell(cursor.par, cursor.pos);
3245 cell_above = cursor.par->table->GetCellAbove(cell);
3252 void LyXText::CursorUpParagraph() const
3254 if (cursor.pos > 0) {
3255 SetCursor(cursor.par, 0);
3257 else if (cursor.par->Previous()) {
3258 SetCursor(cursor.par->Previous(), 0);
3263 void LyXText::CursorDownParagraph() const
3265 if (cursor.par->Next()) {
3266 SetCursor(cursor.par->Next(), 0);
3268 SetCursor(cursor.par, cursor.par->Last());
3274 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3276 DebugTracer trc1("1");
3278 // Would be wrong to delete anything if we have a selection.
3279 if (selection) return;
3281 // We allow all kinds of "mumbo-jumbo" when freespacing.
3282 if (textclasslist.Style(parameters->textclass,
3283 old_cursor.par->GetLayout()).free_spacing)
3286 bool deleted = false;
3288 #ifdef FIX_DOUBLE_SPACE
3289 /* Ok I'll put some comments here about what is missing.
3290 I have fixed BackSpace (and thus Delete) to not delete
3291 double-spaces automagically. I have also changed Cut,
3292 Copy and Paste to hopefully do some sensible things.
3293 There are still some small problems that can lead to
3294 double spaces stored in the document file or space at
3295 the beginning of paragraphs. This happens if you have
3296 the cursor betwenn to spaces and then save. Or if you
3297 cut and paste and the selection have a space at the
3298 beginning and then save right after the paste. I am
3299 sure none of these are very hard to fix, but I will
3300 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3301 that I can get some feedback. (Lgb)
3304 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3305 // delete the LineSeparator.
3308 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3309 // delete the LineSeparator.
3312 // If the pos around the old_cursor were spaces, delete one of them.
3313 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3314 DebugTracer trc2("2");
3316 if (old_cursor.pos > 0
3317 && old_cursor.pos < old_cursor.par->Last()
3318 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3319 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3320 DebugTracer trc3("3");
3322 lyxerr << "Old cursor pos: " << old_cursor.pos << endl;
3323 old_cursor.par->Erase(old_cursor.pos - 1);
3324 status = LyXText::NEED_MORE_REFRESH;
3326 if (old_cursor.par == cursor.par &&
3327 cursor.pos > old_cursor.pos) {
3328 DebugTracer trc4("4");
3331 //SetCursor(cursor.par, cursor.pos - 1);
3336 // SetCursor(cursor.par, cursor.pos);
3342 // Do not delete empty paragraphs with keepempty set.
3343 if ((textclasslist.Style(parameters->textclass,
3344 old_cursor.par->GetLayout())).keepempty)
3347 LyXCursor tmpcursor;
3349 if (old_cursor.par != cursor.par) {
3350 DebugTracer trc5("5");
3352 if ( (old_cursor.par->Last() == 0
3353 || (old_cursor.par->Last() == 1
3354 && old_cursor.par->IsLineSeparator(0)))
3355 && old_cursor.par->FirstPhysicalPar()
3356 == old_cursor.par->LastPhysicalPar()) {
3357 DebugTracer trc6("6");
3360 // ok, we will delete anything
3362 // make sure that you do not delete any environments
3363 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3364 !(old_cursor.row->previous
3365 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3366 && !(old_cursor.row->next
3367 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3368 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3369 && ((old_cursor.row->previous
3370 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3371 || (old_cursor.row->next
3372 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3374 DebugTracer trc7("7");
3376 status = LyXText::NEED_MORE_REFRESH;
3379 if (old_cursor.row->previous) {
3380 DebugTracer trc8("8");
3382 refresh_row = old_cursor.row->previous;
3383 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3385 cursor = old_cursor; // that undo can restore the right cursor position
3386 LyXParagraph * endpar = old_cursor.par->next;
3387 if (endpar && endpar->GetDepth()) {
3388 DebugTracer trc9("9");
3390 while (endpar && endpar->GetDepth()) {
3391 endpar = endpar->LastPhysicalPar()->Next();
3394 SetUndo(Undo::DELETE,
3395 old_cursor.par->previous,
3400 RemoveRow(old_cursor.row);
3401 if (params->paragraph == old_cursor.par) {
3402 DebugTracer trc10("10");
3405 params->paragraph = params->paragraph->next;
3408 delete old_cursor.par;
3410 /* Breakagain the next par. Needed
3411 * because of the parindent that
3412 * can occur or dissappear. The
3413 * next row can change its height,
3414 * if there is another layout before */
3415 if (refresh_row->next) {
3416 DebugTracer trc11("11");
3418 BreakAgain(refresh_row->next);
3419 UpdateCounters(refresh_row);
3421 SetHeightOfRow(refresh_row);
3423 DebugTracer trc12("12");
3425 refresh_row = old_cursor.row->next;
3426 refresh_y = old_cursor.y - old_cursor.row->baseline;
3429 cursor = old_cursor; // that undo can restore the right cursor position
3430 LyXParagraph *endpar = old_cursor.par->next;
3431 if (endpar && endpar->GetDepth()) {
3432 DebugTracer trc13("13");
3434 while (endpar && endpar->GetDepth()) {
3435 endpar = endpar->LastPhysicalPar()->Next();
3438 SetUndo(Undo::DELETE,
3439 old_cursor.par->previous,
3444 RemoveRow(old_cursor.row);
3446 if (params->paragraph == old_cursor.par) {
3447 DebugTracer trc14("14");
3449 params->paragraph = params->paragraph->next;
3451 delete old_cursor.par;
3453 /* Breakagain the next par. Needed
3454 because of the parindent that can
3455 occur or dissappear.
3456 The next row can change its height,
3457 if there is another layout before
3460 DebugTracer trc15("15");
3462 BreakAgain(refresh_row);
3463 UpdateCounters(refresh_row->previous);
3468 SetCursor(cursor.par, cursor.pos);
3470 /* if (cursor.y > old_cursor.y)
3471 cursor.y -= old_cursor.row->height; */
3473 if (sel_cursor.par == old_cursor.par
3474 && sel_cursor.pos == sel_cursor.pos) {
3475 DebugTracer trc16("16");
3477 // correct selection
3478 sel_cursor = cursor;
3483 DebugTracer trc17("17");
3485 if (old_cursor.par->ClearParagraph()){
3486 DebugTracer trc18("18");
3488 RedoParagraphs(old_cursor, old_cursor.par->Next());
3490 SetCursor(cursor.par, cursor.pos);
3491 sel_cursor = cursor;
3499 LyXParagraph * LyXText::GetParFromID(int id)
3501 LyXParagraph * result = FirstParagraph();
3502 while (result && result->id() != id)
3503 result = result->next;
3509 bool LyXText::TextUndo()
3511 // returns false if no undo possible
3512 Undo * undo = params->undostack.pop();
3517 .push(CreateUndo(undo->kind,
3518 GetParFromID(undo->number_of_before_par),
3519 GetParFromID(undo->number_of_behind_par)));
3521 return TextHandleUndo(undo);
3525 bool LyXText::TextRedo()
3527 // returns false if no redo possible
3528 Undo * undo = params->redostack.pop();
3533 .push(CreateUndo(undo->kind,
3534 GetParFromID(undo->number_of_before_par),
3535 GetParFromID(undo->number_of_behind_par)));
3537 return TextHandleUndo(undo);
3541 bool LyXText::TextHandleUndo(Undo * undo)
3543 // returns false if no undo possible
3544 bool result = false;
3546 LyXParagraph * before =
3547 GetParFromID(undo->number_of_before_par);
3548 LyXParagraph * behind =
3549 GetParFromID(undo->number_of_behind_par);
3550 LyXParagraph * tmppar;
3551 LyXParagraph * tmppar2;
3552 LyXParagraph * endpar;
3553 LyXParagraph * tmppar5;
3555 // if there's no before take the beginning
3556 // of the document for redoing
3558 SetCursorIntern(FirstParagraph(), 0);
3560 // replace the paragraphs with the undo informations
3562 LyXParagraph * tmppar3 = undo->par;
3563 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3564 LyXParagraph * tmppar4 = tmppar3;
3566 while (tmppar4->next)
3567 tmppar4 = tmppar4->next;
3568 } // get last undo par
3570 // now remove the old text if there is any
3571 if (before != behind || (!behind && !before)){
3573 tmppar5 = before->next;
3575 tmppar5 = params->paragraph;
3577 while (tmppar5 && tmppar5 != behind){
3579 tmppar5 = tmppar5->next;
3580 // a memory optimization for edit: Only layout information
3581 // is stored in the undo. So restore the text informations.
3582 if (undo->kind == Undo::EDIT) {
3583 tmppar2->setContentsFromPar(tmppar);
3584 tmppar->clearContents();
3585 //tmppar2->text = tmppar->text;
3586 //tmppar->text.clear();
3587 tmppar2 = tmppar2->next;
3589 if ( currentrow && currentrow->par == tmppar )
3590 currentrow = currentrow -> previous;
3591 // Commenting out this might remove the error
3592 // reported by Purify, but it might also
3593 // introduce a memory leak. We need to
3599 // put the new stuff in the list if there is one
3602 before->next = tmppar3;
3604 params->paragraph = tmppar3;
3605 tmppar3->previous = before;
3609 params->paragraph = behind;
3612 tmppar4->next = behind;
3614 behind->previous = tmppar4;
3618 // Set the cursor for redoing
3620 SetCursorIntern(before->FirstSelfrowPar(), 0);
3621 // check wether before points to a closed float and open it if necessary
3622 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3623 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3625 while (tmppar4->previous &&
3626 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3627 tmppar4 = tmppar4->previous;
3628 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3629 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3630 tmppar4 = tmppar4->next;
3635 // open a cosed footnote at the end if necessary
3636 if (behind && behind->previous &&
3637 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3638 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3639 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3640 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3641 behind = behind->next;
3645 // calculate the endpar for redoing the paragraphs.
3647 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3648 endpar = behind->LastPhysicalPar()->Next();
3650 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3655 tmppar = GetParFromID(undo->number_of_cursor_par);
3656 RedoParagraphs(cursor, endpar);
3658 SetCursorIntern(tmppar, undo->cursor_pos);
3659 UpdateCounters(cursor.row);
3669 void LyXText::FinishUndo()
3671 // makes sure the next operation will be stored
3672 undo_finished = True;
3676 void LyXText::FreezeUndo()
3678 // this is dangerous and for internal use only
3683 void LyXText::UnFreezeUndo()
3685 // this is dangerous and for internal use only
3686 undo_frozen = false;
3690 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3691 LyXParagraph const * behind) const
3694 params->undostack.push(CreateUndo(kind, before, behind));
3695 params->redostack.clear();
3699 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3700 LyXParagraph const * behind)
3702 params->redostack.push(CreateUndo(kind, before, behind));
3706 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3707 LyXParagraph const * behind) const
3709 int before_number = -1;
3710 int behind_number = -1;
3712 before_number = before->id();
3714 behind_number = behind->id();
3715 // Undo::EDIT and Undo::FINISH are
3716 // always finished. (no overlapping there)
3717 // overlapping only with insert and delete inside one paragraph:
3718 // Nobody wants all removed character
3719 // appear one by one when undoing.
3720 // EDIT is special since only layout information, not the
3721 // contents of a paragaph are stored.
3722 if (!undo_finished && kind != Undo::EDIT &&
3723 kind != Undo::FINISH){
3724 // check wether storing is needed
3725 if (!params->undostack.empty() &&
3726 params->undostack.top()->kind == kind &&
3727 params->undostack.top()->number_of_before_par == before_number &&
3728 params->undostack.top()->number_of_behind_par == behind_number ){
3733 // create a new Undo
3734 LyXParagraph * undopar;
3735 LyXParagraph * tmppar;
3736 LyXParagraph * tmppar2;
3738 LyXParagraph * start = 0;
3739 LyXParagraph * end = 0;
3742 start = before->next;
3744 start = FirstParagraph();
3746 end = behind->previous;
3748 end = FirstParagraph();
3754 && start != end->next
3755 && (before != behind || (!before && !behind))) {
3757 tmppar2 = tmppar->Clone();
3758 tmppar2->id(tmppar->id());
3760 // a memory optimization: Just store the layout information
3762 if (kind == Undo::EDIT){
3763 //tmppar2->text.clear();
3764 tmppar2->clearContents();
3769 while (tmppar != end && tmppar->next) {
3770 tmppar = tmppar->next;
3771 tmppar2->next = tmppar->Clone();
3772 tmppar2->next->id(tmppar->id());
3773 // a memory optimization: Just store the layout
3774 // information when only edit
3775 if (kind == Undo::EDIT){
3776 //tmppar2->next->text.clear();
3777 tmppar2->clearContents();
3779 tmppar2->next->previous = tmppar2;
3780 tmppar2 = tmppar2->next;
3784 undopar = 0; // nothing to replace (undo of delete maybe)
3786 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3787 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3789 Undo * undo = new Undo(kind,
3790 before_number, behind_number,
3791 cursor_par, cursor_pos,
3794 undo_finished = false;
3799 void LyXText::SetCursorParUndo()
3801 SetUndo(Undo::FINISH,
3802 cursor.par->ParFromPos(cursor.pos)->previous,
3803 cursor.par->ParFromPos(cursor.pos)->next);
3807 void LyXText::RemoveTableRow(LyXCursor * cur) const
3813 // move to the previous row
3814 int cell_act = NumberOfCell(cur->par, cur->pos);
3817 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3820 !cur->par->table->IsFirstCell(cell_act)) {
3822 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3827 // now we have to pay attention if the actual table is the
3828 // main row of TableContRows and if yes to delete all of them
3833 // delete up to the next row
3834 while (cur->pos < cur->par->Last() &&
3836 || !cur->par->table->IsFirstCell(cell_act))) {
3837 while (cur->pos < cur->par->Last() &&
3838 !cur->par->IsNewline(cur->pos))
3839 cur->par->Erase(cur->pos);
3842 if (cur->pos < cur->par->Last())
3843 cur->par->Erase(cur->pos);
3845 if (cur->pos && cur->pos == cur->par->Last()) {
3847 cur->par->Erase(cur->pos); // no newline at very end!
3849 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3850 !cur->par->table->IsContRow(cell_org) &&
3851 cur->par->table->IsContRow(cell));
3852 cur->par->table->DeleteRow(cell_org);
3857 bool LyXText::IsEmptyTableCell() const
3859 LyXParagraph::size_type pos = cursor.pos - 1;
3860 while (pos >= 0 && pos < cursor.par->Last()
3861 && !cursor.par->IsNewline(pos))
3863 return cursor.par->IsNewline(pos + 1);
3867 void LyXText::toggleAppendix(){
3868 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3869 bool start = !par->start_of_appendix;
3871 // ensure that we have only one start_of_appendix in this document
3872 LyXParagraph * tmp = FirstParagraph();
3873 for (; tmp; tmp = tmp->next)
3874 tmp->start_of_appendix = 0;
3875 par->start_of_appendix = start;
3877 // we can set the refreshing parameters now
3878 status = LyXText::NEED_MORE_REFRESH;
3880 refresh_row = 0; // not needed for full update
3882 SetCursor(cursor.par, cursor.pos);