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);
1352 char const * alphaCounter(int n) {
1353 static char result[2];
1358 result[0] = 'A' + n;
1366 // set the counter of a paragraph. This includes the labels
1367 void LyXText::SetCounter(LyXParagraph * par) const
1369 // this is only relevant for the beginning of paragraph
1370 par = par->FirstPhysicalPar();
1372 LyXLayout const & layout = textclasslist.Style(parameters->textclass,
1375 LyXTextClass const & textclass =
1376 textclasslist.TextClass(parameters->textclass);
1378 /* copy the prev-counters to this one, unless this is the start of a
1379 footnote or of a bibliography or the very first paragraph */
1381 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1382 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1383 && par->footnotekind == LyXParagraph::FOOTNOTE)
1384 && !(textclasslist.Style(parameters->textclass,
1385 par->Previous()->GetLayout()
1386 ).labeltype != LABEL_BIBLIO
1387 && layout.labeltype == LABEL_BIBLIO)) {
1388 for (int i = 0; i < 10; ++i) {
1389 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1391 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1392 if (!par->appendix && par->start_of_appendix){
1393 par->appendix = true;
1394 for (int i = 0; i < 10; ++i) {
1395 par->setCounter(i, 0);
1398 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1399 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1402 for (int i = 0; i < 10; ++i) {
1403 par->setCounter(i, 0);
1405 par->appendix = par->start_of_appendix;
1410 // if this is an open marginnote and this is the first
1411 // entry in the marginnote and the enclosing
1412 // environment is an enum/item then correct for the
1413 // LaTeX behaviour (ARRae)
1414 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1415 && par->footnotekind == LyXParagraph::MARGIN
1417 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1418 && (par->PreviousBeforeFootnote()
1419 && textclasslist.Style(parameters->textclass,
1420 par->PreviousBeforeFootnote()->GetLayout()
1421 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1422 // Any itemize or enumerate environment in a marginnote
1423 // that is embedded in an itemize or enumerate
1424 // paragraph is seen by LaTeX as being at a deeper
1425 // level within that enclosing itemization/enumeration
1426 // even if there is a "standard" layout at the start of
1432 /* Maybe we have to increment the enumeration depth.
1433 * BUT, enumeration in a footnote is considered in isolation from its
1434 * surrounding paragraph so don't increment if this is the
1435 * first line of the footnote
1436 * AND, bibliographies can't have their depth changed ie. they
1437 * are always of depth 0
1440 && par->Previous()->GetDepth() < par->GetDepth()
1441 && textclasslist.Style(parameters->textclass,
1442 par->Previous()->GetLayout()
1443 ).labeltype == LABEL_COUNTER_ENUMI
1444 && par->enumdepth < 3
1445 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1446 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1447 && par->footnotekind == LyXParagraph::FOOTNOTE)
1448 && layout.labeltype != LABEL_BIBLIO) {
1452 /* Maybe we have to decrement the enumeration depth, see note above */
1454 && par->Previous()->GetDepth() > par->GetDepth()
1455 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1456 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1457 && par->footnotekind == LyXParagraph::FOOTNOTE)
1458 && layout.labeltype != LABEL_BIBLIO) {
1459 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1460 par->setCounter(6 + par->enumdepth,
1461 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1462 /* reset the counters.
1463 * A depth change is like a breaking layout
1465 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1466 par->setCounter(i, 0);
1469 if (!par->labelstring.empty()) {
1470 par->labelstring.clear();
1473 if (layout.margintype == MARGIN_MANUAL) {
1474 if (par->labelwidthstring.empty()) {
1475 par->SetLabelWidthString(layout.labelstring());
1478 par->SetLabelWidthString(string());
1481 /* is it a layout that has an automatic label ? */
1482 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1484 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1485 if (i >= 0 && i<= parameters->secnumdepth) {
1486 par->incCounter(i); // increment the counter
1488 char * s = new char[50];
1490 // Is there a label? Useful for Chapter layout
1491 if (!par->appendix){
1492 if (!layout.labelstring().empty())
1493 par->labelstring = layout.labelstring();
1495 par->labelstring.clear();
1497 if (!layout.labelstring_appendix().empty())
1498 par->labelstring = layout.labelstring_appendix();
1500 par->labelstring.clear();
1503 if (!par->appendix){
1504 switch (2 * LABEL_FIRST_COUNTER -
1505 textclass.maxcounter() + i) {
1506 case LABEL_COUNTER_CHAPTER:
1508 par->getCounter(i));
1510 case LABEL_COUNTER_SECTION:
1512 par->getCounter(i - 1),
1513 par->getCounter(i));
1515 case LABEL_COUNTER_SUBSECTION:
1516 sprintf(s, "%d.%d.%d",
1517 par->getCounter(i-2),
1518 par->getCounter(i-1),
1519 par->getCounter(i));
1521 case LABEL_COUNTER_SUBSUBSECTION:
1522 sprintf(s, "%d.%d.%d.%d",
1523 par->getCounter(i-3),
1524 par->getCounter(i-2),
1525 par->getCounter(i-1),
1526 par->getCounter(i));
1528 case LABEL_COUNTER_PARAGRAPH:
1529 sprintf(s, "%d.%d.%d.%d.%d",
1530 par->getCounter(i-4),
1531 par->getCounter(i-3),
1532 par->getCounter(i-2),
1533 par->getCounter(i-1),
1534 par->getCounter(i));
1536 case LABEL_COUNTER_SUBPARAGRAPH:
1537 sprintf(s, "%d.%d.%d.%d.%d.%d",
1538 par->getCounter(i-5),
1539 par->getCounter(i-4),
1540 par->getCounter(i-3),
1541 par->getCounter(i-2),
1542 par->getCounter(i-1),
1543 par->getCounter(i));
1546 sprintf(s, "%d.", par->getCounter(i));
1550 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1551 case LABEL_COUNTER_CHAPTER:
1553 alphaCounter(par->getCounter(i)));
1555 case LABEL_COUNTER_SECTION:
1557 alphaCounter(par->getCounter(i - 1)),
1558 par->getCounter(i));
1560 case LABEL_COUNTER_SUBSECTION:
1561 sprintf(s, "%s.%d.%d",
1562 alphaCounter(par->getCounter(i-2)),
1563 par->getCounter(i-1),
1564 par->getCounter(i));
1566 case LABEL_COUNTER_SUBSUBSECTION:
1567 sprintf(s, "%s.%d.%d.%d",
1568 alphaCounter(par->getCounter(i-3)),
1569 par->getCounter(i-2),
1570 par->getCounter(i-1),
1571 par->getCounter(i));
1573 case LABEL_COUNTER_PARAGRAPH:
1574 sprintf(s, "%s.%d.%d.%d.%d",
1575 alphaCounter(par->getCounter(i-4)),
1576 par->getCounter(i-3),
1577 par->getCounter(i-2),
1578 par->getCounter(i-1),
1579 par->getCounter(i));
1581 case LABEL_COUNTER_SUBPARAGRAPH:
1582 sprintf(s, "%s.%d.%d.%d.%d.%d",
1583 alphaCounter(par->getCounter(i-5)),
1584 par->getCounter(i-4),
1585 par->getCounter(i-3),
1586 par->getCounter(i-2),
1587 par->getCounter(i-1),
1588 par->getCounter(i));
1591 sprintf(s, "%c.", par->getCounter(i));
1596 par->labelstring += s;
1599 for (i++; i < 10; ++i) {
1600 // reset the following counters
1601 par->setCounter(i, 0);
1603 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1604 for (i++; i < 10; ++i) {
1605 // reset the following counters
1606 par->setCounter(i, 0);
1608 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1609 par->incCounter(i + par->enumdepth);
1610 char * s = new char[25];
1611 int number = par->getCounter(i + par->enumdepth);
1613 static const char *roman[20] = {
1614 "i", "ii", "iii", "iv", "v",
1615 "vi", "vii", "viii", "ix", "x",
1616 "xi", "xii", "xiii", "xiv", "xv",
1617 "xvi", "xvii", "xviii", "xix", "xx"
1619 static const char hebrew[22] = {
1620 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1621 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1622 '÷', 'ø', 'ù', 'ú'
1625 switch (par->enumdepth) {
1627 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1628 sprintf(s, "(%c)", ((number-1) % 26) + 'a');
1630 sprintf(s, "(%c)", hebrew[(number-1) % 22]);
1633 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1634 sprintf(s, "%s.", roman[(number-1) % 20]);
1636 sprintf(s, ".%s", roman[(number-1) % 20]);
1639 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1640 sprintf(s, "%c.", ((number-1) % 26) + 'A');
1642 sprintf(s, ".%c", ((number-1) % 26) + 'A');
1645 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1646 sprintf(s, "%d.", number);
1648 sprintf(s, ".%d", number);
1651 par->labelstring = s;
1654 for (i += par->enumdepth + 1; i < 10; ++i)
1655 par->setCounter(i, 0); /* reset the following counters */
1658 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1659 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1661 int number = par->getCounter(i);
1663 par->bibkey = new InsetBibKey();
1664 par->bibkey->setCounter(number);
1665 par->labelstring = layout.labelstring();
1667 // In biblio should't be following counters but...
1669 string s = layout.labelstring();
1671 // the caption hack:
1673 if (layout.labeltype == LABEL_SENSITIVE) {
1674 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1675 && (par->footnotekind == LyXParagraph::FIG
1676 || par->footnotekind == LyXParagraph::WIDE_FIG))
1677 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1681 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1682 && (par->footnotekind == LyXParagraph::TAB
1683 || par->footnotekind == LyXParagraph::WIDE_TAB))
1684 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1688 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1689 && par->footnotekind == LyXParagraph::ALGORITHM)
1690 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1693 s = ":Ãúéøåâìà ";
1695 /* par->SetLayout(0);
1696 s = layout->labelstring; */
1697 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1700 s = " :úåòîùî øñç";
1704 par->labelstring = s;
1706 /* reset the enumeration counter. They are always resetted
1707 * when there is any other layout between */
1708 for (int i = 6 + par->enumdepth; i < 10; ++i)
1709 par->setCounter(i, 0);
1714 /* Updates all counters BEHIND the row. Changed paragraphs
1715 * with a dynamic left margin will be rebroken. */
1716 void LyXText::UpdateCounters(Row * row) const
1725 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1726 par = row->par->LastPhysicalPar()->Next();
1728 par = row->par->next;
1733 while (row->par != par)
1738 /* now check for the headline layouts. remember that they
1739 * have a dynamic left margin */
1741 && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1742 || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1745 /* Rebreak the paragraph */
1746 RemoveParagraph(row);
1747 AppendParagraph(row);
1749 /* think about the damned open footnotes! */
1750 while (par->Next() &&
1751 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1752 || par->Next()->IsDummy())){
1754 if (par->IsDummy()) {
1755 while (row->par != par)
1757 RemoveParagraph(row);
1758 AppendParagraph(row);
1763 par = par->LastPhysicalPar()->Next();
1769 /* insets an inset. */
1770 void LyXText::InsertInset(Inset *inset)
1772 SetUndo(Undo::INSERT,
1773 cursor.par->ParFromPos(cursor.pos)->previous,
1774 cursor.par->ParFromPos(cursor.pos)->next);
1775 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1776 cursor.par->InsertInset(cursor.pos, inset);
1777 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1778 * The character will not be inserted a
1783 // this is for the simple cut and paste mechanism
1784 static LyXParagraph * simple_cut_buffer = 0;
1785 static char simple_cut_buffer_textclass = 0;
1787 void DeleteSimpleCutBuffer()
1789 if (!simple_cut_buffer)
1791 LyXParagraph * tmppar;
1793 while (simple_cut_buffer) {
1794 tmppar = simple_cut_buffer;
1795 simple_cut_buffer = simple_cut_buffer->next;
1798 simple_cut_buffer = 0;
1802 void LyXText::copyEnvironmentType()
1804 copylayouttype = cursor.par->GetLayout();
1808 void LyXText::pasteEnvironmentType()
1810 SetLayout(copylayouttype);
1814 void LyXText::CutSelection(bool doclear)
1816 // This doesn't make sense, if there is no selection
1820 // OK, we have a selection. This is always between sel_start_cursor
1821 // and sel_end cursor
1822 LyXParagraph * tmppar;
1824 // Check whether there are half footnotes in the selection
1825 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1826 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1827 tmppar = sel_start_cursor.par;
1828 while (tmppar != sel_end_cursor.par){
1829 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1830 WriteAlert(_("Impossible operation"),
1831 _("Don't know what to do with half floats."),
1835 tmppar = tmppar->Next();
1839 /* table stuff -- begin */
1840 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1841 if ( sel_start_cursor.par != sel_end_cursor.par) {
1842 WriteAlert(_("Impossible operation"),
1843 _("Don't know what to do with half tables."),
1847 sel_start_cursor.par->table->Reinit();
1849 /* table stuff -- end */
1851 // make sure that the depth behind the selection are restored, too
1852 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1853 LyXParagraph * undoendpar = endpar;
1855 if (endpar && endpar->GetDepth()) {
1856 while (endpar && endpar->GetDepth()) {
1857 endpar = endpar->LastPhysicalPar()->Next();
1858 undoendpar = endpar;
1860 } else if (endpar) {
1861 endpar = endpar->Next(); // because of parindents etc.
1864 SetUndo(Undo::DELETE,
1866 .par->ParFromPos(sel_start_cursor.pos)->previous,
1869 // clear the simple_cut_buffer
1870 DeleteSimpleCutBuffer();
1872 // set the textclass
1873 simple_cut_buffer_textclass = parameters->textclass;
1875 #ifdef WITH_WARNINGS
1876 #warning Asger: Make cut more intelligent here.
1879 White paper for "intelligent" cutting:
1881 Example: "This is our text."
1882 Using " our " as selection, cutting will give "This istext.".
1883 Using "our" as selection, cutting will give "This is text.".
1884 Using " our" as selection, cutting will give "This is text.".
1885 Using "our " as selection, cutting will give "This is text.".
1887 All those four selections will (however) paste identically:
1888 Pasting with the cursor right after the "is" will give the
1889 original text with all four selections.
1891 The rationale is to be intelligent such that words are copied,
1892 cut and pasted in a functional manner.
1894 This is not implemented yet. (Asger)
1896 The changes below sees to do a lot of what you want. However
1897 I have not verified that all cases work as they should:
1899 - cut in multiple row
1901 - cut across footnotes and paragraph
1902 My simplistic tests show that the idea are basically sound but
1903 there are some items to fix up...we only need to find them
1906 As do redo Asger's example above (with | beeing the cursor in the
1907 result after cutting.):
1909 Example: "This is our 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.".
1913 Using "our " as selection, cutting will give "This is |text.".
1918 #ifndef FIX_DOUBLE_SPACE
1919 bool space_wrapped =
1920 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1921 if (sel_end_cursor.pos > 0
1922 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1923 // please break before a space at the end
1924 sel_end_cursor.pos--;
1925 space_wrapped = true;
1927 // cut behind a space if there is one
1928 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
1929 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
1930 && (sel_start_cursor.par != sel_end_cursor.par
1931 || sel_start_cursor.pos < sel_end_cursor.pos))
1932 sel_start_cursor.pos++;
1934 // there are two cases: cut only within one paragraph or
1935 // more than one paragraph
1937 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
1938 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
1939 // only within one paragraph
1940 simple_cut_buffer = new LyXParagraph;
1941 LyXParagraph::size_type i =
1942 sel_start_cursor.pos;
1943 for (; i < sel_end_cursor.pos; ++i) {
1944 /* table stuff -- begin */
1945 if (sel_start_cursor.par->table
1946 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
1947 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1948 sel_start_cursor.pos++;
1950 /* table stuff -- end */
1951 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
1952 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1954 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
1956 #ifndef FIX_DOUBLE_SPACE
1957 // check for double spaces
1958 if (sel_start_cursor.pos &&
1959 sel_start_cursor.par->Last() > sel_start_cursor.pos
1960 && sel_start_cursor.par
1961 ->IsLineSeparator(sel_start_cursor.pos - 1)
1962 && sel_start_cursor.par
1963 ->IsLineSeparator(sel_start_cursor.pos)) {
1964 sel_start_cursor.par->Erase(sel_start_cursor.pos);
1967 simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
1970 endpar = sel_end_cursor.par->Next();
1972 // cut more than one paragraph
1975 ->BreakParagraphConservative(sel_end_cursor.pos);
1976 #ifndef FIX_DOUBLE_SPACE
1977 // insert a space at the end if there was one
1980 ->InsertChar(sel_end_cursor.par->Last(), ' ');
1982 sel_end_cursor.par = sel_end_cursor.par->Next();
1983 sel_end_cursor.pos = 0;
1985 cursor = sel_end_cursor;
1987 #ifndef FIX_DOUBLE_SPACE
1988 // please break behind a space, if there is one.
1989 // The space should be copied too
1990 if (sel_start_cursor.par
1991 ->IsLineSeparator(sel_start_cursor.pos))
1992 sel_start_cursor.pos++;
1994 sel_start_cursor.par
1995 ->BreakParagraphConservative(sel_start_cursor.pos);
1996 #ifndef FIX_DOUBLE_SPACE
1997 if (!sel_start_cursor.pos
1998 || sel_start_cursor.par
1999 ->IsLineSeparator(sel_start_cursor.pos - 1)
2000 || sel_start_cursor.par
2001 ->IsNewline(sel_start_cursor.pos - 1)) {
2002 sel_start_cursor.par->Next()->InsertChar(0, ' ');
2005 // store the endparagraph for redoing later
2006 endpar = sel_end_cursor.par->Next(); /* needed because
2011 // store the selection
2012 simple_cut_buffer = sel_start_cursor.par
2013 ->ParFromPos(sel_start_cursor.pos)->next;
2014 simple_cut_buffer->previous = 0;
2015 sel_end_cursor.par->previous->next = 0;
2017 // cut the selection
2018 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2019 = sel_end_cursor.par;
2021 sel_end_cursor.par->previous
2022 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2024 // care about footnotes
2025 if (simple_cut_buffer->footnoteflag) {
2026 LyXParagraph * tmppar = simple_cut_buffer;
2028 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2029 tmppar = tmppar->next;
2033 // the cut selection should begin with standard layout
2034 simple_cut_buffer->Clear();
2036 // paste the paragraphs again, if possible
2038 sel_start_cursor.par->Next()->ClearParagraph();
2039 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2041 !sel_start_cursor.par->Next()->Last())
2042 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2044 #ifndef FIX_DOUBLE_SPACE
2045 // maybe a forgotten blank
2046 if (sel_start_cursor.pos
2047 && sel_start_cursor.par
2048 ->IsLineSeparator(sel_start_cursor.pos)
2049 && sel_start_cursor.par
2050 ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2051 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2056 // sometimes necessary
2058 sel_start_cursor.par->ClearParagraph();
2060 RedoParagraphs(sel_start_cursor, endpar);
2063 cursor = sel_start_cursor;
2064 SetCursor(cursor.par, cursor.pos);
2065 sel_cursor = cursor;
2066 UpdateCounters(cursor.row);
2070 void LyXText::CopySelection()
2072 // this doesnt make sense, if there is no selection
2076 // ok we have a selection. This is always between sel_start_cursor
2077 // and sel_end cursor
2078 LyXParagraph * tmppar;
2080 /* check wether there are half footnotes in the selection */
2081 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2082 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2083 tmppar = sel_start_cursor.par;
2084 while (tmppar != sel_end_cursor.par) {
2085 if (tmppar->footnoteflag !=
2086 sel_end_cursor.par->footnoteflag) {
2087 WriteAlert(_("Impossible operation"),
2088 _("Don't know what to do"
2089 " with half floats."),
2093 tmppar = tmppar->Next();
2097 /* table stuff -- begin */
2098 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2099 if ( sel_start_cursor.par != sel_end_cursor.par){
2100 WriteAlert(_("Impossible operation"),
2101 _("Don't know what to do with half tables."),
2106 /* table stuff -- end */
2108 // delete the simple_cut_buffer
2109 DeleteSimpleCutBuffer();
2111 // set the textclass
2112 simple_cut_buffer_textclass = parameters->textclass;
2114 #ifdef FIX_DOUBLE_SPACE
2115 // copy behind a space if there is one
2116 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2117 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2118 && (sel_start_cursor.par != sel_end_cursor.par
2119 || sel_start_cursor.pos < sel_end_cursor.pos))
2120 sel_start_cursor.pos++;
2122 // there are two cases: copy only within one paragraph
2123 // or more than one paragraph
2124 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2125 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2126 // only within one paragraph
2127 simple_cut_buffer = new LyXParagraph;
2128 LyXParagraph::size_type i = 0;
2129 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2130 sel_start_cursor.par->CopyIntoMinibuffer(i);
2131 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2134 // copy more than one paragraph
2135 // clone the paragraphs within the selection
2137 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2138 simple_cut_buffer = tmppar->Clone();
2139 LyXParagraph *tmppar2 = simple_cut_buffer;
2141 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2143 tmppar = tmppar->next;
2144 tmppar2->next = tmppar->Clone();
2145 tmppar2->next->previous = tmppar2;
2146 tmppar2 = tmppar2->next;
2150 // care about footnotes
2151 if (simple_cut_buffer->footnoteflag) {
2152 tmppar = simple_cut_buffer;
2154 tmppar->footnoteflag =
2155 LyXParagraph::NO_FOOTNOTE;
2156 tmppar = tmppar->next;
2160 // the simple_cut_buffer paragraph is too big
2161 LyXParagraph::size_type tmpi2 =
2162 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2163 for (; tmpi2; --tmpi2)
2164 simple_cut_buffer->Erase(0);
2166 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2168 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2169 while (tmppar2->size() > tmpi2) {
2170 tmppar2->Erase(tmppar2->text.size() - 1);
2176 void LyXText::PasteSelection()
2178 // this does not make sense, if there is nothing to paste
2179 if (!simple_cut_buffer)
2182 LyXParagraph * tmppar;
2183 LyXParagraph * endpar;
2185 LyXCursor tmpcursor;
2187 // be carefull with footnotes in footnotes
2188 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2190 // check whether the cut_buffer includes a footnote
2191 tmppar = simple_cut_buffer;
2193 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2194 tmppar = tmppar->next;
2197 WriteAlert(_("Impossible operation"),
2198 _("Can't paste float into float!"),
2204 /* table stuff -- begin */
2205 if (cursor.par->table) {
2206 if (simple_cut_buffer->next) {
2207 WriteAlert(_("Impossible operation"),
2208 _("Table cell cannot include more than one paragraph!"),
2213 /* table stuff -- end */
2215 SetUndo(Undo::INSERT,
2216 cursor.par->ParFromPos(cursor.pos)->previous,
2217 cursor.par->ParFromPos(cursor.pos)->next);
2221 // There are two cases: cutbuffer only one paragraph or many
2222 if (!simple_cut_buffer->next) {
2223 // only within a paragraph
2225 #ifndef FIX_DOUBLE_SPACE
2226 // please break behind a space, if there is one
2227 while (tmpcursor.par->Last() > tmpcursor.pos
2228 && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2231 tmppar = simple_cut_buffer->Clone();
2232 /* table stuff -- begin */
2233 bool table_too_small = false;
2234 if (tmpcursor.par->table) {
2235 while (simple_cut_buffer->text.size()
2236 && !table_too_small) {
2237 if (simple_cut_buffer->IsNewline(0)){
2238 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2240 simple_cut_buffer->Erase(0);
2241 if (tmpcursor.pos < tmpcursor.par->Last())
2244 table_too_small = true;
2246 #ifdef FIX_DOUBLE_SPACE
2247 // This is an attempt to fix the
2248 // "never insert a space at the
2249 // beginning of a paragraph" problem.
2250 if (tmpcursor.pos == 0
2251 && simple_cut_buffer->IsLineSeparator(0)) {
2252 simple_cut_buffer->Erase(0);
2254 simple_cut_buffer->CutIntoMinibuffer(0);
2255 simple_cut_buffer->Erase(0);
2256 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2260 simple_cut_buffer->CutIntoMinibuffer(0);
2261 simple_cut_buffer->Erase(0);
2262 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2268 /* table stuff -- end */
2269 // Some provisions should be done here for checking
2270 // if we are inserting at the beginning of a
2271 // paragraph. If there are a space at the beginning
2272 // of the text to insert and we are inserting at
2273 // the beginning of the paragraph the space should
2275 while (simple_cut_buffer->text.size()) {
2276 #ifdef FIX_DOUBLE_SPACE
2277 // This is an attempt to fix the
2278 // "never insert a space at the
2279 // beginning of a paragraph" problem.
2280 if (tmpcursor.pos == 0
2281 && simple_cut_buffer->IsLineSeparator(0)) {
2282 simple_cut_buffer->Erase(0);
2284 simple_cut_buffer->CutIntoMinibuffer(0);
2285 simple_cut_buffer->Erase(0);
2286 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2290 simple_cut_buffer->CutIntoMinibuffer(0);
2291 simple_cut_buffer->Erase(0);
2292 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2297 delete simple_cut_buffer;
2298 simple_cut_buffer = tmppar;
2299 endpar = tmpcursor.par->Next();
2303 // make a copy of the simple cut_buffer
2304 tmppar = simple_cut_buffer;
2305 LyXParagraph * simple_cut_clone = tmppar->Clone();
2306 LyXParagraph * tmppar2 = simple_cut_clone;
2307 if (cursor.par->footnoteflag){
2308 tmppar->footnoteflag = cursor.par->footnoteflag;
2309 tmppar->footnotekind = cursor.par->footnotekind;
2311 while (tmppar->next) {
2312 tmppar = tmppar->next;
2313 tmppar2->next = tmppar->Clone();
2314 tmppar2->next->previous = tmppar2;
2315 tmppar2 = tmppar2->next;
2316 if (cursor.par->footnoteflag){
2317 tmppar->footnoteflag = cursor.par->footnoteflag;
2318 tmppar->footnotekind = cursor.par->footnotekind;
2322 // make sure there is no class difference
2323 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2324 parameters->textclass,
2327 // make the simple_cut_buffer exactly the same layout than
2328 // the cursor paragraph
2329 simple_cut_buffer->MakeSameLayout(cursor.par);
2331 // find the end of the buffer
2332 LyXParagraph * lastbuffer = simple_cut_buffer;
2333 while (lastbuffer->Next())
2334 lastbuffer = lastbuffer->Next();
2336 #ifndef FIX_DOUBLE_SPACE
2337 // Please break behind a space, if there is one. The space
2338 // should be copied too.
2339 if (cursor.par->Last() > cursor.pos
2340 && cursor.par->IsLineSeparator(cursor.pos))
2343 bool paste_the_end = false;
2345 // open the paragraph for inserting the simple_cut_buffer
2347 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2348 cursor.par->BreakParagraphConservative(cursor.pos);
2349 paste_the_end = true;
2352 #ifndef FIX_DOUBLE_SPACE
2353 // be careful with double spaces
2354 if ((!cursor.par->Last()
2355 || cursor.par->IsLineSeparator(cursor.pos - 1)
2356 || cursor.par->IsNewline(cursor.pos - 1))
2357 && simple_cut_buffer->text.size()
2358 && simple_cut_buffer->IsLineSeparator(0))
2359 simple_cut_buffer->Erase(0);
2361 // set the end for redoing later
2362 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2365 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2366 cursor.par->ParFromPos(cursor.pos)->next;
2367 cursor.par->ParFromPos(cursor.pos)->next->previous =
2368 lastbuffer->ParFromPos(lastbuffer->Last());
2370 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2371 simple_cut_buffer->previous =
2372 cursor.par->ParFromPos(cursor.pos);
2374 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2375 lastbuffer = cursor.par;
2377 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2379 // store the new cursor position
2380 tmpcursor.par = lastbuffer;
2381 tmpcursor.pos = lastbuffer->Last();
2383 // maybe some pasting
2384 if (lastbuffer->Next() && paste_the_end) {
2385 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2386 #ifndef FIX_DOUBLE_SPACE
2387 // be careful with double spaces
2388 if ((!lastbuffer->Last()
2389 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2390 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2391 && lastbuffer->Next()->Last()
2392 && lastbuffer->Next()->IsLineSeparator(0))
2393 lastbuffer->Next()->Erase(0);
2395 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2397 } else if (!lastbuffer->Next()->Last()) {
2398 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2399 #ifndef FIX_DOUBLE_SPACE
2400 // be careful witth double spaces
2401 if ((!lastbuffer->Last()
2402 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2403 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2404 && lastbuffer->Next()->Last()
2405 && lastbuffer->Next()->IsLineSeparator(0))
2406 lastbuffer->Next()->Erase(0);
2408 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2410 } else if (!lastbuffer->Last()) {
2411 lastbuffer->MakeSameLayout(lastbuffer->next);
2412 #ifndef FIX_DOUBLE_SPACE
2413 // be careful witth double spaces
2414 if ((!lastbuffer->Last()
2415 || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2416 || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2417 && lastbuffer->Next()->Last()
2418 && lastbuffer->Next()->IsLineSeparator(0))
2419 lastbuffer->Next()->Erase(0);
2421 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2424 lastbuffer->Next()->ClearParagraph();
2427 // restore the simple cut buffer
2428 simple_cut_buffer = simple_cut_clone;
2431 RedoParagraphs(cursor, endpar);
2433 SetCursor(cursor.par, cursor.pos);
2436 sel_cursor = cursor;
2437 SetCursor(tmpcursor.par, tmpcursor.pos);
2439 UpdateCounters(cursor.row);
2443 // returns a pointer to the very first LyXParagraph
2444 LyXParagraph * LyXText::FirstParagraph() const
2446 return params->paragraph;
2450 // returns true if the specified string is at the specified position
2451 bool LyXText::IsStringInText(LyXParagraph * par,
2452 LyXParagraph::size_type pos,
2453 char const * str) const
2457 while (pos + i < par->Last() && str[i] &&
2458 str[i] == par->GetChar(pos + i)) {
2468 // sets the selection over the number of characters of string, no check!!
2469 void LyXText::SetSelectionOverString(char const * string)
2471 sel_cursor = cursor;
2472 for (int i = 0; string[i]; ++i)
2478 // simple replacing. The font of the first selected character is used
2479 void LyXText::ReplaceSelectionWithString(char const * str)
2484 if (!selection) { // create a dummy selection
2485 sel_end_cursor = cursor;
2486 sel_start_cursor = cursor;
2489 // Get font setting before we cut
2490 LyXParagraph::size_type pos = sel_end_cursor.pos;
2491 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2493 // Insert the new string
2494 for (int i = 0; str[i]; ++i) {
2495 sel_end_cursor.par->InsertChar(pos, str[i]);
2496 sel_end_cursor.par->SetFont(pos, font);
2500 // Cut the selection
2507 // if the string can be found: return true and set the cursor to
2509 bool LyXText::SearchForward(char const * str) const
2511 LyXParagraph * par = cursor.par;
2512 LyXParagraph::size_type pos = cursor.pos;
2513 while (par && !IsStringInText(par, pos, str)) {
2514 if (pos < par->Last() - 1)
2522 SetCursor(par, pos);
2530 bool LyXText::SearchBackward(char const * string) const
2532 LyXParagraph * par = cursor.par;
2533 int pos = cursor.pos;
2539 // We skip empty paragraphs (Asger)
2541 par = par->Previous();
2543 pos = par->Last() - 1;
2544 } while (par && pos < 0);
2546 } while (par && !IsStringInText(par, pos, string));
2549 SetCursor(par, pos);
2556 void LyXText::InsertStringA(LyXParagraph::TextContainer const & text)
2558 char * str = new char[text.size() + 1];
2559 copy(text.begin(), text.end(), str);
2560 str[text.size()] = '\0';
2566 // needed to insert the selection
2567 void LyXText::InsertStringA(char const * s)
2570 LyXParagraph * par = cursor.par;
2571 LyXParagraph::size_type pos = cursor.pos;
2572 LyXParagraph::size_type a = 0;
2574 LyXParagraph * endpar = cursor.par->Next();
2579 textclasslist.Style(parameters->textclass,
2580 cursor.par->GetLayout()).isEnvironment();
2581 // only to be sure, should not be neccessary
2584 // insert the string, don't insert doublespace
2585 string::size_type i = 0;
2586 while (i < str.length()) {
2587 if (str[i] != '\n') {
2589 && i + 1 < str.length() && str[i + 1] != ' '
2590 && pos && par->GetChar(pos - 1)!= ' ') {
2591 par->InsertChar(pos,' ');
2593 } else if (par->table) {
2594 if (str[i] == '\t') {
2595 while((pos < par->size()) &&
2596 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2598 if (pos < par->size())
2600 else // no more fields to fill skip the rest
2602 } else if ((str[i] != 13) &&
2603 ((str[i] & 127) >= ' ')) {
2604 par->InsertChar(pos, str[i]);
2607 } else if (str[i] == ' ') {
2609 InsetSpecialChar * new_inset =
2610 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2611 par->InsertInset(pos, new_inset);
2613 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2616 } else if (str[i] == '\t') {
2617 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2619 InsetSpecialChar * new_inset =
2620 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2621 par->InsertInset(pos, new_inset);
2623 par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2627 } else if (str[i]!= 13 &&
2628 // Ignore unprintables
2629 (str[i] & 127) >= ' ') {
2630 par->InsertChar(pos, str[i]);
2635 if (i + 1 >= str.length()) {
2639 while((pos < par->size()) &&
2640 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2643 cell = NumberOfCell(par, pos);
2644 while((pos < par->size()) &&
2645 !(par->table->IsFirstCell(cell))) {
2647 while((pos < par->size()) &&
2648 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2651 cell = NumberOfCell(par, pos);
2653 if (pos >= par->size())
2654 // no more fields to fill skip the rest
2657 if (!par->text.size()) {
2659 InsetSpecialChar * new_inset =
2660 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2661 par->InsertInset(pos, new_inset);
2663 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2667 par->BreakParagraph(pos, flag);
2675 RedoParagraphs(cursor, endpar);
2676 SetCursor(cursor.par, cursor.pos);
2677 sel_cursor = cursor;
2678 SetCursor(par, pos);
2683 void LyXText::InsertStringB(LyXParagraph::TextContainer const & text)
2685 char * str = new char[text.size() + 1];
2686 copy(text.begin(), text.end(), str);
2687 str[text.size()] = '\0';
2693 /* turns double-CR to single CR, others where converted into one blank and 13s
2694 * that are ignored .Double spaces are also converted into one. Spaces at
2695 * the beginning of a paragraph are forbidden. tabs are converted into one
2696 * space. then InsertStringA is called */
2697 void LyXText::InsertStringB(char const * s)
2700 LyXParagraph * par = cursor.par;
2701 string::size_type i = 1;
2702 while (i < str.length()) {
2703 if (str[i] == '\t' && !par->table)
2705 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2707 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2708 if (str[i + 1] != '\n') {
2709 if (str[i - 1] != ' ')
2714 while (i + 1 < str.length()
2715 && (str[i + 1] == ' '
2716 || str[i + 1] == '\t'
2717 || str[i + 1] == '\n'
2718 || str[i + 1] == 13)) {
2725 InsertStringA(str.c_str());
2729 bool LyXText::GotoNextError() const
2731 LyXCursor res = cursor;
2733 if (res.pos < res.par->Last() - 1) {
2737 res.par = res.par->Next();
2742 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2743 && res.par->GetInset(res.pos)->AutoDelete()));
2746 SetCursor(res.par, res.pos);
2753 bool LyXText::GotoNextNote() const
2755 LyXCursor res = cursor;
2757 if (res.pos < res.par->Last() - 1) {
2760 res.par = res.par->Next();
2765 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2766 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2769 SetCursor(res.par, res.pos);
2776 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2777 LyXTextClassList::size_type class2,
2781 if (!par || class1 == class2)
2783 par = par->FirstPhysicalPar();
2785 string name = textclasslist.NameOfLayout(class1, par->layout);
2787 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2788 textclasslist.NumberOfLayout(class2, name);
2791 } else { // layout not found
2792 // use default layout "Standard" (0)
2797 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2799 string s = "Layout had to be changed from\n"
2800 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2801 + "\nbecause of class conversion from\n"
2802 + textclasslist.NameOfClass(class1) + " to "
2803 + textclasslist.NameOfClass(class2);
2804 InsetError * new_inset = new InsetError(s);
2805 par->InsertChar(0, LyXParagraph::META_INSET);
2806 par->InsertInset(0, new_inset);
2815 void LyXText::CheckParagraph(LyXParagraph * par,
2816 LyXParagraph::size_type pos)
2819 LyXCursor tmpcursor;
2821 /* table stuff -- begin*/
2824 CheckParagraphInTable(par, pos);
2827 /* table stuff -- end*/
2830 LyXParagraph::size_type z;
2831 Row * row = GetRow(par, pos, y);
2833 // is there a break one row above
2834 if (row->previous && row->previous->par == row->par) {
2835 z = NextBreakPoint(row->previous, paperwidth);
2836 if ( z >= row->pos) {
2837 // set the dimensions of the row above
2838 y -= row->previous->height;
2840 refresh_row = row->previous;
2841 status = LyXText::NEED_MORE_REFRESH;
2843 BreakAgain(row->previous);
2845 // set the cursor again. Otherwise
2846 // dangling pointers are possible
2847 SetCursor(cursor.par, cursor.pos);
2848 sel_cursor = cursor;
2853 int tmpheight = row->height;
2854 LyXParagraph::size_type tmplast = RowLast(row);
2859 if (row->height == tmpheight && RowLast(row) == tmplast)
2860 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2862 status = LyXText::NEED_MORE_REFRESH;
2864 // check the special right address boxes
2865 if (textclasslist.Style(parameters->textclass,
2866 par->GetLayout()).margintype
2867 == MARGIN_RIGHT_ADDRESS_BOX) {
2868 tmpcursor.par = par;
2869 tmpcursor.row = row;
2872 tmpcursor.x_fix = 0;
2873 tmpcursor.pos = pos;
2874 RedoDrawingOfParagraph(tmpcursor);
2879 // set the cursor again. Otherwise dangling pointers are possible
2880 // also set the selection
2884 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2885 sel_cursor = cursor;
2886 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2887 sel_start_cursor = cursor;
2888 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2889 sel_end_cursor = cursor;
2890 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2891 last_sel_cursor = cursor;
2894 SetCursorIntern(cursor.par, cursor.pos);
2898 // returns 0 if inset wasn't found
2899 int LyXText::UpdateInset(Inset * inset)
2901 // first check the current paragraph
2902 int pos = cursor.par->GetPositionOfInset(inset);
2904 CheckParagraph(cursor.par, pos);
2908 // check every paragraph
2910 LyXParagraph * par = FirstParagraph();
2912 // make sure the paragraph is open
2913 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2914 pos = par->GetPositionOfInset(inset);
2916 CheckParagraph(par, pos);
2927 void LyXText::SetCursor(LyXParagraph * par,
2928 LyXParagraph::size_type pos, bool setfont) const
2930 LyXCursor old_cursor = cursor;
2931 SetCursorIntern(par, pos, setfont);
2932 DeleteEmptyParagraphMechanism(old_cursor);
2936 void LyXText::SetCursorIntern(LyXParagraph * par,
2937 LyXParagraph::size_type pos, bool setfont) const
2941 LyXParagraph * tmppar;
2942 LyXParagraph::size_type vpos,cursor_vpos;
2944 // correct the cursor position if impossible
2945 if (pos > par->Last()){
2946 tmppar = par->ParFromPos(pos);
2947 pos = par->PositionInParFromPos(pos);
2950 if (par->IsDummy() && par->previous &&
2951 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
2952 while (par->previous &&
2953 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
2954 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
2955 par = par->previous ;
2956 if (par->IsDummy() &&
2957 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
2958 pos += par->text.size() + 1;
2960 if (par->previous) {
2961 par = par->previous;
2963 pos += par->text.size() + 1;
2971 (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
2972 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
2973 && !cursor.par->IsSeparator(cursor.pos))
2974 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
2976 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
2977 real_current_font = GetFont(cursor.par, cursor.pos - 1);
2979 current_font = cursor.par->GetFontSettings(cursor.pos);
2980 real_current_font = GetFont(cursor.par, cursor.pos);
2981 if (pos == 0 && par->size() == 0
2982 && owner_->buffer()->params.getDocumentDirection() == LYX_DIR_RIGHT_TO_LEFT) {
2983 current_font.setDirection(LyXFont::RTL_DIR);
2984 real_current_font.setDirection(LyXFont::RTL_DIR);
2988 /* get the cursor y position in text */
2989 row = GetRow(par, pos, y);
2990 /* y is now the beginning of the cursor row */
2992 /* y is now the cursor baseline */
2995 /* now get the cursors x position */
2997 float fill_separator, fill_hfill, fill_label_hfill;
2998 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3000 LyXParagraph::size_type last = RowLast(row);
3001 if (row->pos > last)
3003 else if (pos <= last ) {
3004 LyXDirection letter_direction =
3005 row->par->getLetterDirection(pos);
3006 LyXDirection font_direction =
3007 real_current_font.getFontDirection();
3008 if (letter_direction == font_direction || pos == 0)
3009 cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3010 ? log2vis(pos) : log2vis(pos)+1;
3012 cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3013 ? log2vis(pos-1)+1 : log2vis(pos-1);
3015 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3016 ? log2vis(last)+1 : log2vis(last);
3018 /* table stuff -- begin*/
3019 if (row->par->table) {
3020 int cell = NumberOfCell(row->par, row->pos);
3022 x += row->par->table->GetBeginningOfTextInCell(cell);
3023 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3024 pos = vis2log(vpos);
3025 if (row->par->IsNewline(pos)) {
3026 x = x_old + row->par->table->WidthOfColumn(cell);
3029 x += row->par->table->GetBeginningOfTextInCell(cell);
3031 x += SingleWidth(row->par, pos);
3035 /* table stuff -- end*/
3036 LyXParagraph::size_type main_body =
3037 BeginningOfMainBody(row->par);
3038 if (main_body > 0 &&
3039 (main_body-1 > last ||
3040 !row->par->IsLineSeparator(main_body-1)))
3043 for (vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3044 pos = vis2log(vpos);
3045 if (main_body > 0 && pos == main_body-1) {
3046 x += fill_label_hfill +
3047 GetFont(row->par, -2).stringWidth(
3048 textclasslist.Style(parameters->textclass, row->par->GetLayout()).labelsep);
3049 if (row->par->IsLineSeparator(main_body-1))
3050 x -= SingleWidth(row->par, main_body-1);
3053 x += SingleWidth(row->par, pos);
3054 if (HfillExpansion(row, pos)) {
3055 if (pos >= main_body)
3058 x += fill_label_hfill;
3060 else if (pos >= main_body && row->par->IsSeparator(pos)) {
3068 cursor.x_fix = cursor.x;
3073 void LyXText::SetCursorFromCoordinates(int x, long y) const
3075 LyXCursor old_cursor = cursor;
3077 /* get the row first */
3079 Row * row = GetRowNearY(y);
3081 cursor.par = row->par;
3083 int column = GetColumnNearX(row, x);
3084 cursor.pos = row->pos + column;
3086 cursor.y = y + row->baseline;
3091 (cursor.pos == cursor.par->Last()
3092 || cursor.par->IsSeparator(cursor.pos)
3093 || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3094 && !cursor.par->IsSeparator(cursor.pos))
3095 || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3097 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3098 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3100 current_font = cursor.par->GetFontSettings(cursor.pos);
3101 real_current_font = GetFont(cursor.par, cursor.pos);
3103 DeleteEmptyParagraphMechanism(old_cursor);
3107 void LyXText::CursorLeft() const
3110 if (cursor.par->table) {
3111 int cell = NumberOfCell(cursor.par, cursor.pos);
3112 if (cursor.par->table->IsContRow(cell) &&
3113 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3120 void LyXText::CursorLeftIntern() const
3122 if (cursor.pos > 0) {
3123 SetCursor(cursor.par, cursor.pos - 1);
3125 else if (cursor.par->Previous()) {
3126 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3131 void LyXText::CursorRight() const
3133 CursorRightIntern();
3134 if (cursor.par->table) {
3135 int cell = NumberOfCell(cursor.par, cursor.pos);
3136 if (cursor.par->table->IsContRow(cell) &&
3137 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3144 void LyXText::CursorRightIntern() const
3146 if (cursor.pos < cursor.par->Last()) {
3147 SetCursor(cursor.par, cursor.pos + 1);
3149 else if (cursor.par->Next()) {
3150 SetCursor(cursor.par->Next(), 0);
3155 void LyXText::CursorUp() const
3157 SetCursorFromCoordinates(cursor.x_fix,
3158 cursor.y - cursor.row->baseline - 1);
3159 if (cursor.par->table) {
3160 int cell = NumberOfCell(cursor.par, cursor.pos);
3161 if (cursor.par->table->IsContRow(cell) &&
3162 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3169 void LyXText::CursorDown() const
3171 if (cursor.par->table &&
3172 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3175 SetCursorFromCoordinates(cursor.x_fix,
3176 cursor.y - cursor.row->baseline
3177 + cursor.row->height + 1);
3178 if (cursor.par->table) {
3179 int cell = NumberOfCell(cursor.par, cursor.pos);
3180 int cell_above = cursor.par->table->GetCellAbove(cell);
3181 while(cursor.par->table &&
3182 cursor.par->table->IsContRow(cell) &&
3183 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3184 SetCursorFromCoordinates(cursor.x_fix,
3185 cursor.y - cursor.row->baseline
3186 + cursor.row->height + 1);
3187 if (cursor.par->table) {
3188 cell = NumberOfCell(cursor.par, cursor.pos);
3189 cell_above = cursor.par->table->GetCellAbove(cell);
3196 void LyXText::CursorUpParagraph() const
3198 if (cursor.pos > 0) {
3199 SetCursor(cursor.par, 0);
3201 else if (cursor.par->Previous()) {
3202 SetCursor(cursor.par->Previous(), 0);
3207 void LyXText::CursorDownParagraph() const
3209 if (cursor.par->Next()) {
3210 SetCursor(cursor.par->Next(), 0);
3212 SetCursor(cursor.par, cursor.par->Last());
3218 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3220 bool deleted = false;
3222 // this is the delete-empty-paragraph-mechanism.
3223 if (selection) return;
3225 #ifdef FIX_DOUBLE_SPACE
3226 /* Ok I'll put some comments here about what is missing.
3227 I have fixed BackSpace (and thus Delete) to not delete
3228 double-spaces automagically. I have also changed Cut,
3229 Copy and Paste to hopefully do some sensible things.
3230 There are still some small problems that can lead to
3231 double spaces stored in the document file or space at
3232 the beginning of paragraphs. This happens if you have
3233 the cursor betwenn to spaces and then save. Or if you
3234 cut and paste and the selection have a space at the
3235 beginning and then save right after the paste. I am
3236 sure none of these are very hard to fix, but I will
3237 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3238 that I can get some feedback. (Lgb)
3241 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3242 // delete the LineSeparator.
3245 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3246 // delete the LineSeparator.
3249 // If the pos around the old_cursor were spaces, delete one of them.
3250 if (!(old_cursor.par == cursor.par && old_cursor.pos == cursor.pos)
3251 && old_cursor.pos > 0
3252 && old_cursor.pos < old_cursor.par->Last()
3253 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3254 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3255 old_cursor.par->Erase(old_cursor.pos - 1);
3256 RedoParagraphs(old_cursor, old_cursor.par->Next());
3257 // or RedoDrawingOfParagraph(old_cursor);
3259 if (old_cursor.par == cursor.par &&
3260 cursor.pos > old_cursor.pos)
3261 SetCursor(cursor.par, cursor.pos - 1);
3263 SetCursor(cursor.par, cursor.pos);
3268 // Paragraph should not be deleted if empty
3269 if ((textclasslist.Style(parameters->textclass,
3270 old_cursor.par->GetLayout())).keepempty)
3273 LyXCursor tmpcursor;
3275 if (old_cursor.par != cursor.par) {
3276 if ( (old_cursor.par->Last() == 0
3277 || (old_cursor.par->Last() == 1
3278 && (old_cursor.par->IsLineSeparator(0))))
3279 && old_cursor.par->FirstPhysicalPar()
3280 == old_cursor.par->LastPhysicalPar()) {
3282 // ok, we will delete anything
3284 // make sure that you do not delete any environments
3285 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3286 !(old_cursor.row->previous
3287 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3288 && !(old_cursor.row->next
3289 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3291 (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE &&
3292 ((old_cursor.row->previous
3293 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3295 (old_cursor.row->next
3296 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3298 status = LyXText::NEED_MORE_REFRESH;
3301 if (old_cursor.row->previous) {
3302 refresh_row = old_cursor.row->previous;
3303 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3305 cursor = old_cursor; // that undo can restore the right cursor position
3306 LyXParagraph * endpar = old_cursor.par->next;
3307 if (endpar && endpar->GetDepth()) {
3308 while (endpar && endpar->GetDepth()) {
3309 endpar = endpar->LastPhysicalPar()->Next();
3312 SetUndo(Undo::DELETE,
3313 old_cursor.par->previous,
3318 RemoveRow(old_cursor.row);
3319 if (params->paragraph == old_cursor.par) {
3320 params->paragraph = params->paragraph->next;
3323 delete old_cursor.par;
3325 /* Breakagain the next par. Needed
3326 * because of the parindent that
3327 * can occur or dissappear. The
3328 * next row can change its height,
3329 * if there is another layout before */
3330 if (refresh_row->next) {
3331 BreakAgain(refresh_row->next);
3332 UpdateCounters(refresh_row);
3334 SetHeightOfRow(refresh_row);
3336 refresh_row = old_cursor.row->next;
3337 refresh_y = old_cursor.y - old_cursor.row->baseline;
3340 cursor = old_cursor; // that undo can restore the right cursor position
3341 LyXParagraph *endpar = old_cursor.par->next;
3342 if (endpar && endpar->GetDepth()) {
3343 while (endpar && endpar->GetDepth()) {
3344 endpar = endpar->LastPhysicalPar()->Next();
3347 SetUndo(Undo::DELETE,
3348 old_cursor.par->previous,
3353 RemoveRow(old_cursor.row);
3355 if (params->paragraph == old_cursor.par) {
3356 params->paragraph = params->paragraph->next;
3358 delete old_cursor.par;
3360 /* Breakagain the next par. Needed
3361 because of the parindent that can
3362 occur or dissappear.
3363 The next row can change its height,
3364 if there is another layout before
3367 BreakAgain(refresh_row);
3368 UpdateCounters(refresh_row->previous);
3373 SetCursor(cursor.par, cursor.pos);
3375 /* if (cursor.y > old_cursor.y)
3376 cursor.y -= old_cursor.row->height; */
3378 if (sel_cursor.par == old_cursor.par
3379 && sel_cursor.pos == sel_cursor.pos) {
3380 // correct selection
3381 sel_cursor = cursor;
3386 if (old_cursor.par->ClearParagraph()){
3387 RedoParagraphs(old_cursor, old_cursor.par->Next());
3389 SetCursor(cursor.par, cursor.pos);
3390 sel_cursor = cursor;
3397 LyXParagraph * LyXText::GetParFromID(int id)
3399 LyXParagraph * result = FirstParagraph();
3400 while (result && result->id() != id)
3401 result = result->next;
3407 bool LyXText::TextUndo()
3409 // returns false if no undo possible
3410 Undo * undo = params->undostack.pop();
3415 .push(CreateUndo(undo->kind,
3416 GetParFromID(undo->number_of_before_par),
3417 GetParFromID(undo->number_of_behind_par)));
3419 return TextHandleUndo(undo);
3423 bool LyXText::TextRedo()
3425 // returns false if no redo possible
3426 Undo * undo = params->redostack.pop();
3431 .push(CreateUndo(undo->kind,
3432 GetParFromID(undo->number_of_before_par),
3433 GetParFromID(undo->number_of_behind_par)));
3435 return TextHandleUndo(undo);
3439 bool LyXText::TextHandleUndo(Undo * undo)
3441 // returns false if no undo possible
3442 bool result = false;
3444 LyXParagraph * before =
3445 GetParFromID(undo->number_of_before_par);
3446 LyXParagraph * behind =
3447 GetParFromID(undo->number_of_behind_par);
3448 LyXParagraph * tmppar;
3449 LyXParagraph * tmppar2;
3450 LyXParagraph * tmppar3;
3451 LyXParagraph * tmppar4;
3452 LyXParagraph * endpar;
3453 LyXParagraph * tmppar5;
3455 // if there's no before take the beginning
3456 // of the document for redoing
3458 SetCursorIntern(FirstParagraph(), 0);
3460 // replace the paragraphs with the undo informations
3462 tmppar3 = undo->par;
3463 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3466 while (tmppar4->next)
3467 tmppar4 = tmppar4->next;
3468 } // get last undo par
3470 // now remove the old text if there is any
3471 if (before != behind || (!behind && !before)){
3473 tmppar5 = before->next;
3475 tmppar5 = params->paragraph;
3477 while (tmppar5 && tmppar5 != behind){
3479 tmppar5 = tmppar5->next;
3480 // a memory optimization for edit: Only layout information
3481 // is stored in the undo. So restore the text informations.
3482 if (undo->kind == Undo::EDIT){
3483 tmppar2->text = tmppar->text;
3484 tmppar->text.clear();
3485 tmppar2 = tmppar2->next;
3487 if ( currentrow && currentrow->par == tmppar )
3488 currentrow = currentrow -> previous;
3489 // Commenting out this might remove the error
3490 // reported by Purify, but it might also
3491 // introduce a memory leak. We need to
3497 // put the new stuff in the list if there is one
3500 before->next = tmppar3;
3502 params->paragraph = tmppar3;
3503 tmppar3->previous = before;
3507 params->paragraph = behind;
3510 tmppar4->next = behind;
3512 behind->previous = tmppar4;
3516 // Set the cursor for redoing
3518 SetCursorIntern(before->FirstSelfrowPar(), 0);
3519 // check wether before points to a closed float and open it if necessary
3520 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3521 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3523 while (tmppar4->previous &&
3524 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3525 tmppar4 = tmppar4->previous;
3526 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3527 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3528 tmppar4 = tmppar4->next;
3533 // open a cosed footnote at the end if necessary
3534 if (behind && behind->previous &&
3535 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3536 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3537 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3538 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3539 behind = behind->next;
3543 // calculate the endpar for redoing the paragraphs.
3545 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3546 endpar = behind->LastPhysicalPar()->Next();
3548 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3553 tmppar = GetParFromID(undo->number_of_cursor_par);
3554 RedoParagraphs(cursor, endpar);
3556 SetCursorIntern(tmppar, undo->cursor_pos);
3557 UpdateCounters(cursor.row);
3567 void LyXText::FinishUndo()
3569 // makes sure the next operation will be stored
3570 undo_finished = True;
3574 void LyXText::FreezeUndo()
3576 // this is dangerous and for internal use only
3581 void LyXText::UnFreezeUndo()
3583 // this is dangerous and for internal use only
3584 undo_frozen = false;
3588 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3589 LyXParagraph const * behind) const
3592 params->undostack.push(CreateUndo(kind, before, behind));
3593 params->redostack.clear();
3597 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3598 LyXParagraph const * behind)
3600 params->redostack.push(CreateUndo(kind, before, behind));
3604 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3605 LyXParagraph const * behind) const
3607 int before_number = -1;
3608 int behind_number = -1;
3610 before_number = before->id();
3612 behind_number = behind->id();
3613 // Undo::EDIT and Undo::FINISH are
3614 // always finished. (no overlapping there)
3615 // overlapping only with insert and delete inside one paragraph:
3616 // Nobody wants all removed character
3617 // appear one by one when undoing.
3618 // EDIT is special since only layout information, not the
3619 // contents of a paragaph are stored.
3620 if (!undo_finished && kind != Undo::EDIT &&
3621 kind != Undo::FINISH){
3622 // check wether storing is needed
3623 if (!params->undostack.empty() &&
3624 params->undostack.top()->kind == kind &&
3625 params->undostack.top()->number_of_before_par == before_number &&
3626 params->undostack.top()->number_of_behind_par == behind_number ){
3631 // create a new Undo
3632 LyXParagraph * undopar;
3633 LyXParagraph * tmppar;
3634 LyXParagraph * tmppar2;
3636 LyXParagraph * start = 0;
3637 LyXParagraph * end = 0;
3640 start = before->next;
3642 start = FirstParagraph();
3644 end = behind->previous;
3646 end = FirstParagraph();
3652 && start != end->next
3653 && (before != behind || (!before && !behind))) {
3655 tmppar2 = tmppar->Clone();
3656 tmppar2->id(tmppar->id());
3658 // a memory optimization: Just store the layout information
3660 if (kind == Undo::EDIT){
3661 tmppar2->text.clear();
3666 while (tmppar != end && tmppar->next) {
3667 tmppar = tmppar->next;
3668 tmppar2->next = tmppar->Clone();
3669 tmppar2->next->id(tmppar->id());
3670 // a memory optimization: Just store the layout
3671 // information when only edit
3672 if (kind == Undo::EDIT){
3673 tmppar2->next->text.clear();
3675 tmppar2->next->previous = tmppar2;
3676 tmppar2 = tmppar2->next;
3680 undopar = 0; // nothing to replace (undo of delete maybe)
3682 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3683 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
3685 Undo * undo = new Undo(kind,
3686 before_number, behind_number,
3687 cursor_par, cursor_pos,
3690 undo_finished = false;
3695 void LyXText::SetCursorParUndo()
3697 SetUndo(Undo::FINISH,
3698 cursor.par->ParFromPos(cursor.pos)->previous,
3699 cursor.par->ParFromPos(cursor.pos)->next);
3703 void LyXText::RemoveTableRow(LyXCursor * cur) const
3709 // move to the previous row
3710 int cell_act = NumberOfCell(cur->par, cur->pos);
3713 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3716 !cur->par->table->IsFirstCell(cell_act)) {
3718 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3723 // now we have to pay attention if the actual table is the
3724 // main row of TableContRows and if yes to delete all of them
3729 // delete up to the next row
3730 while (cur->pos < cur->par->Last() &&
3732 || !cur->par->table->IsFirstCell(cell_act))) {
3733 while (cur->pos < cur->par->Last() &&
3734 !cur->par->IsNewline(cur->pos))
3735 cur->par->Erase(cur->pos);
3738 if (cur->pos < cur->par->Last())
3739 cur->par->Erase(cur->pos);
3741 if (cur->pos && cur->pos == cur->par->Last()) {
3743 cur->par->Erase(cur->pos); // no newline at very end!
3745 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3746 !cur->par->table->IsContRow(cell_org) &&
3747 cur->par->table->IsContRow(cell));
3748 cur->par->table->DeleteRow(cell_org);
3753 bool LyXText::IsEmptyTableCell() const
3755 LyXParagraph::size_type pos = cursor.pos - 1;
3756 while (pos >= 0 && pos < cursor.par->Last()
3757 && !cursor.par->IsNewline(pos))
3759 return cursor.par->IsNewline(pos + 1);
3763 void LyXText::toggleAppendix(){
3764 LyXParagraph * par = cursor.par->FirstPhysicalPar();
3765 bool start = !par->start_of_appendix;
3767 // ensure that we have only one start_of_appendix in this document
3768 LyXParagraph * tmp = FirstParagraph();
3769 for (; tmp; tmp = tmp->next)
3770 tmp->start_of_appendix = 0;
3771 par->start_of_appendix = start;
3773 // we can set the refreshing parameters now
3774 status = LyXText::NEED_MORE_REFRESH;
3776 refresh_row = 0; // not needed for full update
3778 SetCursor(cursor.par, cursor.pos);