1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-1999 The LyX Team.
9 * ====================================================== */
14 #include FORMS_H_LOCATION
18 #pragma implementation "lyxtext.h"
22 #include "lyxparagraph.h"
23 #include "insets/inseterror.h"
24 #include "insets/insetbib.h"
25 #include "insets/insetspecialchar.h"
28 #include "support/textutils.h"
30 #include "minibuffer.h"
32 #include "bufferparams.h"
33 #include "lyx_gui_misc.h"
36 #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() != 2)
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)->text.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, 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, 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 char first_footnote_par_is_not_empty = tmppar->next->text.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->text.size()
1177 && !firsttmppar->IsSeparator(firsttmppar->text.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 static char const * alphaCounter(int n) {
1352 static char result[2];
1357 result[0] = 'A' + n;
1365 // set the counter of a paragraph. This includes the labels
1366 void LyXText::SetCounter(LyXParagraph * par) const
1368 // this is only relevant for the beginning of paragraph
1369 par = par->FirstPhysicalPar();
1371 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1374 LyXTextClass const & textclass =
1375 textclasslist.TextClass(parameters->textclass);
1377 /* copy the prev-counters to this one, unless this is the start of a
1378 footnote or of a bibliography or the very first paragraph */
1380 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1381 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1382 && par->footnotekind == LyXParagraph::FOOTNOTE)
1383 && !(textclasslist.Style(parameters->textclass,
1384 par->Previous()->GetLayout()
1385 ).labeltype != LABEL_BIBLIO
1386 && layout.labeltype == LABEL_BIBLIO)) {
1387 for (int i = 0; i < 10; ++i) {
1388 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1390 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1391 if (!par->appendix && par->start_of_appendix){
1392 par->appendix = true;
1393 for (int i = 0; i < 10; ++i) {
1394 par->setCounter(i, 0);
1397 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1398 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1401 for (int i = 0; i < 10; ++i) {
1402 par->setCounter(i, 0);
1404 par->appendix = par->start_of_appendix;
1409 // if this is an open marginnote and this is the first
1410 // entry in the marginnote and the enclosing
1411 // environment is an enum/item then correct for the
1412 // LaTeX behaviour (ARRae)
1413 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1414 && par->footnotekind == LyXParagraph::MARGIN
1416 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1417 && (par->PreviousBeforeFootnote()
1418 && textclasslist.Style(parameters->textclass,
1419 par->PreviousBeforeFootnote()->GetLayout()
1420 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1421 // Any itemize or enumerate environment in a marginnote
1422 // that is embedded in an itemize or enumerate
1423 // paragraph is seen by LaTeX as being at a deeper
1424 // level within that enclosing itemization/enumeration
1425 // even if there is a "standard" layout at the start of
1431 /* Maybe we have to increment the enumeration depth.
1432 * BUT, enumeration in a footnote is considered in isolation from its
1433 * surrounding paragraph so don't increment if this is the
1434 * first line of the footnote
1435 * AND, bibliographies can't have their depth changed ie. they
1436 * are always of depth 0
1439 && par->Previous()->GetDepth() < par->GetDepth()
1440 && textclasslist.Style(parameters->textclass,
1441 par->Previous()->GetLayout()
1442 ).labeltype == LABEL_COUNTER_ENUMI
1443 && par->enumdepth < 3
1444 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1445 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1446 && par->footnotekind == LyXParagraph::FOOTNOTE)
1447 && layout.labeltype != LABEL_BIBLIO) {
1451 /* Maybe we have to decrement the enumeration depth, see note above */
1453 && par->Previous()->GetDepth() > par->GetDepth()
1454 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1455 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1456 && par->footnotekind == LyXParagraph::FOOTNOTE)
1457 && layout.labeltype != LABEL_BIBLIO) {
1458 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1459 par->setCounter(6 + par->enumdepth,
1460 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1461 /* reset the counters.
1462 * A depth change is like a breaking layout
1464 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1465 par->setCounter(i, 0);
1468 if (!par->labelstring.empty()) {
1469 par->labelstring.clear();
1472 if (layout.margintype == MARGIN_MANUAL) {
1473 if (par->labelwidthstring.empty()) {
1474 par->SetLabelWidthString(layout.labelstring());
1477 par->SetLabelWidthString(string());
1480 /* is it a layout that has an automatic label ? */
1481 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1483 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1484 if (i >= 0 && i<= parameters->secnumdepth) {
1485 par->incCounter(i); // increment the counter
1487 char * s = new char[50];
1489 // Is there a label? Useful for Chapter layout
1490 if (!par->appendix){
1491 if (!layout.labelstring().empty())
1492 par->labelstring = layout.labelstring();
1494 par->labelstring.clear();
1496 if (!layout.labelstring_appendix().empty())
1497 par->labelstring = layout.labelstring_appendix();
1499 par->labelstring.clear();
1502 if (!par->appendix){
1503 switch (2 * LABEL_FIRST_COUNTER -
1504 textclass.maxcounter() + i) {
1505 case LABEL_COUNTER_CHAPTER:
1507 par->getCounter(i));
1509 case LABEL_COUNTER_SECTION:
1511 par->getCounter(i - 1),
1512 par->getCounter(i));
1514 case LABEL_COUNTER_SUBSECTION:
1515 sprintf(s, "%d.%d.%d",
1516 par->getCounter(i-2),
1517 par->getCounter(i-1),
1518 par->getCounter(i));
1520 case LABEL_COUNTER_SUBSUBSECTION:
1521 sprintf(s, "%d.%d.%d.%d",
1522 par->getCounter(i-3),
1523 par->getCounter(i-2),
1524 par->getCounter(i-1),
1525 par->getCounter(i));
1527 case LABEL_COUNTER_PARAGRAPH:
1528 sprintf(s, "%d.%d.%d.%d.%d",
1529 par->getCounter(i-4),
1530 par->getCounter(i-3),
1531 par->getCounter(i-2),
1532 par->getCounter(i-1),
1533 par->getCounter(i));
1535 case LABEL_COUNTER_SUBPARAGRAPH:
1536 sprintf(s, "%d.%d.%d.%d.%d.%d",
1537 par->getCounter(i-5),
1538 par->getCounter(i-4),
1539 par->getCounter(i-3),
1540 par->getCounter(i-2),
1541 par->getCounter(i-1),
1542 par->getCounter(i));
1545 sprintf(s, "%d.", par->getCounter(i));
1549 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1550 case LABEL_COUNTER_CHAPTER:
1552 alphaCounter(par->getCounter(i)));
1554 case LABEL_COUNTER_SECTION:
1556 alphaCounter(par->getCounter(i - 1)),
1557 par->getCounter(i));
1559 case LABEL_COUNTER_SUBSECTION:
1560 sprintf(s, "%s.%d.%d",
1561 alphaCounter(par->getCounter(i-2)),
1562 par->getCounter(i-1),
1563 par->getCounter(i));
1565 case LABEL_COUNTER_SUBSUBSECTION:
1566 sprintf(s, "%s.%d.%d.%d",
1567 alphaCounter(par->getCounter(i-3)),
1568 par->getCounter(i-2),
1569 par->getCounter(i-1),
1570 par->getCounter(i));
1572 case LABEL_COUNTER_PARAGRAPH:
1573 sprintf(s, "%s.%d.%d.%d.%d",
1574 alphaCounter(par->getCounter(i-4)),
1575 par->getCounter(i-3),
1576 par->getCounter(i-2),
1577 par->getCounter(i-1),
1578 par->getCounter(i));
1580 case LABEL_COUNTER_SUBPARAGRAPH:
1581 sprintf(s, "%s.%d.%d.%d.%d.%d",
1582 alphaCounter(par->getCounter(i-5)),
1583 par->getCounter(i-4),
1584 par->getCounter(i-3),
1585 par->getCounter(i-2),
1586 par->getCounter(i-1),
1587 par->getCounter(i));
1590 sprintf(s, "%c.", par->getCounter(i));
1595 par->labelstring += s;
1598 for (i++; i < 10; ++i) {
1599 // reset the following counters
1600 par->setCounter(i, 0);
1602 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1603 for (i++; i < 10; ++i) {
1604 // reset the following counters
1605 par->setCounter(i, 0);
1607 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1608 par->incCounter(i + par->enumdepth);
1609 char * s = new char[25];
1610 int number = par->getCounter(i + par->enumdepth);
1612 static const char *roman[20] = {
1613 "i", "ii", "iii", "iv", "v",
1614 "vi", "vii", "viii", "ix", "x",
1615 "xi", "xii", "xiii", "xiv", "xv",
1616 "xvi", "xvii", "xviii", "xix", "xx"
1618 static const char hebrew[22] = {
1619 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1620 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1621 '÷', 'ø', 'ù', 'ú'
1624 switch (par->enumdepth) {
1626 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1627 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1629 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1632 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1633 sprintf(s, "%s.", roman[(number-1) % 20]);
1635 sprintf(s, ".%s", roman[(number-1) % 20]);
1638 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1639 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1641 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1644 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1645 sprintf(s, "%d.", number);
1647 sprintf(s, ".%d", number);
1650 par->labelstring = s;
1653 for (i += par->enumdepth + 1; i < 10; ++i)
1654 par->setCounter(i, 0); /* reset the following counters */
1657 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1658 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1660 int number = par->getCounter(i);
1662 par->bibkey = new InsetBibKey();
1663 par->bibkey->setCounter(number);
1664 par->labelstring = layout.labelstring();
1666 // In biblio should't be following counters but...
1668 string s = layout.labelstring();
1670 // the caption hack:
1672 if (layout.labeltype == LABEL_SENSITIVE) {
1673 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1674 && (par->footnotekind == LyXParagraph::FIG
1675 || par->footnotekind == LyXParagraph::WIDE_FIG))
1676 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1680 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1681 && (par->footnotekind == LyXParagraph::TAB
1682 || par->footnotekind == LyXParagraph::WIDE_TAB))
1683 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1687 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1688 && par->footnotekind == LyXParagraph::ALGORITHM)
1689 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1692 s = ":Ãúéøåâìà ";
1694 /* par->SetLayout(0);
1695 s = layout->labelstring; */
1696 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1699 s = " :úåòîùî øñç";
1703 par->labelstring = s;
1705 /* reset the enumeration counter. They are always resetted
1706 * when there is any other layout between */
1707 for (int i = 6 + par->enumdepth; i < 10; ++i)
1708 par->setCounter(i, 0);
1713 /* Updates all counters BEHIND the row. Changed paragraphs
1714 * with a dynamic left margin will be rebroken. */
1715 void LyXText::UpdateCounters(Row * row) const
1724 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1725 par = row->par->LastPhysicalPar()->Next();
1727 par = row->par->next;
1732 while (row->par != par)
1737 /* now check for the headline layouts. remember that they
1738 * have a dynamic left margin */
1740 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1741 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1744 /* Rebreak the paragraph */
1745 RemoveParagraph(row);
1746 AppendParagraph(row);
1748 /* think about the damned open footnotes! */
1749 while (par->Next() &&
1750 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1751 || par->Next()->IsDummy())){
1753 if (par->IsDummy()) {
1754 while (row->par != par)
1756 RemoveParagraph(row);
1757 AppendParagraph(row);
1762 par = par->LastPhysicalPar()->Next();
1768 /* insets an inset. */
1769 void LyXText::InsertInset(Inset *inset)
1771 SetUndo(Undo::INSERT,
1772 cursor.par->ParFromPos(cursor.pos)->previous,
1773 cursor.par->ParFromPos(cursor.pos)->next);
1774 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1775 cursor.par->InsertInset(cursor.pos, inset);
1776 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1777 * The character will not be inserted a
1782 // this is for the simple cut and paste mechanism
1783 static LyXParagraph * simple_cut_buffer = 0;
1784 static char simple_cut_buffer_textclass = 0;
1786 void DeleteSimpleCutBuffer()
1788 if (!simple_cut_buffer)
1790 LyXParagraph * tmppar;
1792 while (simple_cut_buffer) {
1793 tmppar = simple_cut_buffer;
1794 simple_cut_buffer = simple_cut_buffer->next;
1797 simple_cut_buffer = 0;
1801 void LyXText::copyEnvironmentType()
1803 copylayouttype = cursor.par->GetLayout();
1807 void LyXText::pasteEnvironmentType()
1809 SetLayout(copylayouttype);
1813 void LyXText::CutSelection(bool doclear)
1815 // This doesn't make sense, if there is no selection
1819 // OK, we have a selection. This is always between sel_start_cursor
1820 // and sel_end cursor
1821 LyXParagraph * tmppar;
1823 // Check whether there are half footnotes in the selection
1824 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1825 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1826 tmppar = sel_start_cursor.par;
1827 while (tmppar != sel_end_cursor.par){
1828 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1829 WriteAlert(_("Impossible operation"),
1830 _("Don't know what to do with half floats."),
1834 tmppar = tmppar->Next();
1838 /* table stuff -- begin */
1839 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1840 if ( sel_start_cursor.par != sel_end_cursor.par) {
1841 WriteAlert(_("Impossible operation"),
1842 _("Don't know what to do with half tables."),
1846 sel_start_cursor.par->table->Reinit();
1848 /* table stuff -- end */
1850 // make sure that the depth behind the selection are restored, too
1851 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1852 LyXParagraph * undoendpar = endpar;
1854 if (endpar && endpar->GetDepth()) {
1855 while (endpar && endpar->GetDepth()) {
1856 endpar = endpar->LastPhysicalPar()->Next();
1857 undoendpar = endpar;
1859 } else if (endpar) {
1860 endpar = endpar->Next(); // because of parindents etc.
1863 SetUndo(Undo::DELETE,
1865 .par->ParFromPos(sel_start_cursor.pos)->previous,
1868 // clear the simple_cut_buffer
1869 DeleteSimpleCutBuffer();
1871 // set the textclass
1872 simple_cut_buffer_textclass = parameters->textclass;
1874 #ifdef WITH_WARNINGS
1875 #warning Asger: Make cut more intelligent here.
1878 White paper for "intelligent" cutting:
1880 Example: "This is our text."
1881 Using " our " as selection, cutting will give "This istext.".
1882 Using "our" as selection, cutting will give "This is text.".
1883 Using " our" as selection, cutting will give "This is text.".
1884 Using "our " as selection, cutting will give "This is text.".
1886 All those four selections will (however) paste identically:
1887 Pasting with the cursor right after the "is" will give the
1888 original text with all four selections.
1890 The rationale is to be intelligent such that words are copied,
1891 cut and pasted in a functional manner.
1893 This is not implemented yet. (Asger)
1895 The changes below sees to do a lot of what you want. However
1896 I have not verified that all cases work as they should:
1898 - cut in multiple row
1900 - cut across footnotes and paragraph
1901 My simplistic tests show that the idea are basically sound but
1902 there are some items to fix up...we only need to find them
1905 As do redo Asger's example above (with | beeing the cursor in the
1906 result after cutting.):
1908 Example: "This is our text."
1909 Using " our " as selection, cutting will give "This is|text.".
1910 Using "our" as selection, cutting will give "This is | text.".
1911 Using " our" as selection, cutting will give "This is| text.".
1912 Using "our " as selection, cutting will give "This is |text.".
1917 #ifndef FIX_DOUBLE_SPACE
1918 bool space_wrapped =
1919 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1920 if (sel_end_cursor.pos > 0
1921 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1922 // please break before a space at the end
1923 sel_end_cursor.pos--;
1924 space_wrapped = true;
1926 // cut behind a space if there is one
1927 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1928 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1929 && (sel_start_cursor.par != sel_end_cursor.par
1930 || sel_start_cursor.pos < sel_end_cursor.pos))
1931 sel_start_cursor.pos++;
1933 // there are two cases: cut only within one paragraph or
1934 // more than one paragraph
1936 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1937 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1938 // only within one paragraph
1939 simple_cut_buffer = new LyXParagraph;
1940 LyXParagraph::size_type i =
1941 sel_start_cursor.pos;
1942 for (; i < sel_end_cursor.pos; ++i) {
1943 /* table stuff -- begin */
1944 if (sel_start_cursor.par->table
1945 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1946 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1947 sel_start_cursor.pos++;
1949 /* table stuff -- end */
1950 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1951 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1953 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1955 #ifndef FIX_DOUBLE_SPACE
1956 // check for double spaces
1957 if (sel_start_cursor.pos &&
1958 sel_start_cursor.par->Last() > sel_start_cursor.pos
1959 && sel_start_cursor.par
1960 ->IsLineSeparator(sel_start_cursor.pos - 1)
1961 && sel_start_cursor.par
1962 ->IsLineSeparator(sel_start_cursor.pos)) {
1963 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1966 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1969 endpar = sel_end_cursor.par->Next();
1971 // cut more than one paragraph
1974 ->BreakParagraphConservative(sel_end_cursor.pos);
1975 #ifndef FIX_DOUBLE_SPACE
1976 // insert a space at the end if there was one
1979 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1981 sel_end_cursor.par = sel_end_cursor.par->Next();
1982 sel_end_cursor.pos = 0;
1984 cursor = sel_end_cursor;
1986 #ifndef FIX_DOUBLE_SPACE
1987 // please break behind a space, if there is one.
1988 // The space should be copied too
1989 if (sel_start_cursor.par
1990 ->IsLineSeparator(sel_start_cursor.pos))
1991 sel_start_cursor.pos++;
1993 sel_start_cursor.par
1994 ->BreakParagraphConservative(sel_start_cursor.pos);
1995 #ifndef FIX_DOUBLE_SPACE
1996 if (!sel_start_cursor.pos
1997 || sel_start_cursor.par
1998 ->IsLineSeparator(sel_start_cursor.pos - 1)
1999 || sel_start_cursor.par
2000 ->IsNewline(sel_start_cursor.pos - 1)) {
2001 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2004 // store the endparagraph for redoing later
2005 endpar = sel_end_cursor.par->Next(); /* needed because
2010 // store the selection
2011 simple_cut_buffer = sel_start_cursor.par
2012 ->ParFromPos(sel_start_cursor.pos)->next;
2013 simple_cut_buffer->previous = 0;
2014 sel_end_cursor.par->previous->next = 0;
2016 // cut the selection
2017 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2018 = sel_end_cursor.par;
2020 sel_end_cursor.par->previous
2021 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2023 // care about footnotes
2024 if (simple_cut_buffer->footnoteflag) {
2025 LyXParagraph * tmppar = simple_cut_buffer;
2027 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2028 tmppar = tmppar->next;
2032 // the cut selection should begin with standard layout
2033 simple_cut_buffer->Clear();
2035 // paste the paragraphs again, if possible
2037 sel_start_cursor.par->Next()->ClearParagraph();
2038 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2040 !sel_start_cursor.par->Next()->Last())
2041 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2043 #ifndef FIX_DOUBLE_SPACE
2044 // maybe a forgotten blank
2045 if (sel_start_cursor.pos
2046 && sel_start_cursor.par
2047 ->IsLineSeparator(sel_start_cursor.pos)
2048 && sel_start_cursor.par
2049 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2050 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2055 // sometimes necessary
2057 sel_start_cursor.par->ClearParagraph();
2059 RedoParagraphs(sel_start_cursor, endpar);
2062 cursor = sel_start_cursor;
2063 SetCursor(cursor.par, cursor.pos);
2064 sel_cursor = cursor;
2065 UpdateCounters(cursor.row);
2069 void LyXText::CopySelection()
2071 // this doesnt make sense, if there is no selection
2075 // ok we have a selection. This is always between sel_start_cursor
2076 // and sel_end cursor
2077 LyXParagraph * tmppar;
2079 /* check wether there are half footnotes in the selection */
2080 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2081 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2082 tmppar = sel_start_cursor.par;
2083 while (tmppar != sel_end_cursor.par) {
2084 if (tmppar->footnoteflag !=
2085 sel_end_cursor.par->footnoteflag) {
2086 WriteAlert(_("Impossible operation"),
2087 _("Don't know what to do"
2088 " with half floats."),
2092 tmppar = tmppar->Next();
2096 /* table stuff -- begin */
2097 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2098 if ( sel_start_cursor.par != sel_end_cursor.par){
2099 WriteAlert(_("Impossible operation"),
2100 _("Don't know what to do with half tables."),
2105 /* table stuff -- end */
2107 // delete the simple_cut_buffer
2108 DeleteSimpleCutBuffer();
2110 // set the textclass
2111 simple_cut_buffer_textclass = parameters->textclass;
2113 #ifdef FIX_DOUBLE_SPACE
2114 // copy behind a space if there is one
2115 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2116 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2117 && (sel_start_cursor.par != sel_end_cursor.par
2118 || sel_start_cursor.pos < sel_end_cursor.pos))
2119 sel_start_cursor.pos++;
2121 // there are two cases: copy only within one paragraph
2122 // or more than one paragraph
2123 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2124 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2125 // only within one paragraph
2126 simple_cut_buffer = new LyXParagraph;
2127 LyXParagraph::size_type i = 0;
2128 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2129 sel_start_cursor.par->CopyIntoMinibuffer(i);
2130 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2133 // copy more than one paragraph
2134 // clone the paragraphs within the selection
2136 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2137 simple_cut_buffer = tmppar->Clone();
2138 LyXParagraph *tmppar2 = simple_cut_buffer;
2140 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2142 tmppar = tmppar->next;
2143 tmppar2->next = tmppar->Clone();
2144 tmppar2->next->previous = tmppar2;
2145 tmppar2 = tmppar2->next;
2149 // care about footnotes
2150 if (simple_cut_buffer->footnoteflag) {
2151 tmppar = simple_cut_buffer;
2153 tmppar->footnoteflag =
2154 LyXParagraph::NO_FOOTNOTE;
2155 tmppar = tmppar->next;
2159 // the simple_cut_buffer paragraph is too big
2160 LyXParagraph::size_type tmpi2 =
2161 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2162 for (; tmpi2; --tmpi2)
2163 simple_cut_buffer->Erase(0);
2165 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2167 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2168 while (tmppar2->size() > tmpi2) {
2169 tmppar2->Erase(tmppar2->text.size() - 1);
2175 void LyXText::PasteSelection()
2177 // this does not make sense, if there is nothing to paste
2178 if (!simple_cut_buffer)
2181 LyXParagraph * tmppar;
2182 LyXParagraph * endpar;
2184 LyXCursor tmpcursor;
2186 // be carefull with footnotes in footnotes
2187 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2189 // check whether the cut_buffer includes a footnote
2190 tmppar = simple_cut_buffer;
2192 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2193 tmppar = tmppar->next;
2196 WriteAlert(_("Impossible operation"),
2197 _("Can't paste float into float!"),
2203 /* table stuff -- begin */
2204 if (cursor.par->table) {
2205 if (simple_cut_buffer->next) {
2206 WriteAlert(_("Impossible operation"),
2207 _("Table cell cannot include more than one paragraph!"),
2212 /* table stuff -- end */
2214 SetUndo(Undo::INSERT,
2215 cursor.par->ParFromPos(cursor.pos)->previous,
2216 cursor.par->ParFromPos(cursor.pos)->next);
2220 // There are two cases: cutbuffer only one paragraph or many
2221 if (!simple_cut_buffer->next) {
2222 // only within a paragraph
2224 #ifndef FIX_DOUBLE_SPACE
2225 // please break behind a space, if there is one
2226 while (tmpcursor.par->Last() > tmpcursor.pos
2227 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2230 tmppar = simple_cut_buffer->Clone();
2231 /* table stuff -- begin */
2232 bool table_too_small = false;
2233 if (tmpcursor.par->table) {
2234 while (simple_cut_buffer->text.size()
2235 && !table_too_small) {
2236 if (simple_cut_buffer->IsNewline(0)){
2237 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2239 simple_cut_buffer->Erase(0);
2240 if (tmpcursor.pos < tmpcursor.par->Last())
2243 table_too_small = true;
2245 #ifdef FIX_DOUBLE_SPACE
2246 // This is an attempt to fix the
2247 // "never insert a space at the
2248 // beginning of a paragraph" problem.
2249 if (tmpcursor.pos == 0
2250 && simple_cut_buffer->IsLineSeparator(0)) {
2251 simple_cut_buffer->Erase(0);
2253 simple_cut_buffer->CutIntoMinibuffer(0);
2254 simple_cut_buffer->Erase(0);
2255 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2259 simple_cut_buffer->CutIntoMinibuffer(0);
2260 simple_cut_buffer->Erase(0);
2261 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2267 /* table stuff -- end */
2268 // Some provisions should be done here for checking
2269 // if we are inserting at the beginning of a
2270 // paragraph. If there are a space at the beginning
2271 // of the text to insert and we are inserting at
2272 // the beginning of the paragraph the space should
2274 while (simple_cut_buffer->text.size()) {
2275 #ifdef FIX_DOUBLE_SPACE
2276 // This is an attempt to fix the
2277 // "never insert a space at the
2278 // beginning of a paragraph" problem.
2279 if (tmpcursor.pos == 0
2280 && simple_cut_buffer->IsLineSeparator(0)) {
2281 simple_cut_buffer->Erase(0);
2283 simple_cut_buffer->CutIntoMinibuffer(0);
2284 simple_cut_buffer->Erase(0);
2285 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2289 simple_cut_buffer->CutIntoMinibuffer(0);
2290 simple_cut_buffer->Erase(0);
2291 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2296 delete simple_cut_buffer;
2297 simple_cut_buffer = tmppar;
2298 endpar = tmpcursor.par->Next();
2302 // make a copy of the simple cut_buffer
2303 tmppar = simple_cut_buffer;
2304 LyXParagraph * simple_cut_clone = tmppar->Clone();
2305 LyXParagraph * tmppar2 = simple_cut_clone;
2306 if (cursor.par->footnoteflag){
2307 tmppar->footnoteflag = cursor.par->footnoteflag;
2308 tmppar->footnotekind = cursor.par->footnotekind;
2310 while (tmppar->next) {
2311 tmppar = tmppar->next;
2312 tmppar2->next = tmppar->Clone();
2313 tmppar2->next->previous = tmppar2;
2314 tmppar2 = tmppar2->next;
2315 if (cursor.par->footnoteflag){
2316 tmppar->footnoteflag = cursor.par->footnoteflag;
2317 tmppar->footnotekind = cursor.par->footnotekind;
2321 // make sure there is no class difference
2322 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2323 parameters->textclass,
2326 // make the simple_cut_buffer exactly the same layout than
2327 // the cursor paragraph
2328 simple_cut_buffer->MakeSameLayout(cursor.par);
2330 // find the end of the buffer
2331 LyXParagraph * lastbuffer = simple_cut_buffer;
2332 while (lastbuffer->Next())
2333 lastbuffer = lastbuffer->Next();
2335 #ifndef FIX_DOUBLE_SPACE
2336 // Please break behind a space, if there is one. The space
2337 // should be copied too.
2338 if (cursor.par->Last() > cursor.pos
2339 && cursor.par->IsLineSeparator(cursor.pos))
2342 bool paste_the_end = false;
2344 // open the paragraph for inserting the simple_cut_buffer
2346 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2347 cursor.par->BreakParagraphConservative(cursor.pos);
2348 paste_the_end = true;
2351 #ifndef FIX_DOUBLE_SPACE
2352 // be careful with double spaces
2353 if ((!cursor.par->Last()
2354 || cursor.par->IsLineSeparator(cursor.pos - 1)
2355 || cursor.par->IsNewline(cursor.pos - 1))
2356 && simple_cut_buffer->text.size()
2357 && simple_cut_buffer->IsLineSeparator(0))
2358 simple_cut_buffer->Erase(0);
2360 // set the end for redoing later
2361 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2364 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2365 cursor.par->ParFromPos(cursor.pos)->next;
2366 cursor.par->ParFromPos(cursor.pos)->next->previous =
2367 lastbuffer->ParFromPos(lastbuffer->Last());
2369 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2370 simple_cut_buffer->previous =
2371 cursor.par->ParFromPos(cursor.pos);
2373 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2374 lastbuffer = cursor.par;
2376 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2378 // store the new cursor position
2379 tmpcursor.par = lastbuffer;
2380 tmpcursor.pos = lastbuffer->Last();
2382 // maybe some pasting
2383 if (lastbuffer->Next() && paste_the_end) {
2384 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2385 #ifndef FIX_DOUBLE_SPACE
2386 // be careful with double spaces
2387 if ((!lastbuffer->Last()
2388 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2389 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2390 && lastbuffer->Next()->Last()
2391 && lastbuffer->Next()->IsLineSeparator(0))
2392 lastbuffer->Next()->Erase(0);
2394 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2396 } else if (!lastbuffer->Next()->Last()) {
2397 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2398 #ifndef FIX_DOUBLE_SPACE
2399 // be careful witth double spaces
2400 if ((!lastbuffer->Last()
2401 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2402 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2403 && lastbuffer->Next()->Last()
2404 && lastbuffer->Next()->IsLineSeparator(0))
2405 lastbuffer->Next()->Erase(0);
2407 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2409 } else if (!lastbuffer->Last()) {
2410 lastbuffer->MakeSameLayout(lastbuffer->next);
2411 #ifndef FIX_DOUBLE_SPACE
2412 // be careful witth double spaces
2413 if ((!lastbuffer->Last()
2414 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2415 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2416 && lastbuffer->Next()->Last()
2417 && lastbuffer->Next()->IsLineSeparator(0))
2418 lastbuffer->Next()->Erase(0);
2420 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2423 lastbuffer->Next()->ClearParagraph();
2426 // restore the simple cut buffer
2427 simple_cut_buffer = simple_cut_clone;
2430 RedoParagraphs(cursor, endpar);
2432 SetCursor(cursor.par, cursor.pos);
2435 sel_cursor = cursor;
2436 SetCursor(tmpcursor.par, tmpcursor.pos);
2438 UpdateCounters(cursor.row);
2442 // returns a pointer to the very first LyXParagraph
2443 LyXParagraph * LyXText::FirstParagraph() const
2445 return params->paragraph;
2449 // returns true if the specified string is at the specified position
2450 bool LyXText::IsStringInText(LyXParagraph * par,
2451 LyXParagraph::size_type pos,
2452 char const * str) const
2456 while (pos + i < par->Last() && str[i] &&
2457 str[i] == par->GetChar(pos + i)) {
2467 // sets the selection over the number of characters of string, no check!!
2468 void LyXText::SetSelectionOverString(char const * string)
2470 sel_cursor = cursor;
2471 for (int i = 0; string[i]; ++i)
2477 // simple replacing. The font of the first selected character is used
2478 void LyXText::ReplaceSelectionWithString(char const * str)
2483 if (!selection) { // create a dummy selection
2484 sel_end_cursor = cursor;
2485 sel_start_cursor = cursor;
2488 // Get font setting before we cut
2489 LyXParagraph::size_type pos = sel_end_cursor.pos;
2490 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2492 // Insert the new string
2493 for (int i = 0; str[i]; ++i) {
2494 sel_end_cursor.par->InsertChar(pos, str[i]);
2495 sel_end_cursor.par->SetFont(pos, font);
2499 // Cut the selection
2506 // if the string can be found: return true and set the cursor to
2508 bool LyXText::SearchForward(char const * str) const
2510 LyXParagraph * par = cursor.par;
2511 LyXParagraph::size_type pos = cursor.pos;
2512 while (par && !IsStringInText(par, pos, str)) {
2513 if (pos < par->Last() - 1)
2521 SetCursor(par, pos);
2529 bool LyXText::SearchBackward(char const * string) const
2531 LyXParagraph * par = cursor.par;
2532 int pos = cursor.pos;
2538 // We skip empty paragraphs (Asger)
2540 par = par->Previous();
2542 pos = par->Last() - 1;
2543 } while (par && pos < 0);
2545 } while (par && !IsStringInText(par, pos, string));
2548 SetCursor(par, pos);
2555 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2557 char * str = new char[text.size() + 1];
2558 copy(text.begin(), text.end(), str);
2559 str[text.size()] = '\0';
2565 // needed to insert the selection
2566 void LyXText::InsertStringA(char const * s)
2569 LyXParagraph * par = cursor.par;
2570 LyXParagraph::size_type pos = cursor.pos;
2571 LyXParagraph::size_type a = 0;
2573 LyXParagraph * endpar = cursor.par->Next();
2578 textclasslist.Style(parameters->textclass,
2579 cursor.par->GetLayout()).isEnvironment();
2580 // only to be sure, should not be neccessary
2583 // insert the string, don't insert doublespace
2584 string::size_type i = 0;
2585 while (i < str.length()) {
2586 if (str[i] != '\n') {
2588 && i + 1 < str.length() && str[i + 1] != ' '
2589 && pos && par->GetChar(pos - 1)!= ' ') {
2590 par->InsertChar(pos,' ');
2592 } else if (par->table) {
2593 if (str[i] == '\t') {
2594 while((pos < par->size()) &&
2595 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2597 if (pos < par->size())
2599 else // no more fields to fill skip the rest
2601 } else if ((str[i] != 13) &&
2602 ((str[i] & 127) >= ' ')) {
2603 par->InsertChar(pos, str[i]);
2606 } else if (str[i] == ' ') {
2608 InsetSpecialChar * new_inset =
2609 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2610 par->InsertInset(pos, new_inset);
2612 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2615 } else if (str[i] == '\t') {
2616 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2618 InsetSpecialChar * new_inset =
2619 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2620 par->InsertInset(pos, new_inset);
2622 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2626 } else if (str[i]!= 13 &&
2627 // Ignore unprintables
2628 (str[i] & 127) >= ' ') {
2629 par->InsertChar(pos, str[i]);
2634 if (i + 1 >= str.length()) {
2638 while((pos < par->size()) &&
2639 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2642 cell = NumberOfCell(par, pos);
2643 while((pos < par->size()) &&
2644 !(par->table->IsFirstCell(cell))) {
2646 while((pos < par->size()) &&
2647 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2650 cell = NumberOfCell(par, pos);
2652 if (pos >= par->size())
2653 // no more fields to fill skip the rest
2656 if (!par->text.size()) {
2658 InsetSpecialChar * new_inset =
2659 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2660 par->InsertInset(pos, new_inset);
2662 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2666 par->BreakParagraph(pos, flag);
2674 RedoParagraphs(cursor, endpar);
2675 SetCursor(cursor.par, cursor.pos);
2676 sel_cursor = cursor;
2677 SetCursor(par, pos);
2682 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2684 char * str = new char[text.size() + 1];
2685 copy(text.begin(), text.end(), str);
2686 str[text.size()] = '\0';
2692 /* turns double-CR to single CR, others where converted into one blank and 13s
2693 * that are ignored .Double spaces are also converted into one. Spaces at
2694 * the beginning of a paragraph are forbidden. tabs are converted into one
2695 * space. then InsertStringA is called */
2696 void LyXText::InsertStringB(char const * s)
2699 LyXParagraph * par = cursor.par;
2700 string::size_type i = 1;
2701 while (i < str.length()) {
2702 if (str[i] == '\t' && !par->table)
2704 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2706 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2707 if (str[i + 1] != '\n') {
2708 if (str[i - 1] != ' ')
2713 while (i + 1 < str.length()
2714 && (str[i + 1] == ' '
2715 || str[i + 1] == '\t'
2716 || str[i + 1] == '\n'
2717 || str[i + 1] == 13)) {
2724 InsertStringA(str.c_str());
2728 bool LyXText::GotoNextError() const
2730 LyXCursor res = cursor;
2732 if (res.pos < res.par->Last() - 1) {
2736 res.par = res.par->Next();
2741 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2742 && res.par->GetInset(res.pos)->AutoDelete()));
2745 SetCursor(res.par, res.pos);
2752 bool LyXText::GotoNextNote() const
2754 LyXCursor res = cursor;
2756 if (res.pos < res.par->Last() - 1) {
2759 res.par = res.par->Next();
2764 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2765 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2768 SetCursor(res.par, res.pos);
2775 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2776 LyXTextClassList::size_type class2,
2780 if (!par || class1 == class2)
2782 par = par->FirstPhysicalPar();
2784 string name = textclasslist.NameOfLayout(class1, par->layout);
2786 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2787 textclasslist.NumberOfLayout(class2, name);
2790 } else { // layout not found
2791 // use default layout "Standard" (0)
2796 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2798 string s = "Layout had to be changed from\n"
2799 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2800 + "\nbecause of class conversion from\n"
2801 + textclasslist.NameOfClass(class1) + " to "
2802 + textclasslist.NameOfClass(class2);
2803 InsetError * new_inset = new InsetError(s);
2804 par->InsertChar(0, LyXParagraph::META_INSET);
2805 par->InsertInset(0, new_inset);
2814 void LyXText::CheckParagraph(LyXParagraph * par,
2815 LyXParagraph::size_type pos)
2818 LyXCursor tmpcursor;
2820 /* table stuff -- begin*/
2823 CheckParagraphInTable(par, pos);
2826 /* table stuff -- end*/
2829 LyXParagraph::size_type z;
2830 Row * row = GetRow(par, pos, y);
2832 // is there a break one row above
2833 if (row->previous && row->previous->par == row->par) {
2834 z = NextBreakPoint(row->previous, paperwidth);
2835 if ( z >= row->pos) {
2836 // set the dimensions of the row above
2837 y -= row->previous->height;
2839 refresh_row = row->previous;
2840 status = LyXText::NEED_MORE_REFRESH;
2842 BreakAgain(row->previous);
2844 // set the cursor again. Otherwise
2845 // dangling pointers are possible
2846 SetCursor(cursor.par, cursor.pos);
2847 sel_cursor = cursor;
2852 int tmpheight = row->height;
2853 LyXParagraph::size_type tmplast = RowLast(row);
2858 if (row->height == tmpheight && RowLast(row) == tmplast)
2859 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2861 status = LyXText::NEED_MORE_REFRESH;
2863 // check the special right address boxes
2864 if (textclasslist.Style(parameters->textclass,
2865 par->GetLayout()).margintype
2866 == MARGIN_RIGHT_ADDRESS_BOX) {
2867 tmpcursor.par = par;
2868 tmpcursor.row = row;
2871 tmpcursor.x_fix = 0;
2872 tmpcursor.pos = pos;
2873 RedoDrawingOfParagraph(tmpcursor);
2878 // set the cursor again. Otherwise dangling pointers are possible
2879 // also set the selection
2883 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2884 sel_cursor = cursor;
2885 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2886 sel_start_cursor = cursor;
2887 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2888 sel_end_cursor = cursor;
2889 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2890 last_sel_cursor = cursor;
2893 SetCursorIntern(cursor.par, cursor.pos);
2897 // returns 0 if inset wasn't found
2898 int LyXText::UpdateInset(Inset * inset)
2900 // first check the current paragraph
2901 int pos = cursor.par->GetPositionOfInset(inset);
2903 CheckParagraph(cursor.par, pos);
2907 // check every paragraph
2909 LyXParagraph * par = FirstParagraph();
2911 // make sure the paragraph is open
2912 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2913 pos = par->GetPositionOfInset(inset);
2915 CheckParagraph(par, pos);
2926 void LyXText::SetCursor(LyXParagraph * par,
2927 LyXParagraph::size_type pos, bool setfont) const
2929 LyXCursor old_cursor = cursor;
2930 SetCursorIntern(par, pos, setfont);
2931 DeleteEmptyParagraphMechanism(old_cursor);
2935 void LyXText::SetCursorIntern(LyXParagraph * par,
2936 LyXParagraph::size_type pos, bool setfont) const
2940 LyXParagraph * tmppar;
2941 LyXParagraph::size_type vpos,cursor_vpos;
2943 // correct the cursor position if impossible
2944 if (pos > par->Last()){
2945 tmppar = par->ParFromPos(pos);
2946 pos = par->PositionInParFromPos(pos);
2949 if (par->IsDummy() && par->previous &&
2950 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2951 while (par->previous &&
2952 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2953 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2954 par = par->previous ;
2955 if (par->IsDummy() &&
2956 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2957 pos += par->text.size() + 1;
2959 if (par->previous) {
2960 par = par->previous;
2962 pos += par->text.size() + 1;
2970 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2971 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2972 && !cursor.par->IsSeparator(cursor.pos))
2973 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2975 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2976 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2978 current_font = cursor.par->GetFontSettings(cursor.pos);
2979 real_current_font = GetFont(cursor.par, cursor.pos);
2980 if (pos == 0 && par->size() == 0
2981 && owner_->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2982 current_font.setDirection(LyXFont::RTL_DIR);
2983 real_current_font.setDirection(LyXFont::RTL_DIR);
2987 /* get the cursor y position in text */
2988 row = GetRow(par, pos, y);
2989 /* y is now the beginning of the cursor row */
2991 /* y is now the cursor baseline */
2994 /* now get the cursors x position */
2996 float fill_separator, fill_hfill, fill_label_hfill;
2997 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
2999 LyXParagraph::size_type last = RowLast(row);
3000 if (row->pos > last)
3002 else if (pos <= last ) {
3003 LyXDirection letter_direction =
3004 row->par->getLetterDirection(pos);
3005 LyXDirection font_direction =
3006 real_current_font.getFontDirection();
3007 if (letter_direction == font_direction || pos == 0)
3008 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3009 ? log2vis(pos) : log2vis(pos)+1;
3011 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3012 ? log2vis(pos-1)+1 : log2vis(pos-1);
3014 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3015 ? log2vis(last)+1 : log2vis(last);
3017 /* table stuff -- begin*/
3018 if (row->par->table) {
3019 int cell = NumberOfCell(row->par, row->pos);
3021 x += row->par->table->GetBeginningOfTextInCell(cell);
3022 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3023 pos = vis2log(vpos);
3024 if (row->par->IsNewline(pos)) {
3025 x = x_old + row->par->table->WidthOfColumn(cell);
3028 x += row->par->table->GetBeginningOfTextInCell(cell);
3030 x += SingleWidth(row->par, pos);
3034 /* table stuff -- end*/
3035 LyXParagraph::size_type main_body =
3036 BeginningOfMainBody(row->par);
3037 if (main_body > 0 &&
3038 (main_body-1 > last ||
3039 !row->par->IsLineSeparator(main_body-1)))
3042 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3043 pos = vis2log(vpos);
3044 if (main_body > 0 && pos == main_body-1) {
3045 x += fill_label_hfill +
3046 GetFont(row->par, -2).stringWidth(
3047 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3048 if (row->par->IsLineSeparator(main_body-1))
3049 x -= SingleWidth(row->par, main_body-1);
3052 x += SingleWidth(row->par, pos);
3053 if (HfillExpansion(row, pos)) {
3054 if (pos >= main_body)
3057 x += fill_label_hfill;
3059 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3067 cursor.x_fix = cursor.x;
3072 void LyXText::SetCursorFromCoordinates(int x, long y) const
3074 LyXCursor old_cursor = cursor;
3076 /* get the row first */
3078 Row * row = GetRowNearY(y);
3080 cursor.par = row->par;
3082 int column = GetColumnNearX(row, x);
3083 cursor.pos = row->pos + column;
3085 cursor.y = y + row->baseline;
3090 (cursor.pos == cursor.par->Last()
3091 || cursor.par->IsSeparator(cursor.pos)
3092 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3093 && !cursor.par->IsSeparator(cursor.pos))
3094 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3096 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3097 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3099 current_font = cursor.par->GetFontSettings(cursor.pos);
3100 real_current_font = GetFont(cursor.par, cursor.pos);
3102 DeleteEmptyParagraphMechanism(old_cursor);
3106 void LyXText::CursorLeft() const
3109 if (cursor.par->table) {
3110 int cell = NumberOfCell(cursor.par, cursor.pos);
3111 if (cursor.par->table->IsContRow(cell) &&
3112 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3119 void LyXText::CursorLeftIntern() const
3121 if (cursor.pos > 0) {
3122 SetCursor(cursor.par, cursor.pos - 1);
3124 else if (cursor.par->Previous()) {
3125 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3130 void LyXText::CursorRight() const
3132 CursorRightIntern();
3133 if (cursor.par->table) {
3134 int cell = NumberOfCell(cursor.par, cursor.pos);
3135 if (cursor.par->table->IsContRow(cell) &&
3136 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3143 void LyXText::CursorRightIntern() const
3145 if (cursor.pos < cursor.par->Last()) {
3146 SetCursor(cursor.par, cursor.pos + 1);
3148 else if (cursor.par->Next()) {
3149 SetCursor(cursor.par->Next(), 0);
3154 void LyXText::CursorUp() const
3156 SetCursorFromCoordinates(cursor.x_fix,
3157 cursor.y - cursor.row->baseline - 1);
3158 if (cursor.par->table) {
3159 int cell = NumberOfCell(cursor.par, cursor.pos);
3160 if (cursor.par->table->IsContRow(cell) &&
3161 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3168 void LyXText::CursorDown() const
3170 if (cursor.par->table &&
3171 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3174 SetCursorFromCoordinates(cursor.x_fix,
3175 cursor.y - cursor.row->baseline
3176 + cursor.row->height + 1);
3177 if (cursor.par->table) {
3178 int cell = NumberOfCell(cursor.par, cursor.pos);
3179 int cell_above = cursor.par->table->GetCellAbove(cell);
3180 while(cursor.par->table &&
3181 cursor.par->table->IsContRow(cell) &&
3182 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3183 SetCursorFromCoordinates(cursor.x_fix,
3184 cursor.y - cursor.row->baseline
3185 + cursor.row->height + 1);
3186 if (cursor.par->table) {
3187 cell = NumberOfCell(cursor.par, cursor.pos);
3188 cell_above = cursor.par->table->GetCellAbove(cell);
3195 void LyXText::CursorUpParagraph() const
3197 if (cursor.pos > 0) {
3198 SetCursor(cursor.par, 0);
3200 else if (cursor.par->Previous()) {
3201 SetCursor(cursor.par->Previous(), 0);
3206 void LyXText::CursorDownParagraph() const
3208 if (cursor.par->Next()) {
3209 SetCursor(cursor.par->Next(), 0);
3211 SetCursor(cursor.par, cursor.par->Last());
3217 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3219 bool deleted = false;
3221 // this is the delete-empty-paragraph-mechanism.
3222 if (selection) return;
3224 #ifdef FIX_DOUBLE_SPACE
3225 /* Ok I'll put some comments here about what is missing.
3226 I have fixed BackSpace (and thus Delete) to not delete
3227 double-spaces automagically. I have also changed Cut,
3228 Copy and Paste to hopefully do some sensible things.
3229 There are still some small problems that can lead to
3230 double spaces stored in the document file or space at
3231 the beginning of paragraphs. This happens if you have
3232 the cursor betwenn to spaces and then save. Or if you
3233 cut and paste and the selection have a space at the
3234 beginning and then save right after the paste. I am
3235 sure none of these are very hard to fix, but I will
3236 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3237 that I can get some feedback. (Lgb)
3240 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3241 // delete the LineSeparator.
3244 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3245 // delete the LineSeparator.
3248 // If the pos around the old_cursor were spaces, delete one of them.
3249 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3250 && old_cursor.pos > 0
3251 && old_cursor.pos < old_cursor.par->Last()
3252 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3253 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3254 old_cursor.par->Erase(old_cursor.pos - 1);
3255 RedoParagraphs(old_cursor, old_cursor.par->Next());
3256 // or RedoDrawingOfParagraph(old_cursor);
3258 if (old_cursor.par == cursor.par &&
3259 cursor.pos > old_cursor.pos)
3260 SetCursor(cursor.par, cursor.pos - 1);
3262 SetCursor(cursor.par, cursor.pos);
3267 // Paragraph should not be deleted if empty
3268 if ((textclasslist.Style(parameters->textclass,
3269 old_cursor.par->GetLayout())).keepempty)
3272 LyXCursor tmpcursor;
3274 if (old_cursor.par != cursor.par) {
3275 if ( (old_cursor.par->Last() == 0
3276 || (old_cursor.par->Last() == 1
3277 && (old_cursor.par->IsLineSeparator(0))))
3278 && old_cursor.par->FirstPhysicalPar()
3279 == old_cursor.par->LastPhysicalPar()) {
3281 // ok, we will delete anything
3283 // make sure that you do not delete any environments
3284 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3285 !(old_cursor.row->previous
3286 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3287 && !(old_cursor.row->next
3288 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3290 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3291 ((old_cursor.row->previous
3292 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3294 (old_cursor.row->next
3295 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3297 status = LyXText::NEED_MORE_REFRESH;
3300 if (old_cursor.row->previous) {
3301 refresh_row = old_cursor.row->previous;
3302 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3304 cursor = old_cursor; // that undo can restore the right cursor position
3305 LyXParagraph * endpar = old_cursor.par->next;
3306 if (endpar && endpar->GetDepth()) {
3307 while (endpar && endpar->GetDepth()) {
3308 endpar = endpar->LastPhysicalPar()->Next();
3311 SetUndo(Undo::DELETE,
3312 old_cursor.par->previous,
3317 RemoveRow(old_cursor.row);
3318 if (params->paragraph == old_cursor.par) {
3319 params->paragraph = params->paragraph->next;
3322 delete old_cursor.par;
3324 /* Breakagain the next par. Needed
3325 * because of the parindent that
3326 * can occur or dissappear. The
3327 * next row can change its height,
3328 * if there is another layout before */
3329 if (refresh_row->next) {
3330 BreakAgain(refresh_row->next);
3331 UpdateCounters(refresh_row);
3333 SetHeightOfRow(refresh_row);
3335 refresh_row = old_cursor.row->next;
3336 refresh_y = old_cursor.y - old_cursor.row->baseline;
3339 cursor = old_cursor; // that undo can restore the right cursor position
3340 LyXParagraph *endpar = old_cursor.par->next;
3341 if (endpar && endpar->GetDepth()) {
3342 while (endpar && endpar->GetDepth()) {
3343 endpar = endpar->LastPhysicalPar()->Next();
3346 SetUndo(Undo::DELETE,
3347 old_cursor.par->previous,
3352 RemoveRow(old_cursor.row);
3354 if (params->paragraph == old_cursor.par) {
3355 params->paragraph = params->paragraph->next;
3357 delete old_cursor.par;
3359 /* Breakagain the next par. Needed
3360 because of the parindent that can
3361 occur or dissappear.
3362 The next row can change its height,
3363 if there is another layout before
3366 BreakAgain(refresh_row);
3367 UpdateCounters(refresh_row->previous);
3372 SetCursor(cursor.par, cursor.pos);
3374 /* if (cursor.y > old_cursor.y)
3375 cursor.y -= old_cursor.row->height; */
3377 if (sel_cursor.par == old_cursor.par
3378 && sel_cursor.pos == sel_cursor.pos) {
3379 // correct selection
3380 sel_cursor = cursor;
3385 if (old_cursor.par->ClearParagraph()){
3386 RedoParagraphs(old_cursor, old_cursor.par->Next());
3388 SetCursor(cursor.par, cursor.pos);
3389 sel_cursor = cursor;
3396 LyXParagraph * LyXText::GetParFromID(int id)
3398 LyXParagraph * result = FirstParagraph();
3399 while (result && result->id() != id)
3400 result = result->next;
3406 bool LyXText::TextUndo()
3408 // returns false if no undo possible
3409 Undo * undo = params->undostack.pop();
3414 .push(CreateUndo(undo->kind,
3415 GetParFromID(undo->number_of_before_par),
3416 GetParFromID(undo->number_of_behind_par)));
3418 return TextHandleUndo(undo);
3422 bool LyXText::TextRedo()
3424 // returns false if no redo possible
3425 Undo * undo = params->redostack.pop();
3430 .push(CreateUndo(undo->kind,
3431 GetParFromID(undo->number_of_before_par),
3432 GetParFromID(undo->number_of_behind_par)));
3434 return TextHandleUndo(undo);
3438 bool LyXText::TextHandleUndo(Undo * undo)
3440 // returns false if no undo possible
3441 bool result = false;
3443 LyXParagraph * before =
3444 GetParFromID(undo->number_of_before_par);
3445 LyXParagraph * behind =
3446 GetParFromID(undo->number_of_behind_par);
3447 LyXParagraph * tmppar;
3448 LyXParagraph * tmppar2;
3449 LyXParagraph * tmppar3;
3450 LyXParagraph * tmppar4;
3451 LyXParagraph * endpar;
3452 LyXParagraph * tmppar5;
3454 // if there's no before take the beginning
3455 // of the document for redoing
3457 SetCursorIntern(FirstParagraph(), 0);
3459 // replace the paragraphs with the undo informations
3461 tmppar3 = undo->par;
3462 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3465 while (tmppar4->next)
3466 tmppar4 = tmppar4->next;
3467 } // get last undo par
3469 // now remove the old text if there is any
3470 if (before != behind || (!behind && !before)){
3472 tmppar5 = before->next;
3474 tmppar5 = params->paragraph;
3476 while (tmppar5 && tmppar5 != behind){
3478 tmppar5 = tmppar5->next;
3479 // a memory optimization for edit: Only layout information
3480 // is stored in the undo. So restore the text informations.
3481 if (undo->kind == Undo::EDIT){
3482 tmppar2->text = tmppar->text;
3483 tmppar->text.clear();
3484 tmppar2 = tmppar2->next;
3486 if ( currentrow && currentrow->par == tmppar )
3487 currentrow = currentrow -> previous;
3488 // Commenting out this might remove the error
3489 // reported by Purify, but it might also
3490 // introduce a memory leak. We need to
3496 // put the new stuff in the list if there is one
3499 before->next = tmppar3;
3501 params->paragraph = tmppar3;
3502 tmppar3->previous = before;
3506 params->paragraph = behind;
3509 tmppar4->next = behind;
3511 behind->previous = tmppar4;
3515 // Set the cursor for redoing
3517 SetCursorIntern(before->FirstSelfrowPar(), 0);
3518 // check wether before points to a closed float and open it if necessary
3519 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3520 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3522 while (tmppar4->previous &&
3523 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3524 tmppar4 = tmppar4->previous;
3525 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3526 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3527 tmppar4 = tmppar4->next;
3532 // open a cosed footnote at the end if necessary
3533 if (behind && behind->previous &&
3534 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3535 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3536 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3537 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3538 behind = behind->next;
3542 // calculate the endpar for redoing the paragraphs.
3544 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3545 endpar = behind->LastPhysicalPar()->Next();
3547 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3552 tmppar = GetParFromID(undo->number_of_cursor_par);
3553 RedoParagraphs(cursor, endpar);
3555 SetCursorIntern(tmppar, undo->cursor_pos);
3556 UpdateCounters(cursor.row);
3566 void LyXText::FinishUndo()
3568 // makes sure the next operation will be stored
3569 undo_finished = True;
3573 void LyXText::FreezeUndo()
3575 // this is dangerous and for internal use only
3580 void LyXText::UnFreezeUndo()
3582 // this is dangerous and for internal use only
3583 undo_frozen = false;
3587 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3588 LyXParagraph const * behind) const
3591 params->undostack.push(CreateUndo(kind, before, behind));
3592 params->redostack.clear();
3596 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3597 LyXParagraph const * behind)
3599 params->redostack.push(CreateUndo(kind, before, behind));
3603 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3604 LyXParagraph const * behind) const
3606 int before_number = -1;
3607 int behind_number = -1;
3609 before_number = before->id();
3611 behind_number = behind->id();
3612 // Undo::EDIT and Undo::FINISH are
3613 // always finished. (no overlapping there)
3614 // overlapping only with insert and delete inside one paragraph:
3615 // Nobody wants all removed character
3616 // appear one by one when undoing.
3617 // EDIT is special since only layout information, not the
3618 // contents of a paragaph are stored.
3619 if (!undo_finished && kind != Undo::EDIT &&
3620 kind != Undo::FINISH){
3621 // check wether storing is needed
3622 if (!params->undostack.empty() &&
3623 params->undostack.top()->kind == kind &&
3624 params->undostack.top()->number_of_before_par == before_number &&
3625 params->undostack.top()->number_of_behind_par == behind_number ){
3630 // create a new Undo
3631 LyXParagraph * undopar;
3632 LyXParagraph * tmppar;
3633 LyXParagraph * tmppar2;
3635 LyXParagraph * start = 0;
3636 LyXParagraph * end = 0;
3639 start = before->next;
3641 start = FirstParagraph();
3643 end = behind->previous;
3645 end = FirstParagraph();
3651 && start != end->next
3652 && (before != behind || (!before && !behind))) {
3654 tmppar2 = tmppar->Clone();
3655 tmppar2->id(tmppar->id());
3657 // a memory optimization: Just store the layout information
3659 if (kind == Undo::EDIT){
3660 tmppar2->text.clear();
3665 while (tmppar != end && tmppar->next) {
3666 tmppar = tmppar->next;
3667 tmppar2->next = tmppar->Clone();
3668 tmppar2->next->id(tmppar->id());
3669 // a memory optimization: Just store the layout
3670 // information when only edit
3671 if (kind == Undo::EDIT){
3672 tmppar2->next->text.clear();
3674 tmppar2->next->previous = tmppar2;
3675 tmppar2 = tmppar2->next;
3679 undopar = 0; // nothing to replace (undo of delete maybe)
3681 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3682 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3684 Undo * undo = new Undo(kind,
3685 before_number, behind_number,
3686 cursor_par, cursor_pos,
3689 undo_finished = false;
3694 void LyXText::SetCursorParUndo()
3696 SetUndo(Undo::FINISH,
3697 cursor.par->ParFromPos(cursor.pos)->previous,
3698 cursor.par->ParFromPos(cursor.pos)->next);
3702 void LyXText::RemoveTableRow(LyXCursor * cur) const
3708 // move to the previous row
3709 int cell_act = NumberOfCell(cur->par, cur->pos);
3712 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3715 !cur->par->table->IsFirstCell(cell_act)) {
3717 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3722 // now we have to pay attention if the actual table is the
3723 // main row of TableContRows and if yes to delete all of them
3728 // delete up to the next row
3729 while (cur->pos < cur->par->Last() &&
3731 || !cur->par->table->IsFirstCell(cell_act))) {
3732 while (cur->pos < cur->par->Last() &&
3733 !cur->par->IsNewline(cur->pos))
3734 cur->par->Erase(cur->pos);
3737 if (cur->pos < cur->par->Last())
3738 cur->par->Erase(cur->pos);
3740 if (cur->pos && cur->pos == cur->par->Last()) {
3742 cur->par->Erase(cur->pos); // no newline at very end!
3744 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3745 !cur->par->table->IsContRow(cell_org) &&
3746 cur->par->table->IsContRow(cell));
3747 cur->par->table->DeleteRow(cell_org);
3752 bool LyXText::IsEmptyTableCell() const
3754 LyXParagraph::size_type pos = cursor.pos - 1;
3755 while (pos >= 0 && pos < cursor.par->Last()
3756 && !cursor.par->IsNewline(pos))
3758 return cursor.par->IsNewline(pos + 1);
3762 void LyXText::toggleAppendix(){
3763 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3764 bool start = !par->start_of_appendix;
3766 // ensure that we have only one start_of_appendix in this document
3767 LyXParagraph * tmp = FirstParagraph();
3768 for (; tmp; tmp = tmp->next)
3769 tmp->start_of_appendix = 0;
3770 par->start_of_appendix = start;
3772 // we can set the refreshing parameters now
3773 status = LyXText::NEED_MORE_REFRESH;
3775 refresh_row = 0; // not needed for full update
3777 SetCursor(cursor.par, cursor.pos);