1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
13 #include FORMS_H_LOCATION
17 #pragma implementation "lyxtext.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
27 #include "support/textutils.h"
29 #include "minibuffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
35 #include "BufferView.h"
38 #include "CutAndPaste.h"
43 //#define USE_OLD_CUT_AND_PASTE 1
49 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
58 status = LyXText::UNCHANGED;
59 LyXParagraph * par = p->paragraph;
60 current_font = GetFont(par, 0);
65 InsertParagraph(par, lastrow);
69 // set cursor at the very top position
70 selection = true; /* these setting is necessary
71 because of the delete-empty-
72 paragraph mechanism in
74 SetCursor(firstrow->par, 0);
79 // no rebreak necessary
85 // Default layouttype for copy environment type
89 // Dump all rowinformation:
90 Row * tmprow = firstrow;
91 lyxerr << "Baseline Paragraph Pos Height Ascent Fill\n";
93 lyxerr << tmprow->baseline << '\t'
94 << tmprow->par << '\t'
95 << tmprow->pos << '\t'
96 << tmprow->height << '\t'
97 << tmprow->ascent_of_text << '\t'
98 << tmprow->fill << '\n';
99 tmprow = tmprow->next;
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next;
118 void LyXText::owner(BufferView * bv)
120 if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
124 // Gets the fully instantiated font at a given position in a paragraph
125 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
126 // The difference is that this one is used for displaying, and thus we
127 // are allowed to make cosmetic improvements. For instance make footnotes
129 // If position is -1, we get the layout font of the paragraph.
130 // If position is -2, we get the font of the manual label of the paragraph.
131 LyXFont LyXText::GetFont(LyXParagraph * par,
132 LyXParagraph::size_type pos) const
134 LyXLayout const & layout =
135 textclasslist.Style(buffer->params.textclass,
138 char par_depth = par->GetDepth();
139 // We specialize the 95% common case:
140 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
143 if (layout.labeltype == LABEL_MANUAL
144 && pos < BeginningOfMainBody(par)) {
146 return par->GetFontSettings(pos).
147 realize(layout.reslabelfont);
149 return par->GetFontSettings(pos).
150 realize(layout.resfont);
153 // process layoutfont for pos == -1 and labelfont for pos < -1
155 return layout.resfont;
157 return layout.reslabelfont;
161 // The uncommon case need not be optimized as much
163 LyXFont layoutfont, tmpfont;
167 if (pos < BeginningOfMainBody(par)) {
169 layoutfont = layout.labelfont;
172 layoutfont = layout.font;
174 tmpfont = par->GetFontSettings(pos);
175 tmpfont.realize(layoutfont);
178 // process layoutfont for pos == -1 and labelfont for pos < -1
180 tmpfont = layout.font;
182 tmpfont = layout.labelfont;
185 // Resolve against environment font information
186 while (par && par_depth && !tmpfont.resolved()) {
187 par = par->DepthHook(par_depth - 1);
189 tmpfont.realize(textclasslist.
190 Style(buffer->params.textclass,
191 par->GetLayout()).font);
192 par_depth = par->GetDepth();
196 tmpfont.realize(textclasslist.TextClass(buffer->params.textclass).defaultfont());
198 // Cosmetic improvement: If this is an open footnote, make the font
200 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
201 && par->footnotekind == LyXParagraph::FOOTNOTE) {
209 void LyXText::SetCharFont(LyXParagraph * par,
210 LyXParagraph::size_type pos,
214 // Let the insets convert their font
215 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
216 if (par->GetInset(pos))
217 font = par->GetInset(pos)->ConvertFont(font);
220 LyXLayout const & layout =
221 textclasslist.Style(buffer->params.textclass,
224 // Get concrete layout font to reduce against
227 if (pos < BeginningOfMainBody(par))
228 layoutfont = layout.labelfont;
230 layoutfont = layout.font;
232 // Realize against environment font information
233 if (par->GetDepth()){
234 LyXParagraph * tp = par;
235 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
236 tp = tp->DepthHook(tp->GetDepth()-1);
238 layoutfont.realize(textclasslist.
239 Style(buffer->params.textclass,
240 tp->GetLayout()).font);
244 layoutfont.realize(textclasslist.TextClass(buffer->params.textclass).defaultfont());
246 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
247 && par->footnotekind == LyXParagraph::FOOTNOTE) {
248 layoutfont.decSize();
251 // Now, reduce font against full layout font
252 font.reduce(layoutfont);
254 par->SetFont(pos, font);
258 /* inserts a new row behind the specified row, increments
259 * the touched counters */
260 void LyXText::InsertRow(Row * row, LyXParagraph * par,
261 LyXParagraph::size_type pos) const
263 Row * tmprow = new Row;
265 tmprow->previous = 0;
266 tmprow->next = firstrow;
269 tmprow->previous = row;
270 tmprow->next = row->next;
275 tmprow->next->previous = tmprow;
277 if (tmprow->previous)
278 tmprow->previous->next = tmprow;
286 ++number_of_rows; // one more row
290 // removes the row and reset the touched counters
291 void LyXText::RemoveRow(Row * row) const
293 /* this must not happen before the currentrow for clear reasons.
294 so the trick is just to set the current row onto the previous
297 GetRow(row->par, row->pos, unused_y);
300 row->next->previous = row->previous;
301 if (!row->previous) {
302 firstrow = row->next;
304 row->previous->next = row->next;
307 lastrow = row->previous;
309 height -= row->height; // the text becomes smaller
312 --number_of_rows; // one row less
316 // remove all following rows of the paragraph of the specified row.
317 void LyXText::RemoveParagraph(Row * row) const
319 LyXParagraph * tmppar = row->par;
323 while (row && row->par == tmppar) {
331 // insert the specified paragraph behind the specified row
332 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
334 InsertRow(row, par, 0); /* insert a new row, starting
337 SetCounter(par); // set the counters
339 // and now append the whole paragraph behind the new row
341 firstrow->height = 0;
342 AppendParagraph(firstrow);
344 row->next->height = 0;
345 AppendParagraph(row->next);
350 void LyXText::ToggleFootnote()
352 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
354 && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
356 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
358 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
364 void LyXText::OpenStuff()
366 if (cursor.pos == 0 && cursor.par->bibkey){
367 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
369 else if (cursor.pos < cursor.par->Last()
370 && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
371 && cursor.par->GetInset(cursor.pos)->Editable()) {
372 owner_->owner()->getMiniBuffer()
373 ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
374 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
376 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
383 void LyXText::CloseFootnote()
385 LyXParagraph * tmppar;
386 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
388 // if the cursor is not in an open footnote, or
389 // there is no open footnote in this paragraph, just return.
390 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
393 par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
394 owner_->owner()->getMiniBuffer()
395 ->Set(_("Nothing to do"));
399 // ok, move the cursor right before the footnote
400 // just a little faster than using CursorRight()
402 cursor.par->ParFromPos(cursor.pos) != par;
406 // now the cursor is at the beginning of the physical par
407 SetCursor(cursor.par,
409 cursor.par->ParFromPos(cursor.pos)->size());
411 /* we are in a footnote, so let us move at the beginning */
412 /* this is just faster than using just CursorLeft() */
415 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
416 // just a little bit faster than movin the cursor
417 tmppar = tmppar->Previous();
419 SetCursor(tmppar, tmppar->Last());
422 // the cursor must be exactly before the footnote
423 par = cursor.par->ParFromPos(cursor.pos);
425 status = LyXText::NEED_MORE_REFRESH;
426 refresh_row = cursor.row;
427 refresh_y = cursor.y - cursor.row->baseline;
430 LyXParagraph * endpar = par->NextAfterFootnote()->Next();
431 Row * row = cursor.row;
433 tmppar->CloseFootnote(cursor.pos);
435 while (tmppar != endpar) {
436 RemoveRow(row->next);
438 tmppar = row->next->par;
443 AppendParagraph(cursor.row);
445 SetCursor(cursor.par, cursor.pos);
449 if (cursor.row->next)
450 SetHeightOfRow(cursor.row->next);
454 /* used in setlayout */
455 // Asger is not sure we want to do this...
456 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
459 LyXLayout const & layout =
460 textclasslist.Style(buffer->params.textclass,
463 LyXFont layoutfont, tmpfont;
464 for (LyXParagraph::size_type pos = 0;
465 pos < par->Last(); ++pos) {
466 if (pos < BeginningOfMainBody(par))
467 layoutfont = layout.labelfont;
469 layoutfont = layout.font;
471 tmpfont = par->GetFontSettings(pos);
472 tmpfont.reduce(layoutfont);
473 par->SetFont(pos, tmpfont);
477 LyXParagraph * LyXText::SetLayout(LyXCursor & cur, LyXCursor & sstart_cur,
478 LyXCursor & send_cur,
479 LyXTextClass::size_type layout)
481 LyXParagraph * endpar = send_cur.par->LastPhysicalPar()->Next();
482 LyXParagraph * undoendpar = endpar;
484 if (endpar && endpar->GetDepth()) {
485 while (endpar && endpar->GetDepth()) {
486 endpar = endpar->LastPhysicalPar()->Next();
490 endpar = endpar->Next(); // because of parindents etc.
494 sstart_cur.par->ParFromPos(sstart_cur.pos)->previous,
497 /* ok we have a selection. This is always between sstart_cur
498 * and sel_end cursor */
501 LyXLayout const & lyxlayout =
502 textclasslist.Style(buffer->params.textclass, layout);
504 while (cur.par != send_cur.par) {
505 if (cur.par->footnoteflag == sstart_cur.par->footnoteflag) {
506 cur.par->SetLayout(layout);
507 MakeFontEntriesLayoutSpecific(cur.par);
508 LyXParagraph* fppar = cur.par->FirstPhysicalPar();
509 fppar->added_space_top = lyxlayout.fill_top ?
510 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
511 fppar->added_space_bottom = lyxlayout.fill_bottom ?
512 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
513 if (lyxlayout.margintype == MARGIN_MANUAL)
514 cur.par->SetLabelWidthString(lyxlayout.labelstring());
515 if (lyxlayout.labeltype != LABEL_BIBLIO
517 delete fppar->bibkey;
521 cur.par = cur.par->Next();
523 if (cur.par->footnoteflag == sstart_cur.par->footnoteflag) {
524 cur.par->SetLayout(layout);
525 MakeFontEntriesLayoutSpecific(cur.par);
526 LyXParagraph* fppar = cur.par->FirstPhysicalPar();
527 fppar->added_space_top = lyxlayout.fill_top ?
528 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
529 fppar->added_space_bottom = lyxlayout.fill_bottom ?
530 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
531 if (lyxlayout.margintype == MARGIN_MANUAL)
532 cur.par->SetLabelWidthString(lyxlayout.labelstring());
533 if (lyxlayout.labeltype != LABEL_BIBLIO
535 delete fppar->bibkey;
542 // set layout over selection and make a total rebreak of those paragraphs
543 void LyXText::SetLayout(LyXTextClass::size_type layout)
546 tmpcursor = cursor; /* store the current cursor */
548 #ifdef USE_OLD_SET_LAYOUT
549 // if there is no selection just set the layout
550 // of the current paragraph */
552 sel_start_cursor = cursor; // dummy selection
553 sel_end_cursor = cursor;
556 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
557 LyXParagraph * undoendpar = endpar;
559 if (endpar && endpar->GetDepth()) {
560 while (endpar && endpar->GetDepth()) {
561 endpar = endpar->LastPhysicalPar()->Next();
566 endpar = endpar->Next(); // because of parindents etc.
570 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
573 /* ok we have a selection. This is always between sel_start_cursor
574 * and sel_end cursor */
575 cursor = sel_start_cursor;
577 LyXLayout const & lyxlayout =
578 textclasslist.Style(buffer->params.textclass, layout);
580 while (cursor.par != sel_end_cursor.par) {
581 if (cursor.par->footnoteflag ==
582 sel_start_cursor.par->footnoteflag) {
583 cursor.par->SetLayout(layout);
584 MakeFontEntriesLayoutSpecific(cursor.par);
585 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
586 fppar->added_space_top = lyxlayout.fill_top ?
587 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
588 fppar->added_space_bottom = lyxlayout.fill_bottom ?
589 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
590 if (lyxlayout.margintype == MARGIN_MANUAL)
591 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
592 if (lyxlayout.labeltype != LABEL_BIBLIO
594 delete fppar->bibkey;
598 cursor.par = cursor.par->Next();
600 if (cursor.par->footnoteflag ==
601 sel_start_cursor.par->footnoteflag) {
602 cursor.par->SetLayout(layout);
603 MakeFontEntriesLayoutSpecific(cursor.par);
604 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
605 fppar->added_space_top = lyxlayout.fill_top ?
606 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
607 fppar->added_space_bottom = lyxlayout.fill_bottom ?
608 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
609 if (lyxlayout.margintype == MARGIN_MANUAL)
610 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
611 if (lyxlayout.labeltype != LABEL_BIBLIO
613 delete fppar->bibkey;
618 // if there is no selection just set the layout
619 // of the current paragraph */
621 sel_start_cursor = cursor; // dummy selection
622 sel_end_cursor = cursor;
625 endpar = SetLayout(cursor, sel_start_cursor, sel_end_cursor, layout);
627 RedoParagraphs(sel_start_cursor, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed */
631 SetCursor(sel_start_cursor.par, sel_start_cursor.pos, false);
633 SetCursor(sel_end_cursor.par, sel_end_cursor.pos, false);
634 UpdateCounters(cursor.row);
637 SetCursor(tmpcursor.par, tmpcursor.pos, true);
641 // increment depth over selection and
642 // make a total rebreak of those paragraphs
643 void LyXText::IncDepth()
645 // If there is no selection, just use the current paragraph
647 sel_start_cursor = cursor; // dummy selection
648 sel_end_cursor = cursor;
651 // We end at the next paragraph with depth 0
652 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
653 LyXParagraph * undoendpar = endpar;
655 if (endpar && endpar->GetDepth()) {
656 while (endpar && endpar->GetDepth()) {
657 endpar = endpar->LastPhysicalPar()->Next();
662 endpar = endpar->Next(); // because of parindents etc.
667 .par->ParFromPos(sel_start_cursor.pos)->previous,
670 LyXCursor tmpcursor = cursor; // store the current cursor
672 // ok we have a selection. This is always between sel_start_cursor
673 // and sel_end cursor
674 cursor = sel_start_cursor;
676 bool anything_changed = false;
679 // NOTE: you can't change the depth of a bibliography entry
680 if (cursor.par->footnoteflag ==
681 sel_start_cursor.par->footnoteflag
682 && textclasslist.Style(buffer->params.textclass,
683 cursor.par->GetLayout()
684 ).labeltype != LABEL_BIBLIO) {
685 LyXParagraph * prev =
686 cursor.par->FirstPhysicalPar()->Previous();
688 && (prev->GetDepth() - cursor.par->GetDepth() > 0
689 || (prev->GetDepth() == cursor.par->GetDepth()
690 && textclasslist.Style(buffer->params.textclass,
691 prev->GetLayout()).isEnvironment()))) {
692 cursor.par->FirstPhysicalPar()->depth++;
693 anything_changed = true;
696 if (cursor.par == sel_end_cursor.par)
698 cursor.par = cursor.par->Next();
701 // if nothing changed set all depth to 0
702 if (!anything_changed) {
703 cursor = sel_start_cursor;
704 while (cursor.par != sel_end_cursor.par) {
705 cursor.par->FirstPhysicalPar()->depth = 0;
706 cursor.par = cursor.par->Next();
708 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
709 cursor.par->FirstPhysicalPar()->depth = 0;
712 RedoParagraphs(sel_start_cursor, endpar);
714 // we have to reset the selection, because the
715 // geometry could have changed
716 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
718 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
719 UpdateCounters(cursor.row);
722 SetCursor(tmpcursor.par, tmpcursor.pos);
726 // decrement depth over selection and
727 // make a total rebreak of those paragraphs
728 void LyXText::DecDepth()
730 // if there is no selection just set the layout
731 // of the current paragraph
733 sel_start_cursor = cursor; // dummy selection
734 sel_end_cursor = cursor;
737 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
738 LyXParagraph * undoendpar = endpar;
740 if (endpar && endpar->GetDepth()) {
741 while (endpar && endpar->GetDepth()) {
742 endpar = endpar->LastPhysicalPar()->Next();
747 endpar = endpar->Next(); // because of parindents etc.
752 .par->ParFromPos(sel_start_cursor.pos)->previous,
755 LyXCursor tmpcursor = cursor; // store the current cursor
757 // ok we have a selection. This is always between sel_start_cursor
758 // and sel_end cursor
759 cursor = sel_start_cursor;
762 if (cursor.par->footnoteflag ==
763 sel_start_cursor.par->footnoteflag) {
764 if (cursor.par->FirstPhysicalPar()->depth)
765 cursor.par->FirstPhysicalPar()->depth--;
767 if (cursor.par == sel_end_cursor.par)
769 cursor.par = cursor.par->Next();
772 RedoParagraphs(sel_start_cursor, endpar);
774 // we have to reset the selection, because the
775 // geometry could have changed
776 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
778 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
779 UpdateCounters(cursor.row);
782 SetCursor(tmpcursor.par, tmpcursor.pos);
786 // set font over selection and make a total rebreak of those paragraphs
787 void LyXText::SetFont(LyXFont const & font, bool toggleall)
789 // if there is no selection just set the current_font
791 // Determine basis font
793 if (cursor.pos < BeginningOfMainBody(cursor.par))
794 layoutfont = GetFont(cursor.par, -2);
796 layoutfont = GetFont(cursor.par, -1);
797 // Update current font
798 real_current_font.update(font,
799 buffer->params.language_info,
802 // Reduce to implicit settings
803 current_font = real_current_font;
804 current_font.reduce(layoutfont);
805 // And resolve it completely
806 real_current_font.realize(layoutfont);
810 LyXCursor tmpcursor = cursor; // store the current cursor
812 // ok we have a selection. This is always between sel_start_cursor
813 // and sel_end cursor
816 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
817 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
818 cursor = sel_start_cursor;
819 while (cursor.par != sel_end_cursor.par ||
820 (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
821 && cursor.pos < sel_end_cursor.pos))
823 if (cursor.pos < cursor.par->Last()
824 && cursor.par->footnoteflag
825 == sel_start_cursor.par->footnoteflag) {
826 // an open footnote should behave
828 LyXFont newfont = GetFont(cursor.par, cursor.pos);
830 buffer->params.language_info,
832 SetCharFont(cursor.par, cursor.pos, newfont);
836 cursor.par = cursor.par->Next();
840 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
842 // we have to reset the selection, because the
843 // geometry could have changed
844 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
846 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
849 SetCursor(tmpcursor.par, tmpcursor.pos, true, tmpcursor.boundary);
853 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
855 Row * tmprow = cur.row;
856 long y = cur.y - tmprow->baseline;
858 SetHeightOfRow(tmprow);
859 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
860 // find the first row of the paragraph
861 if (first_phys_par != tmprow->par)
862 while (tmprow->previous
863 && tmprow->previous->par != first_phys_par) {
864 tmprow = tmprow->previous;
866 SetHeightOfRow(tmprow);
868 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
869 tmprow = tmprow->previous;
871 SetHeightOfRow(tmprow);
874 // we can set the refreshing parameters now
875 status = LyXText::NEED_MORE_REFRESH;
877 refresh_row = tmprow;
878 SetCursor(cur.par, cur.pos, false, cursor.boundary);
882 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
884 Row * tmprow = cur.row;
886 long y = cur.y - tmprow->baseline;
887 SetHeightOfRow(tmprow);
888 LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
889 // find the first row of the paragraph
890 if (first_phys_par != tmprow->par)
891 while (tmprow->previous && tmprow->previous->par != first_phys_par) {
892 tmprow = tmprow->previous;
895 while (tmprow->previous && tmprow->previous->par == first_phys_par) {
896 tmprow = tmprow->previous;
900 // we can set the refreshing parameters now
901 if (status == LyXText::UNCHANGED || y < refresh_y) {
903 refresh_row = tmprow;
905 status = LyXText::NEED_MORE_REFRESH;
906 SetCursor(cur.par, cur.pos);
910 /* deletes and inserts again all paragaphs between the cursor
911 * and the specified par
912 * This function is needed after SetLayout and SetFont etc. */
913 void LyXText::RedoParagraphs(LyXCursor const & cur,
914 LyXParagraph const * endpar) const
917 LyXParagraph * tmppar, * first_phys_par;
919 Row * tmprow = cur.row;
921 long y = cur.y - tmprow->baseline;
923 if (!tmprow->previous){
924 first_phys_par = FirstParagraph(); // a trick/hack for UNDO
926 first_phys_par = tmprow->par->FirstPhysicalPar();
927 // find the first row of the paragraph
928 if (first_phys_par != tmprow->par)
929 while (tmprow->previous &&
930 (tmprow->previous->par != first_phys_par)) {
931 tmprow = tmprow->previous;
934 while (tmprow->previous
935 && tmprow->previous->par == first_phys_par) {
936 tmprow = tmprow->previous;
941 // we can set the refreshing parameters now
942 status = LyXText::NEED_MORE_REFRESH;
944 refresh_row = tmprow->previous; /* the real refresh row will
945 be deleted, so I store
949 tmppar = tmprow->next->par;
952 while (tmppar != endpar) {
953 RemoveRow(tmprow->next);
955 tmppar = tmprow->next->par;
960 // remove the first one
961 tmprow2 = tmprow; /* this is because tmprow->previous
963 tmprow = tmprow->previous;
966 tmppar = first_phys_par;
970 InsertParagraph(tmppar, tmprow);
973 while (tmprow->next && tmprow->next->par == tmppar)
974 tmprow = tmprow->next;
975 tmppar = tmppar->Next();
977 } while (tmppar != endpar);
979 // this is because of layout changes
981 refresh_y -= refresh_row->height;
982 SetHeightOfRow(refresh_row);
984 refresh_row = firstrow;
986 SetHeightOfRow(refresh_row);
989 if (tmprow && tmprow->next)
990 SetHeightOfRow(tmprow->next);
994 bool LyXText::FullRebreak()
996 if (need_break_row) {
997 BreakAgain(need_break_row);
1005 /* important for the screen */
1008 /* the cursor set functions have a special mechanism. When they
1009 * realize, that you left an empty paragraph, they will delete it.
1010 * They also delete the corresponding row */
1012 // need the selection cursor:
1013 void LyXText::SetSelection()
1016 last_sel_cursor = sel_cursor;
1017 sel_start_cursor = sel_cursor;
1018 sel_end_cursor = sel_cursor;
1023 // first the toggling area
1024 if (cursor.y < last_sel_cursor.y
1025 || (cursor.y == last_sel_cursor.y
1026 && cursor.x < last_sel_cursor.x)) {
1027 toggle_end_cursor = last_sel_cursor;
1028 toggle_cursor = cursor;
1030 toggle_end_cursor = cursor;
1031 toggle_cursor = last_sel_cursor;
1034 last_sel_cursor = cursor;
1036 // and now the whole selection
1038 if (sel_cursor.par == cursor.par)
1039 if (sel_cursor.pos < cursor.pos) {
1040 sel_end_cursor = cursor;
1041 sel_start_cursor = sel_cursor;
1043 sel_end_cursor = sel_cursor;
1044 sel_start_cursor = cursor;
1046 else if (sel_cursor.y < cursor.y ||
1047 (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
1048 sel_end_cursor = cursor;
1049 sel_start_cursor = sel_cursor;
1052 sel_end_cursor = sel_cursor;
1053 sel_start_cursor = cursor;
1056 // a selection with no contents is not a selection
1057 if (sel_start_cursor.par == sel_end_cursor.par &&
1058 sel_start_cursor.pos == sel_end_cursor.pos)
1061 // Stuff what we got on the clipboard. Even if there is no selection.
1063 // There is a problem with having the stuffing here in that the
1064 // larger the selection the slower LyX will get. This can be
1065 // solved by running the line below only when the selection has
1066 // finished. The solution used currently just works, to make it
1067 // faster we need to be more clever and probably also have more
1068 // calls to stuffClipboard. (Lgb)
1069 owner_->stuffClipboard(selectionAsString());
1073 string LyXText::selectionAsString() const
1075 if (!selection) return string();
1078 // Special handling if the whole selection is within one paragraph
1079 if (sel_start_cursor.par == sel_end_cursor.par) {
1080 result += sel_start_cursor.par->String(sel_start_cursor.pos,
1081 sel_end_cursor.pos);
1085 // The selection spans more than one paragraph
1087 // First paragraph in selection
1088 result += sel_start_cursor.par->String(sel_start_cursor.pos,
1089 sel_start_cursor.par->Last())
1092 // The paragraphs in between (if any)
1093 LyXCursor tmpcur(sel_start_cursor);
1094 tmpcur.par = tmpcur.par->Next();
1095 while (tmpcur.par != sel_end_cursor.par) {
1096 result += tmpcur.par->String(0, tmpcur.par->Last()) + "\n\n";
1097 tmpcur.par = tmpcur.par->Next(); // Or NextAfterFootnote??
1100 // Last paragraph in selection
1101 result += sel_end_cursor.par->String(0, sel_end_cursor.pos);
1107 void LyXText::ClearSelection() const
1114 void LyXText::CursorHome() const
1116 SetCursor(cursor.par, cursor.row->pos);
1120 void LyXText::CursorEnd() const
1122 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1123 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1125 if (cursor.par->Last() &&
1126 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1127 || cursor.par->IsNewline(RowLast(cursor.row))))
1128 SetCursor(cursor.par, RowLast(cursor.row));
1130 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1132 if (cursor.par->table) {
1133 int cell = NumberOfCell(cursor.par, cursor.pos);
1134 if (cursor.par->table->RowHasContRow(cell) &&
1135 cursor.par->table->CellHasContRow(cell)<0) {
1136 if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1137 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1139 if (cursor.par->Last() &&
1140 (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1141 || cursor.par->IsNewline(RowLast(cursor.row))))
1142 SetCursor(cursor.par, RowLast(cursor.row));
1144 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1151 void LyXText::CursorTop() const
1153 while (cursor.par->Previous())
1154 cursor.par = cursor.par->Previous();
1155 SetCursor(cursor.par, 0);
1159 void LyXText::CursorBottom() const
1161 while (cursor.par->Next())
1162 cursor.par = cursor.par->Next();
1163 SetCursor(cursor.par, cursor.par->Last());
1167 /* returns a pointer to the row near the specified y-coordinate
1168 * (relative to the whole text). y is set to the real beginning
1170 Row * LyXText::GetRowNearY(long & y) const
1172 Row * tmprow = firstrow;
1175 while (tmprow->next && tmpy + tmprow->height <= y) {
1176 tmpy += tmprow->height;
1177 tmprow = tmprow->next;
1180 y = tmpy; // return the real y
1185 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1187 // If the mask is completely neutral, tell user
1188 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1189 // Could only happen with user style
1190 owner_->owner()->getMiniBuffer()
1191 ->Set(_("No font change defined. Use Character under"
1192 " the Layout menu to define font change."));
1196 // Try implicit word selection
1197 // If there is a change in the language the implicit word selection
1199 LyXCursor resetCursor = cursor;
1200 bool implicitSelection = (font.language() == ignore_language)
1201 ? SelectWordWhenUnderCursor() : false;
1204 SetFont(font, toggleall);
1206 /* Implicit selections are cleared afterwards and cursor is set to the
1207 original position. */
1208 if (implicitSelection) {
1210 cursor = resetCursor;
1211 SetCursor( cursor.par, cursor.pos );
1212 sel_cursor = cursor;
1217 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1219 if (textclasslist.Style(buffer->params.textclass,
1220 par->GetLayout()).labeltype != LABEL_MANUAL)
1223 return par->BeginningOfMainBody();
1227 /* if there is a selection, reset every environment you can find
1228 * in the selection, otherwise just the environment you are in */
1229 void LyXText::MeltFootnoteEnvironment()
1231 LyXParagraph * tmppar, * firsttmppar;
1235 /* is is only allowed, if the cursor is IN an open footnote.
1236 * Otherwise it is too dangerous */
1237 if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1240 SetUndo(Undo::FINISH,
1241 cursor.par->PreviousBeforeFootnote()->previous,
1242 cursor.par->NextAfterFootnote()->next);
1244 /* ok, move to the beginning of the footnote. */
1245 while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1246 cursor.par = cursor.par->Previous();
1248 SetCursor(cursor.par, cursor.par->Last());
1249 /* this is just faster than using CursorLeft(); */
1251 firsttmppar = cursor.par->ParFromPos(cursor.pos);
1252 tmppar = firsttmppar;
1253 /* tmppar is now the paragraph right before the footnote */
1255 bool first_footnote_par_is_not_empty = tmppar->next->size();
1258 && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1259 tmppar = tmppar->next; /* I use next instead of Next(),
1260 * because there cannot be any
1261 * footnotes in a footnote
1263 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1265 /* remember the captions and empty paragraphs */
1266 if ((textclasslist.Style(buffer->params.textclass,
1267 tmppar->GetLayout())
1268 .labeltype == LABEL_SENSITIVE)
1270 tmppar->SetLayout(0);
1273 // now we will paste the ex-footnote, if the layouts allow it
1274 // first restore the layout of the paragraph right behind
1277 tmppar->next->MakeSameLayout(cursor.par);
1280 if ((!tmppar->GetLayout() && !tmppar->table)
1282 && (!tmppar->Next()->Last()
1283 || tmppar->Next()->HasSameLayout(tmppar)))) {
1284 if (tmppar->Next()->Last()
1285 && tmppar->Next()->IsLineSeparator(0))
1286 tmppar->Next()->Erase(0);
1287 tmppar->PasteParagraph();
1290 tmppar = tmppar->Next(); /* make sure tmppar cannot be touched
1291 * by the pasting of the beginning */
1293 /* then the beginning */
1294 /* if there is no space between the text and the footnote, so we insert
1296 * (only if the previous par and the footnotepar are not empty!) */
1297 if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1298 || firsttmppar->HasSameLayout(firsttmppar->next)) {
1299 if (firsttmppar->size()
1300 && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1301 && first_footnote_par_is_not_empty) {
1302 firsttmppar->next->InsertChar(0, ' ');
1304 firsttmppar->PasteParagraph();
1307 /* now redo the paragaphs */
1308 RedoParagraphs(cursor, tmppar);
1310 SetCursor(cursor.par, cursor.pos);
1312 /* sometimes it can happen, that there is a counter change */
1313 Row * row = cursor.row;
1314 while (row->next && row->par != tmppar && row->next->par != tmppar)
1316 UpdateCounters(row);
1323 /* the DTP switches for paragraphs. LyX will store them in the
1324 * first physicla paragraph. When a paragraph is broken, the top settings
1325 * rest, the bottom settings are given to the new one. So I can make shure,
1326 * they do not duplicate themself and you cannnot make dirty things with
1329 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1330 bool pagebreak_top, bool pagebreak_bottom,
1331 VSpace const & space_top,
1332 VSpace const & space_bottom,
1334 string labelwidthstring,
1337 LyXCursor tmpcursor = cursor;
1339 sel_start_cursor = cursor;
1340 sel_end_cursor = cursor;
1343 // make sure that the depth behind the selection are restored, too
1344 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1345 LyXParagraph * undoendpar = endpar;
1347 if (endpar && endpar->GetDepth()) {
1348 while (endpar && endpar->GetDepth()) {
1349 endpar = endpar->LastPhysicalPar()->Next();
1350 undoendpar = endpar;
1354 endpar = endpar->Next(); // because of parindents etc.
1359 .par->ParFromPos(sel_start_cursor.pos)->previous,
1363 LyXParagraph * tmppar = sel_end_cursor.par;
1364 while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1365 SetCursor(tmppar->FirstPhysicalPar(), 0);
1366 status = LyXText::NEED_MORE_REFRESH;
1367 refresh_row = cursor.row;
1368 refresh_y = cursor.y - cursor.row->baseline;
1369 if (cursor.par->footnoteflag ==
1370 sel_start_cursor.par->footnoteflag) {
1371 cursor.par->line_top = line_top;
1372 cursor.par->line_bottom = line_bottom;
1373 cursor.par->pagebreak_top = pagebreak_top;
1374 cursor.par->pagebreak_bottom = pagebreak_bottom;
1375 cursor.par->added_space_top = space_top;
1376 cursor.par->added_space_bottom = space_bottom;
1377 // does the layout allow the new alignment?
1378 if (align == LYX_ALIGN_LAYOUT)
1379 align = textclasslist
1380 .Style(buffer->params.textclass,
1381 cursor.par->GetLayout()).align;
1382 if (align & textclasslist
1383 .Style(buffer->params.textclass,
1384 cursor.par->GetLayout()).alignpossible) {
1385 if (align == textclasslist
1386 .Style(buffer->params.textclass,
1387 cursor.par->GetLayout()).align)
1388 cursor.par->align = LYX_ALIGN_LAYOUT;
1390 cursor.par->align = align;
1392 cursor.par->SetLabelWidthString(labelwidthstring);
1393 cursor.par->noindent = noindent;
1396 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1399 RedoParagraphs(sel_start_cursor, endpar);
1402 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1403 sel_cursor = cursor;
1404 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1406 SetCursor(tmpcursor.par, tmpcursor.pos);
1410 void LyXText::SetParagraphExtraOpt(int type,
1412 char const * widthp,
1413 int alignment, bool hfill,
1414 bool start_minipage)
1416 LyXCursor tmpcursor = cursor;
1417 LyXParagraph * tmppar;
1419 sel_start_cursor = cursor;
1420 sel_end_cursor = cursor;
1423 // make sure that the depth behind the selection are restored, too
1424 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1425 LyXParagraph * undoendpar = endpar;
1427 if (endpar && endpar->GetDepth()) {
1428 while (endpar && endpar->GetDepth()) {
1429 endpar = endpar->LastPhysicalPar()->Next();
1430 undoendpar = endpar;
1434 endpar = endpar->Next(); // because of parindents etc.
1439 .par->ParFromPos(sel_start_cursor.pos)->previous,
1442 tmppar = sel_end_cursor.par;
1443 while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1444 SetCursor(tmppar->FirstPhysicalPar(), 0);
1445 status = LyXText::NEED_MORE_REFRESH;
1446 refresh_row = cursor.row;
1447 refresh_y = cursor.y - cursor.row->baseline;
1448 if (cursor.par->footnoteflag ==
1449 sel_start_cursor.par->footnoteflag) {
1450 if (type == LyXParagraph::PEXTRA_NONE) {
1451 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1452 cursor.par->UnsetPExtraType();
1453 cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1456 cursor.par->SetPExtraType(type, width, widthp);
1457 cursor.par->pextra_hfill = hfill;
1458 cursor.par->pextra_start_minipage = start_minipage;
1459 cursor.par->pextra_alignment = alignment;
1462 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1464 RedoParagraphs(sel_start_cursor, endpar);
1466 SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1467 sel_cursor = cursor;
1468 SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1470 SetCursor(tmpcursor.par, tmpcursor.pos);
1474 char loweralphaCounter(int n)
1476 if (n < 1 || n > 26)
1482 char alphaCounter(int n)
1484 if (n < 1 || n > 26)
1490 char hebrewCounter(int n)
1492 static const char hebrew[22] = {
1493 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1494 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1495 '÷', 'ø', 'ù', 'ú'
1497 if (n < 1 || n > 22)
1503 static char const * romanCounter(int n)
1505 static char const * roman[20] = {
1506 "i", "ii", "iii", "iv", "v",
1507 "vi", "vii", "viii", "ix", "x",
1508 "xi", "xii", "xiii", "xiv", "xv",
1509 "xvi", "xvii", "xviii", "xix", "xx"
1511 if (n < 1 || n > 20)
1517 // set the counter of a paragraph. This includes the labels
1518 void LyXText::SetCounter(LyXParagraph * par) const
1520 // this is only relevant for the beginning of paragraph
1521 par = par->FirstPhysicalPar();
1523 LyXLayout const & layout =
1524 textclasslist.Style(buffer->params.textclass,
1527 LyXTextClass const & textclass =
1528 textclasslist.TextClass(buffer->params.textclass);
1530 /* copy the prev-counters to this one, unless this is the start of a
1531 footnote or of a bibliography or the very first paragraph */
1533 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1534 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1535 && par->footnotekind == LyXParagraph::FOOTNOTE)
1536 && !(textclasslist.Style(buffer->params.textclass,
1537 par->Previous()->GetLayout()
1538 ).labeltype != LABEL_BIBLIO
1539 && layout.labeltype == LABEL_BIBLIO)) {
1540 for (int i = 0; i < 10; ++i) {
1541 par->setCounter(i, par->Previous()->GetFirstCounter(i));
1543 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1544 if (!par->appendix && par->start_of_appendix){
1545 par->appendix = true;
1546 for (int i = 0; i < 10; ++i) {
1547 par->setCounter(i, 0);
1550 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1551 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1554 for (int i = 0; i < 10; ++i) {
1555 par->setCounter(i, 0);
1557 par->appendix = par->start_of_appendix;
1562 // if this is an open marginnote and this is the first
1563 // entry in the marginnote and the enclosing
1564 // environment is an enum/item then correct for the
1565 // LaTeX behaviour (ARRae)
1566 if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1567 && par->footnotekind == LyXParagraph::MARGIN
1569 && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1570 && (par->PreviousBeforeFootnote()
1571 && textclasslist.Style(buffer->params.textclass,
1572 par->PreviousBeforeFootnote()->GetLayout()
1573 ).labeltype >= LABEL_COUNTER_ENUMI)) {
1574 // Any itemize or enumerate environment in a marginnote
1575 // that is embedded in an itemize or enumerate
1576 // paragraph is seen by LaTeX as being at a deeper
1577 // level within that enclosing itemization/enumeration
1578 // even if there is a "standard" layout at the start of
1584 /* Maybe we have to increment the enumeration depth.
1585 * BUT, enumeration in a footnote is considered in isolation from its
1586 * surrounding paragraph so don't increment if this is the
1587 * first line of the footnote
1588 * AND, bibliographies can't have their depth changed ie. they
1589 * are always of depth 0
1592 && par->Previous()->GetDepth() < par->GetDepth()
1593 && textclasslist.Style(buffer->params.textclass,
1594 par->Previous()->GetLayout()
1595 ).labeltype == LABEL_COUNTER_ENUMI
1596 && par->enumdepth < 3
1597 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1598 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1599 && par->footnotekind == LyXParagraph::FOOTNOTE)
1600 && layout.labeltype != LABEL_BIBLIO) {
1604 /* Maybe we have to decrement the enumeration depth, see note above */
1606 && par->Previous()->GetDepth() > par->GetDepth()
1607 && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1608 && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1609 && par->footnotekind == LyXParagraph::FOOTNOTE)
1610 && layout.labeltype != LABEL_BIBLIO) {
1611 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1612 par->setCounter(6 + par->enumdepth,
1613 par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1614 /* reset the counters.
1615 * A depth change is like a breaking layout
1617 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1618 par->setCounter(i, 0);
1621 if (!par->labelstring.empty()) {
1622 par->labelstring.erase();
1625 if (layout.margintype == MARGIN_MANUAL) {
1626 if (par->labelwidthstring.empty()) {
1627 par->SetLabelWidthString(layout.labelstring());
1630 par->SetLabelWidthString(string());
1633 /* is it a layout that has an automatic label ? */
1634 if (layout.labeltype >= LABEL_FIRST_COUNTER) {
1636 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1637 if (i >= 0 && i<= buffer->params.secnumdepth) {
1638 par->incCounter(i); // increment the counter
1640 // Is there a label? Useful for Chapter layout
1641 if (!par->appendix){
1642 if (!layout.labelstring().empty())
1643 par->labelstring = layout.labelstring();
1645 par->labelstring.erase();
1647 if (!layout.labelstring_appendix().empty())
1648 par->labelstring = layout.labelstring_appendix();
1650 par->labelstring.erase();
1654 std::ostringstream s;
1658 if (!par->appendix) {
1659 switch (2 * LABEL_FIRST_COUNTER -
1660 textclass.maxcounter() + i) {
1661 case LABEL_COUNTER_CHAPTER:
1662 s << par->getCounter(i);
1664 case LABEL_COUNTER_SECTION:
1665 s << par->getCounter(i - 1) << '.'
1666 << par->getCounter(i);
1668 case LABEL_COUNTER_SUBSECTION:
1669 s << par->getCounter(i - 2) << '.'
1670 << par->getCounter(i - 1) << '.'
1671 << par->getCounter(i);
1673 case LABEL_COUNTER_SUBSUBSECTION:
1674 s << par->getCounter(i - 3) << '.'
1675 << par->getCounter(i - 2) << '.'
1676 << par->getCounter(i - 1) << '.'
1677 << par->getCounter(i);
1680 case LABEL_COUNTER_PARAGRAPH:
1681 s << par->getCounter(i - 4) << '.'
1682 << par->getCounter(i - 3) << '.'
1683 << par->getCounter(i - 2) << '.'
1684 << par->getCounter(i - 1) << '.'
1685 << par->getCounter(i);
1687 case LABEL_COUNTER_SUBPARAGRAPH:
1688 s << par->getCounter(i - 5) << '.'
1689 << par->getCounter(i - 4) << '.'
1690 << par->getCounter(i - 3) << '.'
1691 << par->getCounter(i - 2) << '.'
1692 << par->getCounter(i - 1) << '.'
1693 << par->getCounter(i);
1697 s << par->getCounter(i) << '.';
1700 } else { // appendix
1701 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1702 case LABEL_COUNTER_CHAPTER:
1703 if (par->isRightToLeftPar())
1704 s << hebrewCounter(par->getCounter(i));
1706 s << alphaCounter(par->getCounter(i));
1708 case LABEL_COUNTER_SECTION:
1709 if (par->isRightToLeftPar())
1710 s << hebrewCounter(par->getCounter(i - 1));
1712 s << alphaCounter(par->getCounter(i - 1));
1715 << par->getCounter(i);
1718 case LABEL_COUNTER_SUBSECTION:
1719 if (par->isRightToLeftPar())
1720 s << hebrewCounter(par->getCounter(i - 2));
1722 s << alphaCounter(par->getCounter(i - 2));
1725 << par->getCounter(i-1) << '.'
1726 << par->getCounter(i);
1729 case LABEL_COUNTER_SUBSUBSECTION:
1730 if (par->isRightToLeftPar())
1731 s << hebrewCounter(par->getCounter(i-3));
1733 s << alphaCounter(par->getCounter(i-3));
1736 << par->getCounter(i-2) << '.'
1737 << par->getCounter(i-1) << '.'
1738 << par->getCounter(i);
1741 case LABEL_COUNTER_PARAGRAPH:
1742 if (par->isRightToLeftPar())
1743 s << hebrewCounter(par->getCounter(i-4));
1745 s << alphaCounter(par->getCounter(i-4));
1748 << par->getCounter(i-3) << '.'
1749 << par->getCounter(i-2) << '.'
1750 << par->getCounter(i-1) << '.'
1751 << par->getCounter(i);
1754 case LABEL_COUNTER_SUBPARAGRAPH:
1755 if (par->isRightToLeftPar())
1756 s << hebrewCounter(par->getCounter(i-5));
1758 s << alphaCounter(par->getCounter(i-5));
1761 << par->getCounter(i-4) << '.'
1762 << par->getCounter(i-3) << '.'
1763 << par->getCounter(i-2) << '.'
1764 << par->getCounter(i-1) << '.'
1765 << par->getCounter(i);
1769 // Can this ever be reached? And in the
1770 // case it is, how can this be correct?
1772 s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1778 par->labelstring += s.str().c_str();
1779 // We really want to remove the c_str as soon as
1783 char * tmps = s.str();
1784 par->labelstring += tmps;
1788 for (i++; i < 10; ++i) {
1789 // reset the following counters
1790 par->setCounter(i, 0);
1792 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1793 for (i++; i < 10; ++i) {
1794 // reset the following counters
1795 par->setCounter(i, 0);
1797 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1798 par->incCounter(i + par->enumdepth);
1799 int number = par->getCounter(i + par->enumdepth);
1802 std::ostringstream s;
1806 switch (par->enumdepth) {
1808 if (par->isRightToLeftPar())
1810 << hebrewCounter(number)
1814 << loweralphaCounter(number)
1818 if (par->isRightToLeftPar())
1819 s << '.' << romanCounter(number);
1821 s << romanCounter(number) << '.';
1824 if (par->isRightToLeftPar())
1826 << alphaCounter(number);
1828 s << alphaCounter(number)
1832 if (par->isRightToLeftPar())
1839 par->labelstring = s.str().c_str();
1840 // we really want to get rid of that c_str()
1843 char * tmps = s.str();
1844 par->labelstring = tmps;
1848 for (i += par->enumdepth + 1; i < 10; ++i)
1849 par->setCounter(i, 0); /* reset the following counters */
1852 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1853 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1855 int number = par->getCounter(i);
1857 par->bibkey = new InsetBibKey();
1858 par->bibkey->setCounter(number);
1859 par->labelstring = layout.labelstring();
1861 // In biblio should't be following counters but...
1863 string s = layout.labelstring();
1865 // the caption hack:
1867 if (layout.labeltype == LABEL_SENSITIVE) {
1868 if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1869 && (par->footnotekind == LyXParagraph::FIG
1870 || par->footnotekind == LyXParagraph::WIDE_FIG))
1871 s = (par->getParLanguage()->lang == "hebrew")
1872 ? ":øåéà " : "Figure:";
1873 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1874 && (par->footnotekind == LyXParagraph::TAB
1875 || par->footnotekind == LyXParagraph::WIDE_TAB))
1876 s = (par->getParLanguage()->lang == "hebrew")
1877 ? ":äìáè" : "Table:";
1878 else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1879 && par->footnotekind == LyXParagraph::ALGORITHM)
1880 s = (par->getParLanguage()->lang == "hebrew")
1881 ? ":Ãúéøåâìà " : "Algorithm:";
1883 /* par->SetLayout(0);
1884 s = layout->labelstring; */
1885 s = (par->getParLanguage()->lang == "hebrew")
1886 ? " :úåòîùî øñç" : "Senseless: ";
1889 par->labelstring = s;
1891 /* reset the enumeration counter. They are always resetted
1892 * when there is any other layout between */
1893 for (int i = 6 + par->enumdepth; i < 10; ++i)
1894 par->setCounter(i, 0);
1899 /* Updates all counters BEHIND the row. Changed paragraphs
1900 * with a dynamic left margin will be rebroken. */
1901 void LyXText::UpdateCounters(Row * row) const
1910 && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1911 par = row->par->LastPhysicalPar()->Next();
1913 par = row->par->next;
1918 while (row->par != par)
1923 /* now check for the headline layouts. remember that they
1924 * have a dynamic left margin */
1926 && ( textclasslist.Style(buffer->params.textclass,
1927 par->layout).margintype == MARGIN_DYNAMIC
1928 || textclasslist.Style(buffer->params.textclass,
1929 par->layout).labeltype == LABEL_SENSITIVE)
1932 /* Rebreak the paragraph */
1933 RemoveParagraph(row);
1934 AppendParagraph(row);
1936 /* think about the damned open footnotes! */
1937 while (par->Next() &&
1938 (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1939 || par->Next()->IsDummy())){
1941 if (par->IsDummy()) {
1942 while (row->par != par)
1944 RemoveParagraph(row);
1945 AppendParagraph(row);
1950 par = par->LastPhysicalPar()->Next();
1956 /* insets an inset. */
1957 void LyXText::InsertInset(Inset *inset)
1959 if (!cursor.par->InsertInsetAllowed(inset))
1961 SetUndo(Undo::INSERT,
1962 cursor.par->ParFromPos(cursor.pos)->previous,
1963 cursor.par->ParFromPos(cursor.pos)->next);
1964 cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1965 cursor.par->InsertInset(cursor.pos, inset);
1966 InsertChar(LyXParagraph::META_INSET); /* just to rebreak and refresh correctly.
1967 * The character will not be inserted a
1972 #ifdef USE_OLD_CUT_AND_PASTE
1973 // this is for the simple cut and paste mechanism
1974 static LyXParagraph * simple_cut_buffer = 0;
1975 static char simple_cut_buffer_textclass = 0;
1977 void DeleteSimpleCutBuffer()
1979 if (!simple_cut_buffer)
1981 LyXParagraph * tmppar;
1983 while (simple_cut_buffer) {
1984 tmppar = simple_cut_buffer;
1985 simple_cut_buffer = simple_cut_buffer->next;
1988 simple_cut_buffer = 0;
1992 void LyXText::copyEnvironmentType()
1994 copylayouttype = cursor.par->GetLayout();
1998 void LyXText::pasteEnvironmentType()
2000 SetLayout(copylayouttype);
2003 #ifdef USE_OLD_CUT_AND_PASTE
2004 void LyXText::CutSelection(bool doclear)
2006 // This doesn't make sense, if there is no selection
2010 // OK, we have a selection. This is always between sel_start_cursor
2011 // and sel_end cursor
2012 LyXParagraph * tmppar;
2014 // Check whether there are half footnotes in the selection
2015 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2016 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2017 tmppar = sel_start_cursor.par;
2018 while (tmppar != sel_end_cursor.par){
2019 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
2020 WriteAlert(_("Impossible operation"),
2021 _("Don't know what to do with half floats."),
2025 tmppar = tmppar->Next();
2029 /* table stuff -- begin */
2030 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
2031 if ( sel_start_cursor.par != sel_end_cursor.par) {
2032 WriteAlert(_("Impossible operation"),
2033 _("Don't know what to do with half tables."),
2037 sel_start_cursor.par->table->Reinit();
2039 /* table stuff -- end */
2041 // make sure that the depth behind the selection are restored, too
2042 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
2043 LyXParagraph * undoendpar = endpar;
2045 if (endpar && endpar->GetDepth()) {
2046 while (endpar && endpar->GetDepth()) {
2047 endpar = endpar->LastPhysicalPar()->Next();
2048 undoendpar = endpar;
2050 } else if (endpar) {
2051 endpar = endpar->Next(); // because of parindents etc.
2054 SetUndo(Undo::DELETE,
2056 .par->ParFromPos(sel_start_cursor.pos)->previous,
2059 // clear the simple_cut_buffer
2060 DeleteSimpleCutBuffer();
2062 // set the textclass
2063 simple_cut_buffer_textclass = buffer->params.textclass;
2065 #ifdef WITH_WARNINGS
2066 #warning Asger: Make cut more intelligent here.
2069 White paper for "intelligent" cutting:
2071 Example: "This is our text."
2072 Using " our " as selection, cutting will give "This istext.".
2073 Using "our" as selection, cutting will give "This is text.".
2074 Using " our" as selection, cutting will give "This is text.".
2075 Using "our " as selection, cutting will give "This is text.".
2077 All those four selections will (however) paste identically:
2078 Pasting with the cursor right after the "is" will give the
2079 original text with all four selections.
2081 The rationale is to be intelligent such that words are copied,
2082 cut and pasted in a functional manner.
2084 This is not implemented yet. (Asger)
2086 The changes below sees to do a lot of what you want. However
2087 I have not verified that all cases work as they should:
2089 - cut in multiple row
2091 - cut across footnotes and paragraph
2092 My simplistic tests show that the idea are basically sound but
2093 there are some items to fix up...we only need to find them
2096 As do redo Asger's example above (with | beeing the cursor in the
2097 result after cutting.):
2099 Example: "This is our text."
2100 Using " our " as selection, cutting will give "This is|text.".
2101 Using "our" as selection, cutting will give "This is | text.".
2102 Using " our" as selection, cutting will give "This is| text.".
2103 Using "our " as selection, cutting will give "This is |text.".
2108 // there are two cases: cut only within one paragraph or
2109 // more than one paragraph
2111 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2112 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2113 // only within one paragraph
2114 simple_cut_buffer = new LyXParagraph;
2115 LyXParagraph::size_type i =
2116 sel_start_cursor.pos;
2117 for (; i < sel_end_cursor.pos; ++i) {
2118 /* table stuff -- begin */
2119 if (sel_start_cursor.par->table
2120 && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2121 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2122 sel_start_cursor.pos++;
2124 /* table stuff -- end */
2125 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2126 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2128 simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2130 endpar = sel_end_cursor.par->Next();
2132 // cut more than one paragraph
2135 ->BreakParagraphConservative(sel_end_cursor.pos);
2136 sel_end_cursor.par = sel_end_cursor.par->Next();
2137 sel_end_cursor.pos = 0;
2139 cursor = sel_end_cursor;
2141 sel_start_cursor.par
2142 ->BreakParagraphConservative(sel_start_cursor.pos);
2143 // store the endparagraph for redoing later
2144 endpar = sel_end_cursor.par->Next(); /* needed because
2149 // store the selection
2150 simple_cut_buffer = sel_start_cursor.par
2151 ->ParFromPos(sel_start_cursor.pos)->next;
2152 simple_cut_buffer->previous = 0;
2153 sel_end_cursor.par->previous->next = 0;
2155 // cut the selection
2156 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next
2157 = sel_end_cursor.par;
2159 sel_end_cursor.par->previous
2160 = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2162 // care about footnotes
2163 if (simple_cut_buffer->footnoteflag) {
2164 LyXParagraph * tmppar = simple_cut_buffer;
2166 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2167 tmppar = tmppar->next;
2171 // the cut selection should begin with standard layout
2172 simple_cut_buffer->Clear();
2174 // paste the paragraphs again, if possible
2176 sel_start_cursor.par->Next()->StripLeadingSpaces(simple_cut_buffer_textclass);
2177 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2179 !sel_start_cursor.par->Next()->Last())
2180 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2183 // sometimes necessary
2185 sel_start_cursor.par->StripLeadingSpaces(simple_cut_buffer_textclass);
2187 RedoParagraphs(sel_start_cursor, endpar);
2190 cursor = sel_start_cursor;
2191 SetCursor(cursor.par, cursor.pos);
2192 sel_cursor = cursor;
2193 UpdateCounters(cursor.row);
2196 #else ///////////////////////////////////////////////////////////////////
2198 void LyXText::CutSelection(bool doclear)
2200 // This doesn't make sense, if there is no selection
2204 // OK, we have a selection. This is always between sel_start_cursor
2205 // and sel_end cursor
2206 LyXParagraph * tmppar;
2208 // Check whether there are half footnotes in the selection
2209 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2210 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2211 tmppar = sel_start_cursor.par;
2212 while (tmppar != sel_end_cursor.par){
2213 if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
2214 WriteAlert(_("Impossible operation"),
2215 _("Don't know what to do with half floats."),
2219 tmppar = tmppar->Next();
2223 /* table stuff -- begin */
2224 if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
2225 if ( sel_start_cursor.par != sel_end_cursor.par) {
2226 WriteAlert(_("Impossible operation"),
2227 _("Don't know what to do with half tables."),
2231 sel_start_cursor.par->table->Reinit();
2233 /* table stuff -- end */
2235 // make sure that the depth behind the selection are restored, too
2236 LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
2237 LyXParagraph * undoendpar = endpar;
2239 if (endpar && endpar->GetDepth()) {
2240 while (endpar && endpar->GetDepth()) {
2241 endpar = endpar->LastPhysicalPar()->Next();
2242 undoendpar = endpar;
2244 } else if (endpar) {
2245 endpar = endpar->Next(); // because of parindents etc.
2248 SetUndo(Undo::DELETE, sel_start_cursor
2249 .par->ParFromPos(sel_start_cursor.pos)->previous, undoendpar);
2253 // there are two cases: cut only within one paragraph or
2254 // more than one paragraph
2255 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2256 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2257 // only within one paragraph
2258 endpar = sel_start_cursor.par;
2259 cap.cutSelection(sel_start_cursor.par, &endpar,
2260 sel_start_cursor.pos, sel_end_cursor.pos,
2261 buffer->params.textclass, doclear);
2263 endpar = sel_end_cursor.par;
2265 cap.cutSelection(sel_start_cursor.par, &endpar,
2266 sel_start_cursor.pos, sel_end_cursor.pos,
2267 buffer->params.textclass, doclear);
2268 cursor.par = sel_end_cursor.par = endpar;
2269 cursor.pos = sel_end_cursor.pos;
2271 endpar = endpar->Next();
2273 // sometimes necessary
2275 sel_start_cursor.par->StripLeadingSpaces(buffer->params.textclass);
2277 RedoParagraphs(sel_start_cursor, endpar);
2280 cursor = sel_start_cursor;
2281 SetCursor(cursor.par, cursor.pos);
2282 sel_cursor = cursor;
2283 UpdateCounters(cursor.row);
2287 #ifdef USE_OLD_CUT_AND_PASTE
2288 void LyXText::CopySelection()
2290 // this doesnt make sense, if there is no selection
2294 // ok we have a selection. This is always between sel_start_cursor
2295 // and sel_end cursor
2296 LyXParagraph * tmppar;
2298 /* check wether there are half footnotes in the selection */
2299 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2300 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2301 tmppar = sel_start_cursor.par;
2302 while (tmppar != sel_end_cursor.par) {
2303 if (tmppar->footnoteflag !=
2304 sel_end_cursor.par->footnoteflag) {
2305 WriteAlert(_("Impossible operation"),
2306 _("Don't know what to do"
2307 " with half floats."),
2311 tmppar = tmppar->Next();
2315 /* table stuff -- begin */
2316 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2317 if ( sel_start_cursor.par != sel_end_cursor.par){
2318 WriteAlert(_("Impossible operation"),
2319 _("Don't know what to do with half tables."),
2324 /* table stuff -- end */
2326 // delete the simple_cut_buffer
2327 DeleteSimpleCutBuffer();
2329 // set the textclass
2330 simple_cut_buffer_textclass = buffer->params.textclass;
2332 // copy behind a space if there is one
2333 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2334 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2335 && (sel_start_cursor.par != sel_end_cursor.par
2336 || sel_start_cursor.pos < sel_end_cursor.pos))
2337 sel_start_cursor.pos++;
2339 // there are two cases: copy only within one paragraph
2340 // or more than one paragraph
2341 if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)
2342 == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2343 // only within one paragraph
2344 simple_cut_buffer = new LyXParagraph;
2345 LyXParagraph::size_type i = 0;
2346 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2347 sel_start_cursor.par->CopyIntoMinibuffer(i);
2348 simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2351 // copy more than one paragraph
2352 // clone the paragraphs within the selection
2354 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2355 simple_cut_buffer = tmppar->Clone();
2356 LyXParagraph *tmppar2 = simple_cut_buffer;
2358 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2360 tmppar = tmppar->next;
2361 tmppar2->next = tmppar->Clone();
2362 tmppar2->next->previous = tmppar2;
2363 tmppar2 = tmppar2->next;
2367 // care about footnotes
2368 if (simple_cut_buffer->footnoteflag) {
2369 tmppar = simple_cut_buffer;
2371 tmppar->footnoteflag =
2372 LyXParagraph::NO_FOOTNOTE;
2373 tmppar = tmppar->next;
2377 // the simple_cut_buffer paragraph is too big
2378 LyXParagraph::size_type tmpi2 =
2379 sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2380 for (; tmpi2; --tmpi2)
2381 simple_cut_buffer->Erase(0);
2383 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2385 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2386 while (tmppar2->size() > tmpi2) {
2387 tmppar2->Erase(tmppar2->size() - 1);
2392 #else //////////////////////////////////////////////////////////////////////
2394 void LyXText::CopySelection()
2396 // this doesnt make sense, if there is no selection
2400 // ok we have a selection. This is always between sel_start_cursor
2401 // and sel_end cursor
2402 LyXParagraph * tmppar;
2404 /* check wether there are half footnotes in the selection */
2405 if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2406 || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2407 tmppar = sel_start_cursor.par;
2408 while (tmppar != sel_end_cursor.par) {
2409 if (tmppar->footnoteflag !=
2410 sel_end_cursor.par->footnoteflag) {
2411 WriteAlert(_("Impossible operation"),
2412 _("Don't know what to do"
2413 " with half floats."),
2417 tmppar = tmppar->Next();
2421 /* table stuff -- begin */
2422 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2423 if ( sel_start_cursor.par != sel_end_cursor.par){
2424 WriteAlert(_("Impossible operation"),
2425 _("Don't know what to do with half tables."),
2430 /* table stuff -- end */
2432 // copy behind a space if there is one
2433 while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2434 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2435 && (sel_start_cursor.par != sel_end_cursor.par
2436 || sel_start_cursor.pos < sel_end_cursor.pos))
2437 sel_start_cursor.pos++;
2441 cap.copySelection(sel_start_cursor.par, sel_end_cursor.par,
2442 sel_start_cursor.pos, sel_end_cursor.pos,
2443 buffer->params.textclass);
2447 #ifdef USE_OLD_CUT_AND_PASTE
2448 void LyXText::PasteSelection()
2450 // this does not make sense, if there is nothing to paste
2451 if (!simple_cut_buffer)
2454 LyXParagraph * tmppar;
2455 LyXParagraph * endpar;
2457 LyXCursor tmpcursor;
2459 // be carefull with footnotes in footnotes
2460 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2462 // check whether the cut_buffer includes a footnote
2463 tmppar = simple_cut_buffer;
2465 && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2466 tmppar = tmppar->next;
2469 WriteAlert(_("Impossible operation"),
2470 _("Can't paste float into float!"),
2476 /* table stuff -- begin */
2477 if (cursor.par->table) {
2478 if (simple_cut_buffer->next) {
2479 WriteAlert(_("Impossible operation"),
2480 _("Table cell cannot include more than one paragraph!"),
2485 /* table stuff -- end */
2487 SetUndo(Undo::INSERT,
2488 cursor.par->ParFromPos(cursor.pos)->previous,
2489 cursor.par->ParFromPos(cursor.pos)->next);
2493 // There are two cases: cutbuffer only one paragraph or many
2494 if (!simple_cut_buffer->next) {
2495 // only within a paragraph
2497 tmppar = simple_cut_buffer->Clone();
2498 /* table stuff -- begin */
2499 bool table_too_small = false;
2500 if (tmpcursor.par->table) {
2501 while (simple_cut_buffer->size()
2502 && !table_too_small) {
2503 if (simple_cut_buffer->IsNewline(0)){
2504 while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2506 simple_cut_buffer->Erase(0);
2507 if (tmpcursor.pos < tmpcursor.par->Last())
2510 table_too_small = true;
2512 // This is an attempt to fix the
2513 // "never insert a space at the
2514 // beginning of a paragraph" problem.
2515 if (tmpcursor.pos == 0
2516 && simple_cut_buffer->IsLineSeparator(0)) {
2517 simple_cut_buffer->Erase(0);
2519 simple_cut_buffer->CutIntoMinibuffer(0);
2520 simple_cut_buffer->Erase(0);
2521 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2527 /* table stuff -- end */
2528 // Some provisions should be done here for checking
2529 // if we are inserting at the beginning of a
2530 // paragraph. If there are a space at the beginning
2531 // of the text to insert and we are inserting at
2532 // the beginning of the paragraph the space should
2534 while (simple_cut_buffer->size()) {
2535 // This is an attempt to fix the
2536 // "never insert a space at the
2537 // beginning of a paragraph" problem.
2538 if (tmpcursor.pos == 0
2539 && simple_cut_buffer->IsLineSeparator(0)) {
2540 simple_cut_buffer->Erase(0);
2542 simple_cut_buffer->CutIntoMinibuffer(0);
2543 simple_cut_buffer->Erase(0);
2544 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2549 delete simple_cut_buffer;
2550 simple_cut_buffer = tmppar;
2551 endpar = tmpcursor.par->Next();
2556 // make a copy of the simple cut_buffer
2557 tmppar = simple_cut_buffer;
2558 LyXParagraph * simple_cut_clone = tmppar->Clone();
2559 LyXParagraph * tmppar2 = simple_cut_clone;
2560 if (cursor.par->footnoteflag){
2561 tmppar->footnoteflag = cursor.par->footnoteflag;
2562 tmppar->footnotekind = cursor.par->footnotekind;
2564 while (tmppar->next) {
2565 tmppar = tmppar->next;
2566 tmppar2->next = tmppar->Clone();
2567 tmppar2->next->previous = tmppar2;
2568 tmppar2 = tmppar2->next;
2569 if (cursor.par->footnoteflag){
2570 tmppar->footnoteflag = cursor.par->footnoteflag;
2571 tmppar->footnotekind = cursor.par->footnotekind;
2575 // make sure there is no class difference
2576 cap.SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2577 buffer->params.textclass,
2580 // make the simple_cut_buffer exactly the same layout than
2581 // the cursor paragraph
2582 simple_cut_buffer->MakeSameLayout(cursor.par);
2584 // find the end of the buffer
2585 LyXParagraph * lastbuffer = simple_cut_buffer;
2586 while (lastbuffer->Next())
2587 lastbuffer = lastbuffer->Next();
2589 bool paste_the_end = false;
2591 // open the paragraph for inserting the simple_cut_buffer
2593 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2594 cursor.par->BreakParagraphConservative(cursor.pos);
2595 paste_the_end = true;
2598 // set the end for redoing later
2599 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2602 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2603 cursor.par->ParFromPos(cursor.pos)->next;
2604 cursor.par->ParFromPos(cursor.pos)->next->previous =
2605 lastbuffer->ParFromPos(lastbuffer->Last());
2607 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2608 simple_cut_buffer->previous =
2609 cursor.par->ParFromPos(cursor.pos);
2611 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2612 lastbuffer = cursor.par;
2614 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2616 // store the new cursor position
2617 tmpcursor.par = lastbuffer;
2618 tmpcursor.pos = lastbuffer->Last();
2620 // maybe some pasting
2621 if (lastbuffer->Next() && paste_the_end) {
2622 if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2623 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2625 } else if (!lastbuffer->Next()->Last()) {
2626 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2627 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2629 } else if (!lastbuffer->Last()) {
2630 lastbuffer->MakeSameLayout(lastbuffer->next);
2631 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2634 lastbuffer->Next()->StripLeadingSpaces(buffer->params.textclass);
2637 // restore the simple cut buffer
2638 simple_cut_buffer = simple_cut_clone;
2641 RedoParagraphs(cursor, endpar);
2643 SetCursor(cursor.par, cursor.pos);
2646 sel_cursor = cursor;
2647 SetCursor(tmpcursor.par, tmpcursor.pos);
2649 UpdateCounters(cursor.row);
2652 #else ////////////////////////////////////////////////////////////////////
2654 void LyXText::PasteSelection()
2658 // this does not make sense, if there is nothing to paste
2659 if (!cap.checkPastePossible(cursor.par, cursor.pos))
2662 SetUndo(Undo::INSERT,
2663 cursor.par->ParFromPos(cursor.pos)->previous,
2664 cursor.par->ParFromPos(cursor.pos)->next);
2666 LyXParagraph *endpar;
2667 LyXParagraph *actpar = cursor.par;
2668 int endpos = cursor.pos;
2670 cap.pasteSelection(&actpar, &endpar, endpos, buffer->params.textclass);
2672 RedoParagraphs(cursor, endpar);
2674 SetCursor(cursor.par, cursor.pos);
2677 sel_cursor = cursor;
2678 SetCursor(actpar, endpos);
2680 UpdateCounters(cursor.row);
2684 // returns a pointer to the very first LyXParagraph
2685 LyXParagraph * LyXText::FirstParagraph() const
2687 return buffer->paragraph;
2691 // returns true if the specified string is at the specified position
2692 bool LyXText::IsStringInText(LyXParagraph * par,
2693 LyXParagraph::size_type pos,
2694 char const * str) const
2698 while (pos + i < par->Last() && str[i] &&
2699 str[i] == par->GetChar(pos + i)) {
2709 // sets the selection over the number of characters of string, no check!!
2710 void LyXText::SetSelectionOverString(char const * string)
2712 sel_cursor = cursor;
2713 for (int i = 0; string[i]; ++i)
2719 // simple replacing. The font of the first selected character is used
2720 void LyXText::ReplaceSelectionWithString(char const * str)
2725 if (!selection) { // create a dummy selection
2726 sel_end_cursor = cursor;
2727 sel_start_cursor = cursor;
2730 // Get font setting before we cut
2731 LyXParagraph::size_type pos = sel_end_cursor.pos;
2732 LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2734 // Insert the new string
2735 for (int i = 0; str[i]; ++i) {
2736 sel_end_cursor.par->InsertChar(pos, str[i]);
2737 sel_end_cursor.par->SetFont(pos, font);
2741 // Cut the selection
2748 // if the string can be found: return true and set the cursor to
2750 bool LyXText::SearchForward(char const * str) const
2752 LyXParagraph * par = cursor.par;
2753 LyXParagraph::size_type pos = cursor.pos;
2754 while (par && !IsStringInText(par, pos, str)) {
2755 if (pos < par->Last() - 1)
2763 SetCursor(par, pos);
2771 bool LyXText::SearchBackward(char const * string) const
2773 LyXParagraph * par = cursor.par;
2774 int pos = cursor.pos;
2780 // We skip empty paragraphs (Asger)
2782 par = par->Previous();
2784 pos = par->Last() - 1;
2785 } while (par && pos < 0);
2787 } while (par && !IsStringInText(par, pos, string));
2790 SetCursor(par, pos);
2797 // needed to insert the selection
2798 void LyXText::InsertStringA(string const & str)
2800 LyXParagraph * par = cursor.par;
2801 LyXParagraph::size_type pos = cursor.pos;
2802 LyXParagraph::size_type a = 0;
2804 LyXParagraph * endpar = cursor.par->Next();
2809 textclasslist.Style(buffer->params.textclass,
2810 cursor.par->GetLayout()).isEnvironment();
2811 // only to be sure, should not be neccessary
2814 // insert the string, don't insert doublespace
2815 string::size_type i = 0;
2816 while (i < str.length()) {
2817 if (str[i] != '\n') {
2819 && i + 1 < str.length() && str[i + 1] != ' '
2820 && pos && par->GetChar(pos - 1)!= ' ') {
2821 par->InsertChar(pos,' ');
2822 par->SetFont(pos, current_font);
2824 } else if (par->table) {
2825 if (str[i] == '\t') {
2826 while((pos < par->size()) &&
2827 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2829 if (pos < par->size())
2831 else // no more fields to fill skip the rest
2833 } else if ((str[i] != 13) &&
2834 ((str[i] & 127) >= ' ')) {
2835 par->InsertChar(pos, str[i]);
2836 par->SetFont(pos, current_font);
2839 } else if (str[i] == ' ') {
2840 InsetSpecialChar * new_inset =
2841 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2842 if (par->InsertInsetAllowed(new_inset)) {
2843 par->InsertChar(pos, LyXParagraph::META_INSET);
2844 par->SetFont(pos, current_font);
2845 par->InsertInset(pos, new_inset);
2850 } else if (str[i] == '\t') {
2851 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2852 InsetSpecialChar * new_inset =
2853 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2854 if (par->InsertInsetAllowed(new_inset)) {
2855 par->InsertChar(pos, LyXParagraph::META_INSET);
2856 par->SetFont(pos, current_font);
2857 par->InsertInset(pos, new_inset);
2863 } else if (str[i] != 13 &&
2864 // Ignore unprintables
2865 (str[i] & 127) >= ' ') {
2866 par->InsertChar(pos, str[i]);
2867 par->SetFont(pos, current_font);
2872 if ((i + 1) >= str.length()) {
2873 if (pos < par->size())
2877 while((pos < par->size()) &&
2878 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2881 cell = NumberOfCell(par, pos);
2882 while((pos < par->size()) &&
2883 !(par->table->IsFirstCell(cell))) {
2885 while((pos < par->size()) &&
2886 (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2889 cell = NumberOfCell(par, pos);
2891 if (pos >= par->size())
2892 // no more fields to fill skip the rest
2895 if (!par->size()) { // par is empty
2896 InsetSpecialChar * new_inset =
2897 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2898 if (par->InsertInsetAllowed(new_inset)) {
2899 par->InsertChar(pos, LyXParagraph::META_INSET);
2900 par->SetFont(pos, current_font);
2901 par->InsertInset(pos, new_inset);
2907 par->BreakParagraph(pos, flag);
2915 RedoParagraphs(cursor, endpar);
2916 SetCursor(cursor.par, cursor.pos);
2917 sel_cursor = cursor;
2918 SetCursor(par, pos);
2923 /* turns double-CR to single CR, others where converted into one blank and 13s
2924 * that are ignored .Double spaces are also converted into one. Spaces at
2925 * the beginning of a paragraph are forbidden. tabs are converted into one
2926 * space. then InsertStringA is called */
2927 void LyXText::InsertStringB(string const & s)
2930 LyXParagraph * par = cursor.par;
2931 string::size_type i = 1;
2932 while (i < str.length()) {
2933 if (str[i] == '\t' && !par->table)
2935 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2937 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2938 if (str[i + 1] != '\n') {
2939 if (str[i - 1] != ' ')
2944 while (i + 1 < str.length()
2945 && (str[i + 1] == ' '
2946 || str[i + 1] == '\t'
2947 || str[i + 1] == '\n'
2948 || str[i + 1] == 13)) {
2959 bool LyXText::GotoNextError() const
2961 LyXCursor res = cursor;
2963 if (res.pos < res.par->Last() - 1) {
2967 res.par = res.par->Next();
2972 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2973 && res.par->GetInset(res.pos)->AutoDelete()));
2976 SetCursor(res.par, res.pos);
2983 bool LyXText::GotoNextNote() const
2985 LyXCursor res = cursor;
2987 if (res.pos < res.par->Last() - 1) {
2990 res.par = res.par->Next();
2995 !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2996 && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2999 SetCursor(res.par, res.pos);
3006 void LyXText::CheckParagraph(LyXParagraph * par,
3007 LyXParagraph::size_type pos)
3009 LyXCursor tmpcursor;
3012 /* table stuff -- begin*/
3015 CheckParagraphInTable(par, pos);
3018 /* table stuff -- end*/
3021 LyXParagraph::size_type z;
3022 Row * row = GetRow(par, pos, y);
3024 // is there a break one row above
3025 if (row->previous && row->previous->par == row->par) {
3026 z = NextBreakPoint(row->previous, paperwidth);
3027 if ( z >= row->pos) {
3028 // set the dimensions of the row above
3029 y -= row->previous->height;
3031 refresh_row = row->previous;
3032 status = LyXText::NEED_MORE_REFRESH;
3034 BreakAgain(row->previous);
3036 // set the cursor again. Otherwise
3037 // dangling pointers are possible
3038 SetCursor(cursor.par, cursor.pos);
3039 sel_cursor = cursor;
3044 int tmpheight = row->height;
3045 LyXParagraph::size_type tmplast = RowLast(row);
3050 if (row->height == tmpheight && RowLast(row) == tmplast)
3051 status = LyXText::NEED_VERY_LITTLE_REFRESH;
3053 status = LyXText::NEED_MORE_REFRESH;
3055 // check the special right address boxes
3056 if (textclasslist.Style(buffer->params.textclass,
3057 par->GetLayout()).margintype
3058 == MARGIN_RIGHT_ADDRESS_BOX) {
3059 tmpcursor.par = par;
3060 tmpcursor.row = row;
3063 tmpcursor.x_fix = 0;
3064 tmpcursor.pos = pos;
3065 RedoDrawingOfParagraph(tmpcursor);
3070 // set the cursor again. Otherwise dangling pointers are possible
3071 // also set the selection
3075 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
3076 sel_cursor = cursor;
3077 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
3078 sel_start_cursor = cursor;
3079 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
3080 sel_end_cursor = cursor;
3081 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
3082 last_sel_cursor = cursor;
3085 SetCursorIntern(cursor.par, cursor.pos);
3089 // returns 0 if inset wasn't found
3090 int LyXText::UpdateInset(Inset * inset)
3092 // first check the current paragraph
3093 int pos = cursor.par->GetPositionOfInset(inset);
3095 CheckParagraph(cursor.par, pos);
3099 // check every paragraph
3101 LyXParagraph * par = FirstParagraph();
3103 // make sure the paragraph is open
3104 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
3105 pos = par->GetPositionOfInset(inset);
3107 CheckParagraph(par, pos);
3118 void LyXText::SetCursor(LyXParagraph * par,
3119 LyXParagraph::size_type pos,
3120 bool setfont, bool boundary) const
3122 LyXCursor old_cursor = cursor;
3123 SetCursorIntern(par, pos, setfont, boundary);
3124 DeleteEmptyParagraphMechanism(old_cursor);
3128 void LyXText::SetCursor(LyXCursor & cur, LyXParagraph * par,
3129 LyXParagraph::size_type pos, bool boundary) const
3131 // correct the cursor position if impossible
3132 if (pos > par->Last()){
3133 LyXParagraph * tmppar = par->ParFromPos(pos);
3134 pos = par->PositionInParFromPos(pos);
3137 if (par->IsDummy() && par->previous &&
3138 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3139 while (par->previous &&
3140 ((par->previous->IsDummy() &&
3141 (par->previous->previous->footnoteflag ==
3142 LyXParagraph::CLOSED_FOOTNOTE)) ||
3143 (par->previous->footnoteflag ==
3144 LyXParagraph::CLOSED_FOOTNOTE))) {
3145 par = par->previous ;
3146 if (par->IsDummy() &&
3147 (par->previous->footnoteflag ==
3148 LyXParagraph::CLOSED_FOOTNOTE))
3149 pos += par->size() + 1;
3151 if (par->previous) {
3152 par = par->previous;
3154 pos += par->size() + 1;
3159 cur.boundary = boundary;
3161 /* get the cursor y position in text */
3163 Row * row = GetRow(par, pos, y);
3164 /* y is now the beginning of the cursor row */
3166 /* y is now the cursor baseline */
3169 /* now get the cursors x position */
3171 float fill_separator, fill_hfill, fill_label_hfill;
3172 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3173 LyXParagraph::size_type cursor_vpos;
3174 LyXParagraph::size_type last = RowLastPrintable(row);
3176 if (pos > last + 1) // This shouldn't happen.
3178 else if (pos < row->pos)
3181 if (last < row->pos)
3182 cursor_vpos = row->pos;
3183 else if (pos > last && !boundary)
3184 cursor_vpos = (row->par->isRightToLeftPar())
3185 ? row->pos : last+1;
3186 else if (pos > row->pos &&
3187 (pos > last || boundary ||
3188 (row->par->table && row->par->IsNewline(pos))))
3189 /// Place cursor after char at (logical) position pos-1
3190 cursor_vpos = (bidi_level(pos-1) % 2 == 0)
3191 ? log2vis(pos-1) + 1 : log2vis(pos-1);
3193 /// Place cursor before char at (logical) position pos
3194 cursor_vpos = (bidi_level(pos) % 2 == 0)
3195 ? log2vis(pos) : log2vis(pos) + 1;
3197 /* table stuff -- begin*/
3198 if (row->par->table) {
3199 int cell = NumberOfCell(row->par, row->pos);
3201 x += row->par->table->GetBeginningOfTextInCell(cell);
3202 for (LyXParagraph::size_type vpos = row->pos;
3203 vpos < cursor_vpos; ++vpos) {
3204 pos = vis2log(vpos);
3205 if (row->par->IsNewline(pos)) {
3206 x = x_old + row->par->table->WidthOfColumn(cell);
3209 x += row->par->table->GetBeginningOfTextInCell(cell);
3211 x += SingleWidth(row->par, pos);
3215 /* table stuff -- end*/
3216 LyXParagraph::size_type main_body =
3217 BeginningOfMainBody(row->par);
3218 if ((main_body > 0) &&
3219 ((main_body-1 > last) ||
3220 !row->par->IsLineSeparator(main_body-1)))
3223 for (LyXParagraph::size_type vpos = row->pos;
3224 vpos < cursor_vpos; ++vpos) {
3225 pos = vis2log(vpos);
3226 if (main_body > 0 && pos == main_body-1) {
3227 x += fill_label_hfill +
3228 lyxfont::width(textclasslist.Style(
3229 buffer->params.textclass,
3230 row->par->GetLayout())
3232 GetFont(row->par, -2));
3233 if (row->par->IsLineSeparator(main_body-1))
3234 x -= SingleWidth(row->par,main_body-1);
3236 if (HfillExpansion(row, pos)) {
3237 x += SingleWidth(row->par, pos);
3238 if (pos >= main_body)
3241 x += fill_label_hfill;
3242 } else if (row->par->IsSeparator(pos)) {
3243 x += SingleWidth(row->par, pos);
3244 if (pos >= main_body)
3245 x += fill_separator;
3247 x += SingleWidth(row->par, pos);
3257 void LyXText::SetCursorIntern(LyXParagraph * par,
3258 LyXParagraph::size_type pos,
3259 bool setfont, bool boundary) const
3261 SetCursor(cursor, par, pos, boundary);
3262 // #warning Remove this when verified working (Jug 20000413)
3264 // correct the cursor position if impossible
3265 if (pos > par->Last()){
3266 LyXParagraph * tmppar = par->ParFromPos(pos);
3267 pos = par->PositionInParFromPos(pos);
3270 if (par->IsDummy() && par->previous &&
3271 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3272 while (par->previous &&
3273 ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3274 (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3275 par = par->previous ;
3276 if (par->IsDummy() &&
3277 par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3278 pos += par->size() + 1;
3280 if (par->previous) {
3281 par = par->previous;
3283 pos += par->size() + 1;
3289 /* get the cursor y position in text */
3291 Row * row = GetRow(par, pos, y);
3292 /* y is now the beginning of the cursor row */
3294 /* y is now the cursor baseline */
3297 /* now get the cursors x position */
3299 float fill_separator, fill_hfill, fill_label_hfill;
3300 PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3301 LyXParagraph::size_type cursor_vpos;
3302 LyXParagraph::size_type last = RowLastPrintable(row);
3304 if (pos > last + 1) // This shouldn't happen.
3307 if (last < row->pos)
3309 else if (pos > last ||
3310 (pos - 1 >= row->pos &&
3311 (row->par->IsSeparator(pos) ||
3312 (row->par->table && row->par->IsNewline(pos))
3314 /// Place cursor after char at (logical) position pos-1
3315 cursor_vpos = (bidi_level(pos-1) % 2 == 0)
3316 ? log2vis(pos-1) + 1 : log2vis(pos-1);
3318 /// Place cursor before char at (logical) position pos
3319 cursor_vpos = (bidi_level(pos) % 2 == 0)
3320 ? log2vis(pos) : log2vis(pos) + 1;
3322 /* table stuff -- begin*/
3323 if (row->par->table) {
3324 int cell = NumberOfCell(row->par, row->pos);
3326 x += row->par->table->GetBeginningOfTextInCell(cell);
3327 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3328 pos = vis2log(vpos);
3329 if (row->par->IsNewline(pos)) {
3330 x = x_old + row->par->table->WidthOfColumn(cell);
3333 x += row->par->table->GetBeginningOfTextInCell(cell);
3335 x += SingleWidth(row->par, pos);
3339 /* table stuff -- end*/
3340 LyXParagraph::size_type main_body =
3341 BeginningOfMainBody(row->par);
3342 if (main_body > 0 &&
3343 (main_body-1 > last ||
3344 !row->par->IsLineSeparator(main_body-1)))
3347 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos) {
3348 pos = vis2log(vpos);
3349 if (main_body > 0 && pos == main_body-1) {
3350 x += fill_label_hfill +
3351 lyxfont::width(textclasslist
3352 .Style(buffer->params.textclass,
3353 row->par->GetLayout())
3355 GetFont(row->par, -2));
3356 if (row->par->IsLineSeparator(main_body-1))
3357 x -= SingleWidth(row->par, main_body-1);
3359 if (HfillExpansion(row, pos)) {
3360 x += SingleWidth(row->par, pos);
3361 if (pos >= main_body)
3364 x += fill_label_hfill;
3366 else if (row->par->IsSeparator(pos)) {
3367 x += SingleWidth(row->par, pos);
3368 if (pos >= main_body)
3369 x += fill_separator;
3371 x += SingleWidth(row->par, pos);
3377 cursor.x_fix = cursor.x;
3384 void LyXText::SetCurrentFont() const
3386 LyXParagraph::size_type pos = cursor.pos;
3387 if (cursor.boundary && pos > 0)
3391 if (pos == cursor.par->Last() ||
3392 (cursor.par->table && cursor.par->IsNewline(pos)))
3394 else if (cursor.par->IsSeparator(pos)) {
3395 if (pos > cursor.row->pos &&
3396 bidi_level(pos) % 2 ==
3397 bidi_level(pos - 1) % 2)
3399 else if (pos + 1 < cursor.par->Last())
3404 current_font = cursor.par->GetFontSettings(pos);
3405 real_current_font = GetFont(cursor.par, pos);
3409 void LyXText::SetCursorFromCoordinates(int x, long y) const
3411 LyXCursor old_cursor = cursor;
3413 /* get the row first */
3415 Row * row = GetRowNearY(y);
3416 cursor.par = row->par;
3418 int column = GetColumnNearX(row, x, cursor.boundary);
3419 cursor.pos = row->pos + column;
3421 cursor.y = y + row->baseline;
3424 DeleteEmptyParagraphMechanism(old_cursor);
3427 void LyXText::SetCursorFromCoordinates(LyXCursor & cur, int x, long y) const
3429 /* get the row first */
3431 Row * row = GetRowNearY(y);
3432 int column = GetColumnNearX(row, x, cur.boundary);
3435 cur.pos = row->pos + column;
3437 cur.y = y + row->baseline;
3442 void LyXText::CursorLeft(bool internal) const
3444 CursorLeftIntern(internal);
3445 if (cursor.par->table) {
3446 int cell = NumberOfCell(cursor.par, cursor.pos);
3447 if (cursor.par->table->IsContRow(cell) &&
3448 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell)) < 0) {
3455 void LyXText::CursorLeftIntern(bool internal) const
3457 if (cursor.pos > 0) {
3458 bool boundary = cursor.boundary;
3459 SetCursor(cursor.par, cursor.pos - 1, true, false);
3460 if (!internal && !boundary &&
3461 IsBoundary(cursor.par, cursor.pos + 1))
3462 SetCursor(cursor.par, cursor.pos + 1, true, true);
3463 } else if (cursor.par->Previous()) { // steps into the above paragraph.
3464 LyXParagraph * par = cursor.par->Previous();
3465 LyXParagraph::size_type pos = par->Last();
3466 SetCursor(par, pos);
3467 if (IsBoundary(par, pos))
3468 SetCursor(par, pos, false, true);
3473 void LyXText::CursorRight(bool internal) const
3475 CursorRightIntern(internal);
3476 if (cursor.par->table) {
3477 int cell = NumberOfCell(cursor.par, cursor.pos);
3478 if (cursor.par->table->IsContRow(cell) &&
3479 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3486 void LyXText::CursorRightIntern(bool internal) const
3488 if (cursor.pos < cursor.par->Last()) {
3489 if (!internal && cursor.boundary &&
3490 (!cursor.par->table || !cursor.par->IsNewline(cursor.pos)))
3491 SetCursor(cursor.par, cursor.pos, true, false);
3493 SetCursor(cursor.par, cursor.pos + 1, true, false);
3494 if (!internal && IsBoundary(cursor.par, cursor.pos))
3495 SetCursor(cursor.par, cursor.pos, true, true);
3497 } else if (cursor.par->Next())
3498 SetCursor(cursor.par->Next(), 0);
3502 void LyXText::CursorUp() const
3504 SetCursorFromCoordinates(cursor.x_fix,
3505 cursor.y - cursor.row->baseline - 1);
3506 if (cursor.par->table) {
3507 int cell = NumberOfCell(cursor.par, cursor.pos);
3508 if (cursor.par->table->IsContRow(cell) &&
3509 cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3516 void LyXText::CursorDown() const
3518 if (cursor.par->table &&
3519 cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3522 SetCursorFromCoordinates(cursor.x_fix,
3523 cursor.y - cursor.row->baseline
3524 + cursor.row->height + 1);
3525 if (cursor.par->table) {
3526 int cell = NumberOfCell(cursor.par, cursor.pos);
3527 int cell_above = cursor.par->table->GetCellAbove(cell);
3528 while(cursor.par->table &&
3529 cursor.par->table->IsContRow(cell) &&
3530 (cursor.par->table->CellHasContRow(cell_above)<0)) {
3531 SetCursorFromCoordinates(cursor.x_fix,
3532 cursor.y - cursor.row->baseline
3533 + cursor.row->height + 1);
3534 if (cursor.par->table) {
3535 cell = NumberOfCell(cursor.par, cursor.pos);
3536 cell_above = cursor.par->table->GetCellAbove(cell);
3543 void LyXText::CursorUpParagraph() const
3545 if (cursor.pos > 0) {
3546 SetCursor(cursor.par, 0);
3548 else if (cursor.par->Previous()) {
3549 SetCursor(cursor.par->Previous(), 0);
3554 void LyXText::CursorDownParagraph() const
3556 if (cursor.par->Next()) {
3557 SetCursor(cursor.par->Next(), 0);
3559 SetCursor(cursor.par, cursor.par->Last());
3564 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3566 // Would be wrong to delete anything if we have a selection.
3567 if (selection) return;
3569 // We allow all kinds of "mumbo-jumbo" when freespacing.
3570 if (textclasslist.Style(buffer->params.textclass,
3571 old_cursor.par->GetLayout()).free_spacing)
3574 bool deleted = false;
3576 /* Ok I'll put some comments here about what is missing.
3577 I have fixed BackSpace (and thus Delete) to not delete
3578 double-spaces automagically. I have also changed Cut,
3579 Copy and Paste to hopefully do some sensible things.
3580 There are still some small problems that can lead to
3581 double spaces stored in the document file or space at
3582 the beginning of paragraphs. This happens if you have
3583 the cursor betwenn to spaces and then save. Or if you
3584 cut and paste and the selection have a space at the
3585 beginning and then save right after the paste. I am
3586 sure none of these are very hard to fix, but I will
3587 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3588 that I can get some feedback. (Lgb)
3591 // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3592 // delete the LineSeparator.
3595 // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3596 // delete the LineSeparator.
3599 // If the pos around the old_cursor were spaces, delete one of them.
3600 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) {
3601 // Only if the cursor has really moved
3603 if (old_cursor.pos > 0
3604 && old_cursor.pos < old_cursor.par->Last()
3605 && old_cursor.par->IsLineSeparator(old_cursor.pos)
3606 && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3607 old_cursor.par->Erase(old_cursor.pos - 1);
3608 RedoParagraphs(old_cursor, old_cursor.par->Next());
3610 if (old_cursor.par == cursor.par &&
3611 cursor.pos > old_cursor.pos) {
3612 SetCursorIntern(cursor.par, cursor.pos - 1);
3614 SetCursorIntern(cursor.par, cursor.pos);
3619 // Do not delete empty paragraphs with keepempty set.
3620 if ((textclasslist.Style(buffer->params.textclass,
3621 old_cursor.par->GetLayout())).keepempty)
3624 LyXCursor tmpcursor;
3626 if (old_cursor.par != cursor.par) {
3627 if ( (old_cursor.par->Last() == 0
3628 || (old_cursor.par->Last() == 1
3629 && old_cursor.par->IsLineSeparator(0)))
3630 && old_cursor.par->FirstPhysicalPar()
3631 == old_cursor.par->LastPhysicalPar()) {
3632 // ok, we will delete anything
3634 // make sure that you do not delete any environments
3635 if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3636 !(old_cursor.row->previous
3637 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3638 && !(old_cursor.row->next
3639 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3640 || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3641 && ((old_cursor.row->previous
3642 && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3643 || (old_cursor.row->next
3644 && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3646 status = LyXText::NEED_MORE_REFRESH;
3649 if (old_cursor.row->previous) {
3650 refresh_row = old_cursor.row->previous;
3651 refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3653 cursor = old_cursor; // that undo can restore the right cursor position
3654 LyXParagraph * endpar = old_cursor.par->next;
3655 if (endpar && endpar->GetDepth()) {
3656 while (endpar && endpar->GetDepth()) {
3657 endpar = endpar->LastPhysicalPar()->Next();
3660 SetUndo(Undo::DELETE,
3661 old_cursor.par->previous,
3666 RemoveRow(old_cursor.row);
3667 if (buffer->paragraph == old_cursor.par) {
3668 buffer->paragraph = buffer->paragraph->next;
3671 delete old_cursor.par;
3673 /* Breakagain the next par. Needed
3674 * because of the parindent that
3675 * can occur or dissappear. The
3676 * next row can change its height,
3677 * if there is another layout before */
3678 if (refresh_row->next) {
3679 BreakAgain(refresh_row->next);
3680 UpdateCounters(refresh_row);
3682 SetHeightOfRow(refresh_row);
3684 refresh_row = old_cursor.row->next;
3685 refresh_y = old_cursor.y - old_cursor.row->baseline;
3688 cursor = old_cursor; // that undo can restore the right cursor position
3689 LyXParagraph *endpar = old_cursor.par->next;
3690 if (endpar && endpar->GetDepth()) {
3691 while (endpar && endpar->GetDepth()) {
3692 endpar = endpar->LastPhysicalPar()->Next();
3695 SetUndo(Undo::DELETE,
3696 old_cursor.par->previous,
3701 RemoveRow(old_cursor.row);
3703 if (buffer->paragraph == old_cursor.par) {
3704 buffer->paragraph = buffer->paragraph->next;
3706 delete old_cursor.par;
3708 /* Breakagain the next par. Needed
3709 because of the parindent that can
3710 occur or dissappear.
3711 The next row can change its height,
3712 if there is another layout before
3715 BreakAgain(refresh_row);
3716 UpdateCounters(refresh_row->previous);
3722 SetCursorIntern(cursor.par, cursor.pos);
3724 if (sel_cursor.par == old_cursor.par
3725 && sel_cursor.pos == sel_cursor.pos) {
3726 // correct selection
3727 sel_cursor = cursor;
3732 if (old_cursor.par->StripLeadingSpaces(buffer->params.textclass)) {
3733 RedoParagraphs(old_cursor, old_cursor.par->Next());
3735 SetCursorIntern(cursor.par, cursor.pos);
3736 sel_cursor = cursor;
3743 LyXParagraph * LyXText::GetParFromID(int id)
3745 LyXParagraph * result = FirstParagraph();
3746 while (result && result->id() != id)
3747 result = result->next;
3753 bool LyXText::TextUndo()
3755 // returns false if no undo possible
3756 Undo * undo = buffer->undostack.pop();
3761 .push(CreateUndo(undo->kind,
3762 GetParFromID(undo->number_of_before_par),
3763 GetParFromID(undo->number_of_behind_par)));
3765 return TextHandleUndo(undo);
3769 bool LyXText::TextRedo()
3771 // returns false if no redo possible
3772 Undo * undo = buffer->redostack.pop();
3777 .push(CreateUndo(undo->kind,
3778 GetParFromID(undo->number_of_before_par),
3779 GetParFromID(undo->number_of_behind_par)));
3781 return TextHandleUndo(undo);
3785 bool LyXText::TextHandleUndo(Undo * undo)
3787 // returns false if no undo possible
3788 bool result = false;
3790 LyXParagraph * before =
3791 GetParFromID(undo->number_of_before_par);
3792 LyXParagraph * behind =
3793 GetParFromID(undo->number_of_behind_par);
3794 LyXParagraph * tmppar;
3795 LyXParagraph * tmppar2;
3796 LyXParagraph * endpar;
3797 LyXParagraph * tmppar5;
3799 // if there's no before take the beginning
3800 // of the document for redoing
3802 SetCursorIntern(FirstParagraph(), 0);
3804 // replace the paragraphs with the undo informations
3806 LyXParagraph * tmppar3 = undo->par;
3807 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3808 LyXParagraph * tmppar4 = tmppar3;
3810 while (tmppar4->next)
3811 tmppar4 = tmppar4->next;
3812 } // get last undo par
3814 // now remove the old text if there is any
3815 if (before != behind || (!behind && !before)){
3817 tmppar5 = before->next;
3819 tmppar5 = buffer->paragraph;
3821 while (tmppar5 && tmppar5 != behind){
3823 tmppar5 = tmppar5->next;
3824 // a memory optimization for edit: Only layout information
3825 // is stored in the undo. So restore the text informations.
3826 if (undo->kind == Undo::EDIT) {
3827 tmppar2->setContentsFromPar(tmppar);
3828 tmppar->clearContents();
3829 tmppar2 = tmppar2->next;
3834 // put the new stuff in the list if there is one
3837 before->next = tmppar3;
3839 buffer->paragraph = tmppar3;
3840 tmppar3->previous = before;
3844 buffer->paragraph = behind;
3847 tmppar4->next = behind;
3849 behind->previous = tmppar4;
3853 // Set the cursor for redoing
3855 SetCursorIntern(before->FirstSelfrowPar(), 0);
3856 // check wether before points to a closed float and open it if necessary
3857 if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3858 && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3860 while (tmppar4->previous &&
3861 tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3862 tmppar4 = tmppar4->previous;
3863 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3864 tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3865 tmppar4 = tmppar4->next;
3870 // open a cosed footnote at the end if necessary
3871 if (behind && behind->previous &&
3872 behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3873 behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3874 while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3875 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3876 behind = behind->next;
3880 // calculate the endpar for redoing the paragraphs.
3882 if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3883 endpar = behind->LastPhysicalPar()->Next();
3885 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3890 tmppar = GetParFromID(undo->number_of_cursor_par);
3891 RedoParagraphs(cursor, endpar);
3893 SetCursorIntern(tmppar, undo->cursor_pos);
3894 UpdateCounters(cursor.row);
3904 void LyXText::FinishUndo()
3906 // makes sure the next operation will be stored
3907 undo_finished = true;
3911 void LyXText::FreezeUndo()
3913 // this is dangerous and for internal use only
3918 void LyXText::UnFreezeUndo()
3920 // this is dangerous and for internal use only
3921 undo_frozen = false;
3925 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3926 LyXParagraph const * behind) const
3929 buffer->undostack.push(CreateUndo(kind, before, behind));
3930 buffer->redostack.clear();
3934 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3935 LyXParagraph const * behind)
3937 buffer->redostack.push(CreateUndo(kind, before, behind));
3941 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3942 LyXParagraph const * behind) const
3944 int before_number = -1;
3945 int behind_number = -1;
3947 before_number = before->id();
3949 behind_number = behind->id();
3950 // Undo::EDIT and Undo::FINISH are
3951 // always finished. (no overlapping there)
3952 // overlapping only with insert and delete inside one paragraph:
3953 // Nobody wants all removed character
3954 // appear one by one when undoing.
3955 // EDIT is special since only layout information, not the
3956 // contents of a paragaph are stored.
3957 if (!undo_finished && kind != Undo::EDIT &&
3958 kind != Undo::FINISH){
3959 // check wether storing is needed
3960 if (!buffer->undostack.empty() &&
3961 buffer->undostack.top()->kind == kind &&
3962 buffer->undostack.top()->number_of_before_par == before_number &&
3963 buffer->undostack.top()->number_of_behind_par == behind_number ){
3968 // create a new Undo
3969 LyXParagraph * undopar;
3970 LyXParagraph * tmppar;
3971 LyXParagraph * tmppar2;
3973 LyXParagraph * start = 0;
3974 LyXParagraph * end = 0;
3977 start = before->next;
3979 start = FirstParagraph();
3981 end = behind->previous;
3983 end = FirstParagraph();
3989 && start != end->next
3990 && (before != behind || (!before && !behind))) {
3992 tmppar2 = tmppar->Clone();
3993 tmppar2->id(tmppar->id());
3995 // a memory optimization: Just store the layout information
3997 if (kind == Undo::EDIT){
3998 //tmppar2->text.clear();
3999 tmppar2->clearContents();
4004 while (tmppar != end && tmppar->next) {
4005 tmppar = tmppar->next;
4006 tmppar2->next = tmppar->Clone();
4007 tmppar2->next->id(tmppar->id());
4008 // a memory optimization: Just store the layout
4009 // information when only edit
4010 if (kind == Undo::EDIT){
4011 //tmppar2->next->text.clear();
4012 tmppar2->clearContents();
4014 tmppar2->next->previous = tmppar2;
4015 tmppar2 = tmppar2->next;
4019 undopar = 0; // nothing to replace (undo of delete maybe)
4021 int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
4022 int cursor_pos = cursor.par->PositionInParFromPos(cursor.pos);
4024 Undo * undo = new Undo(kind,
4025 before_number, behind_number,
4026 cursor_par, cursor_pos,
4029 undo_finished = false;
4034 void LyXText::SetCursorParUndo()
4036 SetUndo(Undo::FINISH,
4037 cursor.par->ParFromPos(cursor.pos)->previous,
4038 cursor.par->ParFromPos(cursor.pos)->next);
4042 void LyXText::RemoveTableRow(LyXCursor * cur) const
4048 // move to the previous row
4049 int cell_act = NumberOfCell(cur->par, cur->pos);
4052 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
4055 !cur->par->table->IsFirstCell(cell_act)) {
4057 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
4062 // now we have to pay attention if the actual table is the
4063 // main row of TableContRows and if yes to delete all of them
4068 // delete up to the next row
4069 while (cur->pos < cur->par->Last() &&
4071 || !cur->par->table->IsFirstCell(cell_act))) {
4072 while (cur->pos < cur->par->Last() &&
4073 !cur->par->IsNewline(cur->pos))
4074 cur->par->Erase(cur->pos);
4077 if (cur->pos < cur->par->Last())
4078 cur->par->Erase(cur->pos);
4080 if (cur->pos && cur->pos == cur->par->Last()) {
4082 cur->par->Erase(cur->pos); // no newline at very end!
4084 } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
4085 !cur->par->table->IsContRow(cell_org) &&
4086 cur->par->table->IsContRow(cell));
4087 cur->par->table->DeleteRow(cell_org);
4092 bool LyXText::IsEmptyTableCell() const
4094 LyXParagraph::size_type pos = cursor.pos - 1;
4095 while (pos >= 0 && pos < cursor.par->Last()
4096 && !cursor.par->IsNewline(pos))
4098 return cursor.par->IsNewline(pos + 1);
4102 void LyXText::toggleAppendix(){
4103 LyXParagraph * par = cursor.par->FirstPhysicalPar();
4104 bool start = !par->start_of_appendix;
4106 // ensure that we have only one start_of_appendix in this document
4107 LyXParagraph * tmp = FirstParagraph();
4108 for (; tmp; tmp = tmp->next)
4109 tmp->start_of_appendix = 0;
4110 par->start_of_appendix = start;
4112 // we can set the refreshing parameters now
4113 status = LyXText::NEED_MORE_REFRESH;
4115 refresh_row = 0; // not needed for full update
4117 SetCursor(cursor.par, cursor.pos);