]> git.lyx.org Git - lyx.git/blob - src/text2.C
fix the smallcaps drawing, move xfont metrics functions out from LyXFont, move non...
[lyx.git] / src / text2.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include FORMS_H_LOCATION
14
15
16 #ifdef __GNUG__
17 #pragma implementation "lyxtext.h"
18 #endif
19
20 #include "LString.h"
21 #include "lyxparagraph.h"
22 #include "insets/inseterror.h"
23 #include "insets/insetbib.h"
24 #include "insets/insetspecialchar.h"
25 #include "layout.h"
26 #include "LyXView.h"
27 #include "support/textutils.h"
28 #include "undo.h"
29 #include "minibuffer.h"
30 #include "buffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
33 #include "lyxtext.h"
34 #include "gettext.h"
35 #include "BufferView.h"
36 #include "LyXView.h"
37 #include "lyxrow.h"
38 #include "Painter.h"
39 #include "font.h"
40
41 #define FIX_DOUBLE_SPACE 1
42
43 using std::copy;
44 using std::endl;
45 using std::pair;
46
47 LyXText::LyXText(BufferView * bv, int pw, Buffer * p)
48 {
49         owner_ = bv;
50         firstrow = 0;
51         lastrow = 0;
52         currentrow = 0;
53         currentrow_y = 0;
54         paperwidth = pw;
55         parameters = &p->params;
56         params = p;
57         number_of_rows = 0;
58         refresh_y = 0;
59         status = LyXText::UNCHANGED;
60         LyXParagraph * par = p->paragraph;
61         current_font = GetFont(par, 0);
62    
63         height = 0;
64
65         while (par) {
66                 InsertParagraph(par, lastrow);
67                 par = par->Next();
68         }
69
70         // set cursor at the very top position
71         selection = true;           /* these setting is necessary 
72                                        because of the delete-empty-
73                                        paragraph mechanism in
74                                        SetCursor */
75         SetCursor(firstrow->par, 0);
76         sel_cursor = cursor;
77         selection = false;
78         mark_set = false;
79    
80         // no rebreak necessary
81         need_break_row = 0;
82    
83         undo_finished = true;
84         undo_frozen = false;
85
86         // Default layouttype for copy environment type
87         copylayouttype = 0;
88 }
89
90
91 LyXText::~LyXText()
92 {
93         // Delete all rows, this does not touch the paragraphs!
94         Row * tmprow = firstrow;
95         while (firstrow) {
96                 tmprow = firstrow->next;
97                 delete firstrow;
98                 firstrow = tmprow;
99         }
100 }
101
102
103 void LyXText::owner(BufferView * bv)
104 {
105         if (owner_ && bv) lyxerr << "LyXText::owner_ already set!" << endl;
106         owner_ = bv;
107 }
108
109 // Gets the fully instantiated font at a given position in a paragraph
110 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
111 // The difference is that this one is used for displaying, and thus we
112 // are allowed to make cosmetic improvements. For instance make footnotes
113 // smaller. (Asger)
114 // If position is -1, we get the layout font of the paragraph.
115 // If position is -2, we get the font of the manual label of the paragraph.
116 LyXFont LyXText::GetFont(LyXParagraph * par,
117                          LyXParagraph::size_type pos) const
118 {
119         LyXLayout const & layout = 
120                 textclasslist.Style(parameters->textclass, par->GetLayout());
121
122         char par_depth = par->GetDepth();
123         // We specialize the 95% common case:
124         if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
125                 if (pos >= 0){
126                         // 95% goes here
127                         if (layout.labeltype == LABEL_MANUAL
128                             && pos < BeginningOfMainBody(par)) {
129                                 // 1% goes here
130                                 return par->GetFontSettings(pos).
131                                                 realize(layout.reslabelfont);
132                         } else
133                                 return par->GetFontSettings(pos).
134                                                 realize(layout.resfont);
135                 } else {
136                         // 5% goes here.
137                         // process layoutfont for pos == -1 and labelfont for pos < -1
138                         if (pos == -1)
139                                 return layout.resfont;
140                         else
141                                 return layout.reslabelfont;
142                 }
143         }
144
145         // The uncommon case need not be optimized as much
146
147         LyXFont layoutfont, tmpfont;
148
149         if (pos >= 0){
150                 // 95% goes here
151                 if (pos < BeginningOfMainBody(par)) {
152                         // 1% goes here
153                         layoutfont = layout.labelfont;
154                 } else {
155                         // 99% goes here
156                         layoutfont = layout.font;
157                 }
158                 tmpfont = par->GetFontSettings(pos);
159                 tmpfont.realize(layoutfont);
160         } else {
161                 // 5% goes here.
162                 // process layoutfont for pos == -1 and labelfont for pos < -1
163                 if (pos == -1)
164                         tmpfont = layout.font;
165                 else
166                         tmpfont = layout.labelfont;
167         }
168
169         // Resolve against environment font information
170         while (par && par_depth && !tmpfont.resolved()) {
171                 par = par->DepthHook(par_depth - 1);
172                 if (par) {
173                         tmpfont.realize(textclasslist.
174                                         Style(parameters->textclass,
175                                               par->GetLayout()).font);
176                         par_depth = par->GetDepth();
177                 }
178         }
179
180         tmpfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
181
182         // Cosmetic improvement: If this is an open footnote, make the font 
183         // smaller.
184         if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
185             && par->footnotekind == LyXParagraph::FOOTNOTE) {
186                 tmpfont.decSize();
187         }
188
189         return tmpfont;
190 }
191
192
193 void LyXText::SetCharFont(LyXParagraph * par,
194                           LyXParagraph::size_type pos,
195                           LyXFont const & fnt)
196 {
197         LyXFont font(fnt);
198         // Let the insets convert their font
199         if (par->GetChar(pos) == LyXParagraph::META_INSET) {
200                 if (par->GetInset(pos))
201                         font = par->GetInset(pos)->ConvertFont(font);
202         }
203
204         LyXLayout const & layout = textclasslist.Style(parameters->textclass,
205                                            par->GetLayout());
206
207         // Get concrete layout font to reduce against
208         LyXFont layoutfont;
209
210         if (pos < BeginningOfMainBody(par))
211                 layoutfont = layout.labelfont;
212         else
213                 layoutfont = layout.font;
214
215         // Realize against environment font information
216         if (par->GetDepth()){
217                 LyXParagraph * tp = par;
218                 while (!layoutfont.resolved() && tp && tp->GetDepth()) {
219                         tp = tp->DepthHook(tp->GetDepth()-1);
220                         if (tp)
221                                 layoutfont.realize(textclasslist.
222                                                 Style(parameters->textclass,
223                                                       tp->GetLayout()).font);
224                 }
225         }
226
227         layoutfont.realize(textclasslist.TextClass(parameters->textclass).defaultfont());
228
229         if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
230             && par->footnotekind == LyXParagraph::FOOTNOTE) {
231                 layoutfont.decSize();
232         }
233
234         // Now, reduce font against full layout font
235         font.reduce(layoutfont);
236
237         par->SetFont(pos, font);
238 }
239
240
241 /* inserts a new row behind the specified row, increments
242  * the touched counters */
243 void LyXText::InsertRow(Row * row, LyXParagraph * par,
244                         LyXParagraph::size_type pos) const
245 {
246         Row * tmprow = new Row;
247         if (!row) {
248                 tmprow->previous = 0;
249                 tmprow->next = firstrow;
250                 firstrow = tmprow;
251         } else {
252                 tmprow->previous = row;
253                 tmprow->next = row->next;
254                 row->next = tmprow;
255         }
256    
257         if (tmprow->next)
258                 tmprow->next->previous = tmprow;
259    
260         if (tmprow->previous)
261                 tmprow->previous->next = tmprow;
262    
263    
264         tmprow->par = par;
265         tmprow->pos = pos;
266
267         if (row == lastrow)
268                 lastrow = tmprow;
269         ++number_of_rows; // one more row
270 }
271
272
273 // removes the row and reset the touched counters
274 void LyXText::RemoveRow(Row * row) const
275 {
276         /* this must not happen before the currentrow for clear reasons.
277            so the trick is just to set the current row onto the previous
278            row of this row */
279         long unused_y;
280         GetRow(row->par, row->pos, unused_y);
281         currentrow = currentrow->previous;
282         if (currentrow)
283                 currentrow_y -= currentrow->height;
284         else
285                 currentrow_y = 0;
286    
287         if (row->next)
288                 row->next->previous = row->previous;
289         if (!row->previous) {
290                 firstrow = row->next;
291         } else  {
292                 row->previous->next = row->next;
293         }
294         if (row == lastrow)
295                 lastrow = row->previous;
296    
297         height -= row->height;   // the text becomes smaller
298    
299         delete row;
300         --number_of_rows;       // one row less
301 }
302
303
304 // remove all following rows of the paragraph of the specified row.
305 void LyXText::RemoveParagraph(Row * row) const
306 {
307         LyXParagraph * tmppar = row->par;
308         row = row->next;
309     
310         Row * tmprow;
311         while (row && row->par == tmppar) {
312                 tmprow = row->next;
313                 RemoveRow(row);
314                 row = tmprow;
315         }
316 }
317    
318
319 // insert the specified paragraph behind the specified row
320 void LyXText::InsertParagraph(LyXParagraph * par, Row * row) const
321 {
322         InsertRow(row, par, 0);        /* insert a new row, starting 
323                                         * at postition 0 */
324
325         SetCounter(par);  // set the counters
326    
327         // and now append the whole paragraph behind the new row
328         if (!row) {
329                 firstrow->height = 0;
330                 AppendParagraph(firstrow);
331         } else {
332                 row->next->height = 0;
333                 AppendParagraph(row->next);
334         }
335 }
336     
337
338 void LyXText::ToggleFootnote()
339 {
340         LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
341         if (par->next
342             && par->next->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
343                 OpenFootnote();
344                 owner_->owner()->getMiniBuffer()->Set(_("Opened float"));
345         } else {
346                 owner_->owner()->getMiniBuffer()->Set(_("Closed float"));
347                 CloseFootnote();
348         }
349 }
350
351
352 void LyXText::OpenStuff()
353 {
354         if (cursor.pos == 0 && cursor.par->bibkey){
355                 cursor.par->bibkey->Edit(owner_, 0, 0, 0);
356         }
357         else if (cursor.pos < cursor.par->Last() 
358                  && cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
359                  && cursor.par->GetInset(cursor.pos)->Editable()) {
360                 owner_->owner()->getMiniBuffer()
361                         ->Set(cursor.par->GetInset(cursor.pos)->EditMessage());
362                 if (cursor.par->GetInset(cursor.pos)->Editable() != Inset::HIGHLY_EDITABLE)
363                         SetCursorParUndo();
364                 cursor.par->GetInset(cursor.pos)->Edit(owner_, 0, 0, 0);
365         } else {
366                 ToggleFootnote();
367         }
368 }
369
370
371 void LyXText::CloseFootnote()
372 {
373         LyXParagraph * tmppar;
374         LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
375    
376         // if the cursor is not in an open footnote, or 
377         // there is no open footnote in this paragraph, just return.
378         if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
379       
380                 if (!par->next ||
381                     par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
382                         owner_->owner()->getMiniBuffer()
383                                 ->Set(_("Nothing to do"));
384                         return;
385                 }
386    
387                 // ok, move the cursor right before the footnote
388                 // just a little faster than using CursorRight()
389                 for (cursor.pos = 0;
390                      cursor.par->ParFromPos(cursor.pos) != par;
391                      cursor.pos++)
392                         {}
393                 
394                 // now the cursor is at the beginning of the physical par
395                 SetCursor(cursor.par,
396                           cursor.pos +
397                           cursor.par->ParFromPos(cursor.pos)->size());
398         } else  {
399                 /* we are in a footnote, so let us move at the beginning */ 
400                 /* this is just faster than using just CursorLeft() */ 
401        
402                 tmppar = cursor.par;
403                 while (tmppar->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
404                         // just a little bit faster than movin the cursor
405                         tmppar = tmppar->Previous();
406                 }
407                 SetCursor(tmppar, tmppar->Last());
408         }
409    
410         // the cursor must be exactly before the footnote
411         par = cursor.par->ParFromPos(cursor.pos);
412    
413         status = LyXText::NEED_MORE_REFRESH;
414         refresh_row = cursor.row;
415         refresh_y = cursor.y - cursor.row->baseline;
416    
417         tmppar = cursor.par;
418         LyXParagraph * endpar = par->NextAfterFootnote()->Next();
419         Row * row = cursor.row;
420    
421         tmppar->CloseFootnote(cursor.pos);
422
423         while (tmppar != endpar) {
424                 RemoveRow(row->next);
425                 if (row->next)
426                         tmppar = row->next->par;
427                 else
428                         tmppar = 0;
429         }
430    
431         AppendParagraph(cursor.row);
432    
433         SetCursor(cursor.par, cursor.pos);
434         sel_cursor = cursor;
435    
436         // just necessary
437         if (cursor.row->next)
438                 SetHeightOfRow(cursor.row->next);
439 }
440
441
442 /* used in setlayout */
443 // Asger is not sure we want to do this...
444 void LyXText::MakeFontEntriesLayoutSpecific(LyXParagraph * par)
445 {
446    
447         LyXLayout const & layout =
448                 textclasslist.Style(parameters->textclass, par->GetLayout());
449
450         LyXFont layoutfont, tmpfont;
451         for (LyXParagraph::size_type pos = 0;
452              pos < par->Last(); ++pos) {
453                 if (pos < BeginningOfMainBody(par))
454                         layoutfont = layout.labelfont;
455                 else
456                         layoutfont = layout.font;
457       
458                 tmpfont = par->GetFontSettings(pos);
459                 tmpfont.reduce(layoutfont);
460                 par->SetFont(pos, tmpfont);
461         }
462 }
463
464
465 // set layout over selection and make a total rebreak of those paragraphs
466 void LyXText::SetLayout(LyXTextClass::size_type layout)
467 {
468         LyXCursor tmpcursor;
469
470         // if there is no selection just set the layout
471         // of the current paragraph  */
472         if (!selection) {
473                 sel_start_cursor = cursor;  // dummy selection
474                 sel_end_cursor = cursor;
475         }
476
477         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
478         LyXParagraph * undoendpar = endpar;
479
480         if (endpar && endpar->GetDepth()) {
481                 while (endpar && endpar->GetDepth()) {
482                         endpar = endpar->LastPhysicalPar()->Next();
483                         undoendpar = endpar;
484                 }
485         }
486         else if (endpar) {
487                 endpar = endpar->Next(); // because of parindents etc.
488         }
489    
490         SetUndo(Undo::EDIT, 
491                 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous, 
492                 undoendpar);
493
494         tmpcursor = cursor;                    /* store the current cursor  */
495
496         /* ok we have a selection. This is always between sel_start_cursor
497          * and sel_end cursor */ 
498         cursor = sel_start_cursor;
499    
500         LyXLayout const & lyxlayout =
501                 textclasslist.Style(parameters->textclass, layout);
502    
503         while (cursor.par != sel_end_cursor.par) {
504                 if (cursor.par->footnoteflag ==
505                     sel_start_cursor.par->footnoteflag) {
506                         cursor.par->SetLayout(layout);
507                         MakeFontEntriesLayoutSpecific(cursor.par);
508                         LyXParagraph* fppar = cursor.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                                 cursor.par->SetLabelWidthString(lyxlayout.labelstring());
515                         if (lyxlayout.labeltype != LABEL_BIBLIO
516                             && fppar->bibkey) {
517                                 delete fppar->bibkey;
518                                 fppar->bibkey = 0;
519                         }
520                 }
521                 cursor.par = cursor.par->Next();
522         }
523         if (cursor.par->footnoteflag ==
524             sel_start_cursor.par->footnoteflag) {
525                 cursor.par->SetLayout(layout);
526                 MakeFontEntriesLayoutSpecific(cursor.par);
527                 LyXParagraph* fppar = cursor.par->FirstPhysicalPar();
528                 fppar->added_space_top = lyxlayout.fill_top ?
529                         VSpace(VSpace::VFILL) : VSpace(VSpace::NONE);
530                 fppar->added_space_bottom = lyxlayout.fill_bottom ? 
531                         VSpace(VSpace::VFILL) : VSpace(VSpace::NONE); 
532                 if (lyxlayout.margintype == MARGIN_MANUAL)
533                         cursor.par->SetLabelWidthString(lyxlayout.labelstring());
534                 if (lyxlayout.labeltype != LABEL_BIBLIO
535                     && fppar->bibkey) {
536                         delete fppar->bibkey;
537                         fppar->bibkey = 0;
538                 }
539         }
540    
541         RedoParagraphs(sel_start_cursor, endpar);
542    
543         // we have to reset the selection, because the
544         // geometry could have changed */ 
545         SetCursor(sel_start_cursor.par, sel_start_cursor.pos, false);
546         sel_cursor = cursor;
547         SetCursor(sel_end_cursor.par, sel_end_cursor.pos, false);
548         UpdateCounters(cursor.row);
549         ClearSelection();
550         SetSelection();
551         SetCursor(tmpcursor.par, tmpcursor.pos, true);
552 }
553
554
555 // increment depth over selection and
556 // make a total rebreak of those paragraphs
557 void  LyXText::IncDepth()
558 {
559         // If there is no selection, just use the current paragraph
560         if (!selection) {
561                 sel_start_cursor = cursor; // dummy selection
562                 sel_end_cursor = cursor;
563         }
564
565         // We end at the next paragraph with depth 0
566         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
567         LyXParagraph * undoendpar = endpar;
568
569         if (endpar && endpar->GetDepth()) {
570                 while (endpar && endpar->GetDepth()) {
571                         endpar = endpar->LastPhysicalPar()->Next();
572                         undoendpar = endpar;
573                 }
574         }
575         else if (endpar) {
576                 endpar = endpar->Next(); // because of parindents etc.
577         }
578         
579         SetUndo(Undo::EDIT, 
580                 sel_start_cursor
581                 .par->ParFromPos(sel_start_cursor.pos)->previous, 
582                 undoendpar);
583
584         LyXCursor tmpcursor = cursor; // store the current cursor
585
586         // ok we have a selection. This is always between sel_start_cursor
587         // and sel_end cursor
588         cursor = sel_start_cursor;
589    
590         bool anything_changed = false;
591    
592         while (true) {
593                 // NOTE: you can't change the depth of a bibliography entry
594                 if (cursor.par->footnoteflag ==
595                     sel_start_cursor.par->footnoteflag
596                     && textclasslist.Style(parameters->textclass,
597                                       cursor.par->GetLayout()
598                                      ).labeltype != LABEL_BIBLIO) {
599                         LyXParagraph * prev =
600                                 cursor.par->FirstPhysicalPar()->Previous();
601                         if (prev 
602                             && (prev->GetDepth() - cursor.par->GetDepth() > 0
603                                 || (prev->GetDepth() == cursor.par->GetDepth()
604                                     && textclasslist.Style(parameters->textclass,
605                                                       prev->GetLayout()).isEnvironment()))) {
606                                 cursor.par->FirstPhysicalPar()->depth++;
607                                 anything_changed = true;
608                                 }
609                 }
610                 if (cursor.par == sel_end_cursor.par)
611                        break;
612                 cursor.par = cursor.par->Next();
613         }
614    
615         // if nothing changed set all depth to 0
616         if (!anything_changed) {
617                 cursor = sel_start_cursor;
618                 while (cursor.par != sel_end_cursor.par) {
619                         cursor.par->FirstPhysicalPar()->depth = 0;
620                         cursor.par = cursor.par->Next();
621                 }
622                 if (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag)
623                         cursor.par->FirstPhysicalPar()->depth = 0;
624         }
625    
626         RedoParagraphs(sel_start_cursor, endpar);
627    
628         // we have to reset the selection, because the
629         // geometry could have changed
630         SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
631         sel_cursor = cursor;
632         SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
633         UpdateCounters(cursor.row);
634         ClearSelection();
635         SetSelection();
636         SetCursor(tmpcursor.par, tmpcursor.pos);
637 }
638
639
640 // decrement depth over selection and
641 // make a total rebreak of those paragraphs
642 void  LyXText::DecDepth()
643 {
644         // if there is no selection just set the layout
645         // of the current paragraph
646         if (!selection) {
647                 sel_start_cursor = cursor; // dummy selection
648                 sel_end_cursor = cursor;
649         }
650    
651         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
652         LyXParagraph * undoendpar = endpar;
653
654         if (endpar && endpar->GetDepth()) {
655                 while (endpar && endpar->GetDepth()) {
656                         endpar = endpar->LastPhysicalPar()->Next();
657                         undoendpar = endpar;
658                 }
659         }
660         else if (endpar) {
661                 endpar = endpar->Next(); // because of parindents etc.
662         }
663    
664         SetUndo(Undo::EDIT, 
665                 sel_start_cursor
666                 .par->ParFromPos(sel_start_cursor.pos)->previous, 
667                 undoendpar);
668
669         LyXCursor tmpcursor = cursor; // store the current cursor
670
671         // ok we have a selection. This is always between sel_start_cursor
672         // and sel_end cursor
673         cursor = sel_start_cursor;
674
675         while (true) {
676                 if (cursor.par->footnoteflag ==
677                     sel_start_cursor.par->footnoteflag) {
678                         if (cursor.par->FirstPhysicalPar()->depth)
679                                 cursor.par->FirstPhysicalPar()->depth--;
680                 }
681                 if (cursor.par == sel_end_cursor.par)
682                         break;
683                 cursor.par = cursor.par->Next();
684         }
685
686         RedoParagraphs(sel_start_cursor, endpar);
687    
688         // we have to reset the selection, because the
689         // geometry could have changed
690         SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
691         sel_cursor = cursor;
692         SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
693         UpdateCounters(cursor.row);
694         ClearSelection();
695         SetSelection();
696         SetCursor(tmpcursor.par, tmpcursor.pos);
697 }
698
699
700 // set font over selection and make a total rebreak of those paragraphs
701 void LyXText::SetFont(LyXFont const & font, bool toggleall)
702 {
703         // if there is no selection just set the current_font
704         if (!selection) {
705                 // Determine basis font
706                 LyXFont layoutfont;
707                 if (cursor.pos < BeginningOfMainBody(cursor.par))
708                         layoutfont = GetFont(cursor.par, -2);
709                 else
710                         layoutfont = GetFont(cursor.par, -1);
711                 // Update current font
712                 real_current_font.update(font, parameters->language_info, toggleall);
713
714                 // Reduce to implicit settings
715                 current_font = real_current_font;
716                 current_font.reduce(layoutfont);
717                 // And resolve it completely
718                 real_current_font.realize(layoutfont);
719                 return;
720         }
721
722         LyXCursor tmpcursor = cursor; // store the current cursor
723    
724         // ok we have a selection. This is always between sel_start_cursor
725         // and sel_end cursor
726    
727         SetUndo(Undo::EDIT, 
728                 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous, 
729                 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next); 
730         cursor = sel_start_cursor;
731         while (cursor.par != sel_end_cursor.par ||
732                (cursor.par->footnoteflag == sel_start_cursor.par->footnoteflag
733                 && cursor.pos < sel_end_cursor.pos)) 
734         {
735                 if (cursor.pos < cursor.par->Last()
736                     && cursor.par->footnoteflag
737                     == sel_start_cursor.par->footnoteflag) {
738                         // an open footnote should behave
739                         // like a closed one
740                         LyXFont newfont = GetFont(cursor.par, cursor.pos);
741                         newfont.update(font, parameters->language_info, toggleall);
742                         SetCharFont(cursor.par, cursor.pos, newfont);
743                         cursor.pos++;
744                 } else {
745                         cursor.pos = 0;
746                         cursor.par = cursor.par->Next();
747                 }
748         }
749    
750         RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
751    
752         // we have to reset the selection, because the
753         // geometry could have changed
754         SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
755         sel_cursor = cursor;
756         SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
757         ClearSelection();
758         SetSelection();
759         SetCursor(tmpcursor.par, tmpcursor.pos);
760 }
761
762
763 void LyXText::RedoHeightOfParagraph(LyXCursor const & cur)
764 {
765         Row * tmprow = cur.row;
766         long y = cur.y - tmprow->baseline;
767
768         SetHeightOfRow(tmprow);
769         LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
770         // find the first row of the paragraph
771         if (first_phys_par != tmprow->par)
772                 while (tmprow->previous
773                        && tmprow->previous->par != first_phys_par) {
774                         tmprow = tmprow->previous;
775                         y -= tmprow->height;
776                         SetHeightOfRow(tmprow);
777                 }
778         while (tmprow->previous && tmprow->previous->par == first_phys_par) {
779                 tmprow = tmprow->previous;
780                 y -= tmprow->height;
781                 SetHeightOfRow(tmprow);
782         }
783         
784         // we can set the refreshing parameters now
785         status = LyXText::NEED_MORE_REFRESH;
786         refresh_y = y;
787         refresh_row = tmprow;
788         SetCursor(cur.par, cur.pos);
789 }
790
791
792 void LyXText::RedoDrawingOfParagraph(LyXCursor const & cur)
793 {
794         Row * tmprow = cur.row;
795    
796         long y = cur.y - tmprow->baseline;
797         SetHeightOfRow(tmprow);
798         LyXParagraph * first_phys_par = tmprow->par->FirstPhysicalPar();
799         // find the first row of the paragraph
800         if (first_phys_par != tmprow->par)
801                 while (tmprow->previous && tmprow->previous->par != first_phys_par)  {
802                         tmprow = tmprow->previous;
803                         y -= tmprow->height;
804                 }
805         while (tmprow->previous && tmprow->previous->par == first_phys_par)  {
806                 tmprow = tmprow->previous;
807                 y -= tmprow->height;
808         }
809    
810         // we can set the refreshing parameters now
811         if (status == LyXText::UNCHANGED || y < refresh_y) {
812                 refresh_y = y;
813                 refresh_row = tmprow;
814         }
815         status = LyXText::NEED_MORE_REFRESH;
816         SetCursor(cur.par, cur.pos);
817 }
818
819
820 /* deletes and inserts again all paragaphs between the cursor
821 * and the specified par 
822 * This function is needed after SetLayout and SetFont etc. */
823 void LyXText::RedoParagraphs(LyXCursor const & cur,
824                              LyXParagraph const * endpar) const
825 {
826         Row * tmprow2;
827         LyXParagraph * tmppar, * first_phys_par;
828    
829         Row * tmprow = cur.row;
830    
831         long y = cur.y - tmprow->baseline;
832    
833         if (!tmprow->previous){
834                 first_phys_par = FirstParagraph();   // a trick/hack for UNDO
835         } else {
836                 first_phys_par = tmprow->par->FirstPhysicalPar();
837                 // find the first row of the paragraph
838                 if (first_phys_par != tmprow->par)
839                         while (tmprow->previous &&
840                                (tmprow->previous->par != first_phys_par)) {
841                                 tmprow = tmprow->previous;
842                                 y -= tmprow->height;
843                         }
844                 while (tmprow->previous
845                        && tmprow->previous->par == first_phys_par) {
846                         tmprow = tmprow->previous;
847                         y -= tmprow->height;
848                 }
849         }
850    
851         // we can set the refreshing parameters now
852         status = LyXText::NEED_MORE_REFRESH;
853         refresh_y = y;
854         refresh_row = tmprow->previous;  /* the real refresh row will
855                                             be deleted, so I store
856                                             the previous here */ 
857         // remove it
858         if (tmprow->next)
859                 tmppar = tmprow->next->par;
860         else
861                 tmppar = 0;
862         while (tmppar != endpar) {
863                 RemoveRow(tmprow->next);
864                 if (tmprow->next)
865                         tmppar = tmprow->next->par;
866                 else
867                         tmppar = 0;
868         }  
869    
870         // remove the first one
871         tmprow2 = tmprow;     /* this is because tmprow->previous
872                                  can be 0 */
873         tmprow = tmprow->previous;
874         RemoveRow(tmprow2);
875    
876         tmppar = first_phys_par;
877
878         do {
879                 if (tmppar) {
880                         InsertParagraph(tmppar, tmprow);
881                         if (!tmprow)
882                                 tmprow = firstrow;
883                         while (tmprow->next && tmprow->next->par == tmppar)
884                                 tmprow = tmprow->next;
885                         tmppar = tmppar->Next();
886                 }
887         } while (tmppar != endpar);
888    
889         // this is because of layout changes
890         if (refresh_row) {
891                 refresh_y -= refresh_row->height;
892                 SetHeightOfRow(refresh_row);   
893         } else {
894                 refresh_row = firstrow;
895                 refresh_y = 0;
896                 SetHeightOfRow(refresh_row);   
897         }
898    
899         if (tmprow && tmprow->next)
900                 SetHeightOfRow(tmprow->next);
901 }
902
903
904 int LyXText::FullRebreak()
905 {
906         if (need_break_row) {
907                 BreakAgain(need_break_row);
908                 need_break_row = 0;
909                 return 1;
910         }
911         return 0;
912 }
913
914
915 /* important for the screen */
916
917
918 /* the cursor set functions have a special mechanism. When they
919 * realize, that you left an empty paragraph, they will delete it.
920 * They also delet the corresponding row */
921    
922 // need the selection cursor:
923 void LyXText::SetSelection()
924 {
925         if (!selection) {
926                 last_sel_cursor = sel_cursor;
927                 sel_start_cursor = sel_cursor;
928                 sel_end_cursor = sel_cursor;
929         }
930    
931         selection = true;
932    
933         // first the toggling area
934         if (cursor.y < last_sel_cursor.y ||
935             (cursor.y == last_sel_cursor.y && cursor.x < last_sel_cursor.x)) {
936                 toggle_end_cursor = last_sel_cursor;
937                 toggle_cursor = cursor;
938         }
939         else {
940                 toggle_end_cursor = cursor;
941                 toggle_cursor = last_sel_cursor;
942         }
943    
944         last_sel_cursor = cursor;
945    
946         // and now the whole selection
947
948         if (sel_cursor.par == cursor.par)
949            if (sel_cursor.pos < cursor.pos) {
950                 sel_end_cursor = cursor;
951                 sel_start_cursor = sel_cursor;
952         } else {
953                 sel_end_cursor = sel_cursor; 
954                 sel_start_cursor = cursor;
955         }
956         else if (sel_cursor.y < cursor.y ||
957             (sel_cursor.y == cursor.y && sel_cursor.x < cursor.x)) {
958                 sel_end_cursor = cursor;
959                 sel_start_cursor = sel_cursor;
960         }
961         else {
962                 sel_end_cursor = sel_cursor; 
963                 sel_start_cursor = cursor;
964         }
965    
966         // a selection with no contents is not a selection
967         if (sel_start_cursor.x == sel_end_cursor.x && 
968             sel_start_cursor.y == sel_end_cursor.y)
969                 selection = false;
970 }
971
972
973 void LyXText::ClearSelection() const
974 {
975         selection = false;
976         mark_set = false;
977 }
978
979
980 void LyXText::CursorHome() const
981 {
982         SetCursor(cursor.par, cursor.row->pos);
983 }
984
985
986 void  LyXText::CursorEnd() const
987 {
988         if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
989                 SetCursor(cursor.par, RowLast(cursor.row) + 1);
990         else {
991                 if (cursor.par->Last() && 
992                     (cursor.par->GetChar(RowLast(cursor.row)) == ' '
993                      || cursor.par->IsNewline(RowLast(cursor.row))))
994                         SetCursor(cursor.par, RowLast(cursor.row));
995                 else
996                         SetCursor(cursor.par, RowLast(cursor.row) + 1);
997         }
998         if (cursor.par->table) {
999                 int cell = NumberOfCell(cursor.par, cursor.pos);
1000                 if (cursor.par->table->RowHasContRow(cell) &&
1001                     cursor.par->table->CellHasContRow(cell)<0) {
1002                         if (!cursor.row->next || cursor.row->next->par != cursor.row->par)
1003                                 SetCursor(cursor.par, RowLast(cursor.row) + 1);
1004                         else {
1005                                 if (cursor.par->Last() && 
1006                                     (cursor.par->GetChar(RowLast(cursor.row)) == ' '
1007                                      || cursor.par->IsNewline(RowLast(cursor.row))))
1008                                         SetCursor(cursor.par, RowLast(cursor.row));
1009                                 else
1010                                         SetCursor(cursor.par, RowLast(cursor.row) + 1);
1011                         }
1012                 }
1013         }
1014 }
1015
1016
1017 void  LyXText::CursorTop() const
1018 {
1019         while (cursor.par->Previous())
1020                 cursor.par = cursor.par->Previous();
1021         SetCursor(cursor.par, 0);
1022 }
1023
1024
1025 void  LyXText::CursorBottom() const
1026 {
1027         while (cursor.par->Next())
1028                 cursor.par = cursor.par->Next();
1029         SetCursor(cursor.par, cursor.par->Last());
1030 }
1031    
1032    
1033 /* returns a pointer to the row near the specified y-coordinate
1034 * (relative to the whole text). y is set to the real beginning
1035 * of this row */ 
1036 Row * LyXText::GetRowNearY(long & y) const
1037 {
1038         Row * tmprow;
1039         long tmpy;
1040    
1041         if (currentrow) {
1042                 tmprow = currentrow;
1043                 tmpy = currentrow_y;
1044         } else {
1045                 tmprow = firstrow;
1046                 tmpy = 0;
1047         }
1048
1049         if (tmpy <= y)
1050                 while (tmprow->next && tmpy + tmprow->height <= y) {
1051                         tmpy += tmprow->height;
1052                         tmprow = tmprow->next;
1053                 }
1054         else
1055                 while (tmprow->previous && tmpy > y) {
1056                         tmprow = tmprow->previous;
1057                         tmpy -= tmprow->height;
1058                 }
1059
1060         currentrow = tmprow;
1061         currentrow_y = tmpy;
1062
1063         y = tmpy;   // return the real y
1064         return tmprow;
1065 }
1066    
1067
1068 void LyXText::ToggleFree(LyXFont const & font, bool toggleall)
1069 {
1070         // If the mask is completely neutral, tell user
1071         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1072                 // Could only happen with user style
1073                 owner_->owner()->getMiniBuffer()
1074                         ->Set(_("No font change defined. Use Character under"
1075                                   " the Layout menu to define font change."));
1076                 return;
1077         }
1078
1079         // Try implicit word selection
1080         LyXCursor resetCursor = cursor;
1081         int implicitSelection = SelectWordWhenUnderCursor();
1082
1083         // Set font
1084         SetFont(font, toggleall);
1085
1086         /* Implicit selections are cleared afterwards and cursor is set to the
1087            original position. */
1088         if (implicitSelection) {
1089                 ClearSelection();
1090                 cursor = resetCursor;
1091                 SetCursor( cursor.par, cursor.pos );
1092                 sel_cursor = cursor;
1093         }
1094 }
1095
1096
1097 LyXParagraph::size_type LyXText::BeginningOfMainBody(LyXParagraph * par) const
1098 {
1099         if (textclasslist.Style(parameters->textclass,
1100                                 par->GetLayout()).labeltype != LABEL_MANUAL)
1101                 return 0;
1102         else
1103                 return par->BeginningOfMainBody();
1104 }
1105
1106
1107 /* if there is a selection, reset every environment you can find
1108 * in the selection, otherwise just the environment you are in */ 
1109 void LyXText::MeltFootnoteEnvironment()
1110 {
1111         LyXParagraph * tmppar, * firsttmppar;
1112    
1113         ClearSelection();
1114    
1115         /* is is only allowed, if the cursor is IN an open footnote.
1116          * Otherwise it is too dangerous */ 
1117         if (cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
1118                 return;
1119    
1120         SetUndo(Undo::FINISH, 
1121                 cursor.par->PreviousBeforeFootnote()->previous,
1122                 cursor.par->NextAfterFootnote()->next);
1123
1124         /* ok, move to the beginning of the footnote. */ 
1125         while (cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
1126                 cursor.par = cursor.par->Previous();
1127    
1128         SetCursor(cursor.par, cursor.par->Last());
1129         /* this is just faster than using CursorLeft(); */ 
1130    
1131         firsttmppar = cursor.par->ParFromPos(cursor.pos);
1132         tmppar = firsttmppar;
1133         /* tmppar is now the paragraph right before the footnote */
1134
1135         bool first_footnote_par_is_not_empty = tmppar->next->size();
1136    
1137         while (tmppar->next
1138                && tmppar->next->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
1139                 tmppar = tmppar->next;   /* I use next instead of Next(),
1140                                           * because there cannot be any
1141                                           * footnotes in a footnote
1142                                           * environment */
1143                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
1144       
1145                 /* remember the captions and empty paragraphs */
1146                 if ((textclasslist.Style(parameters->textclass,
1147                                          tmppar->GetLayout())
1148                      .labeltype == LABEL_SENSITIVE)
1149                     || !tmppar->Last())
1150                         tmppar->SetLayout(0);
1151         }
1152    
1153         // now we will paste the ex-footnote, if the layouts allow it
1154         // first restore the layout of the paragraph right behind
1155         // the footnote
1156         if (tmppar->next) 
1157                 tmppar->next->MakeSameLayout(cursor.par);
1158
1159         // first the end
1160         if ((!tmppar->GetLayout() && !tmppar->table)
1161             || (tmppar->Next()
1162                 && (!tmppar->Next()->Last()
1163                     || tmppar->Next()->HasSameLayout(tmppar)))) {
1164                 if (tmppar->Next()->Last()
1165                     && tmppar->Next()->IsLineSeparator(0))
1166                         tmppar->Next()->Erase(0);
1167                 tmppar->PasteParagraph();
1168         }
1169
1170         tmppar = tmppar->Next();  /* make sure tmppar cannot be touched
1171                                    * by the pasting of the beginning */
1172
1173         /* then the beginning */ 
1174         /* if there is no space between the text and the footnote, so we insert
1175          * a blank 
1176          * (only if the previous par and the footnotepar are not empty!) */
1177         if ((!firsttmppar->next->GetLayout() && !firsttmppar->next->table)
1178             || firsttmppar->HasSameLayout(firsttmppar->next)) {
1179                 if (firsttmppar->size()
1180                     && !firsttmppar->IsSeparator(firsttmppar->size() - 1)
1181                     && first_footnote_par_is_not_empty) {
1182                         firsttmppar->next->InsertChar(0, ' ');
1183                 }
1184                 firsttmppar->PasteParagraph();
1185         }
1186    
1187         /* now redo the paragaphs */
1188         RedoParagraphs(cursor, tmppar);
1189    
1190         SetCursor(cursor.par, cursor.pos);
1191    
1192         /* sometimes it can happen, that there is a counter change */ 
1193         Row * row = cursor.row;
1194         while (row->next && row->par != tmppar && row->next->par != tmppar)
1195                 row = row->next;
1196         UpdateCounters(row);
1197    
1198    
1199         ClearSelection();
1200 }
1201
1202
1203 /* the DTP switches for paragraphs. LyX will store them in the 
1204 * first physicla paragraph. When a paragraph is broken, the top settings 
1205 * rest, the bottom settings are given to the new one. So I can make shure, 
1206 * they do not duplicate themself and you cannnot make dirty things with 
1207 * them!  */ 
1208
1209 void LyXText::SetParagraph(bool line_top, bool line_bottom,
1210                            bool pagebreak_top, bool pagebreak_bottom,
1211                            VSpace const & space_top,
1212                            VSpace const & space_bottom,
1213                            LyXAlignment align, 
1214                            string labelwidthstring,
1215                            bool noindent) 
1216 {
1217         LyXCursor tmpcursor = cursor;
1218         if (!selection) {
1219                 sel_start_cursor = cursor;
1220                 sel_end_cursor = cursor;
1221         }
1222
1223         // make sure that the depth behind the selection are restored, too
1224         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1225         LyXParagraph * undoendpar = endpar;
1226
1227         if (endpar && endpar->GetDepth()) {
1228                 while (endpar && endpar->GetDepth()) {
1229                         endpar = endpar->LastPhysicalPar()->Next();
1230                         undoendpar = endpar;
1231                 }
1232         }
1233         else if (endpar) {
1234                 endpar = endpar->Next(); // because of parindents etc.
1235         }
1236    
1237         SetUndo(Undo::EDIT, 
1238                 sel_start_cursor
1239                 .par->ParFromPos(sel_start_cursor.pos)->previous, 
1240                 undoendpar);
1241
1242         
1243         LyXParagraph * tmppar = sel_end_cursor.par;
1244         while (tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1245                 SetCursor(tmppar->FirstPhysicalPar(), 0);
1246                 status = LyXText::NEED_MORE_REFRESH;
1247                 refresh_row = cursor.row;
1248                 refresh_y = cursor.y - cursor.row->baseline;
1249                 if (cursor.par->footnoteflag ==
1250                     sel_start_cursor.par->footnoteflag) {
1251                         cursor.par->line_top = line_top;
1252                         cursor.par->line_bottom = line_bottom;
1253                         cursor.par->pagebreak_top = pagebreak_top;
1254                         cursor.par->pagebreak_bottom = pagebreak_bottom;
1255                         cursor.par->added_space_top = space_top;
1256                         cursor.par->added_space_bottom = space_bottom;
1257                         // does the layout allow the new alignment?
1258                         if (align == LYX_ALIGN_LAYOUT)
1259                                 align = textclasslist
1260                                         .Style(parameters->textclass,
1261                                                cursor.par->GetLayout()).align;
1262                         if (align & textclasslist
1263                             .Style(parameters->textclass,
1264                                    cursor.par->GetLayout()).alignpossible) {
1265                                 if (align == textclasslist
1266                                     .Style(parameters->textclass,
1267                                            cursor.par->GetLayout()).align)
1268                                         cursor.par->align = LYX_ALIGN_LAYOUT;
1269                                 else
1270                                         cursor.par->align = align;
1271                         }
1272                         cursor.par->SetLabelWidthString(labelwidthstring);
1273                         cursor.par->noindent = noindent;
1274                 }
1275                 
1276                 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1277         }
1278         
1279         RedoParagraphs(sel_start_cursor, endpar);
1280         
1281         ClearSelection();
1282         SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1283         sel_cursor = cursor;
1284         SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1285         SetSelection();
1286         SetCursor(tmpcursor.par, tmpcursor.pos);
1287 }
1288
1289
1290 void LyXText::SetParagraphExtraOpt(int type,
1291                                    char const * width,
1292                                    char const * widthp,
1293                                    int alignment, bool hfill,
1294                                    bool start_minipage)
1295 {
1296         LyXCursor tmpcursor = cursor;
1297         LyXParagraph * tmppar;
1298         if (!selection) {
1299                 sel_start_cursor = cursor;
1300                 sel_end_cursor = cursor;
1301         }
1302
1303         // make sure that the depth behind the selection are restored, too
1304         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1305         LyXParagraph * undoendpar = endpar;
1306
1307         if (endpar && endpar->GetDepth()) {
1308                 while (endpar && endpar->GetDepth()) {
1309                         endpar = endpar->LastPhysicalPar()->Next();
1310                         undoendpar = endpar;
1311                 }
1312         }
1313         else if (endpar) {
1314                 endpar = endpar->Next(); // because of parindents etc.
1315         }
1316    
1317         SetUndo(Undo::EDIT, 
1318                 sel_start_cursor
1319                 .par->ParFromPos(sel_start_cursor.pos)->previous, 
1320                 undoendpar);
1321         
1322         tmppar = sel_end_cursor.par;
1323         while(tmppar != sel_start_cursor.par->FirstPhysicalPar()->Previous()) {
1324                 SetCursor(tmppar->FirstPhysicalPar(), 0);
1325                 status = LyXText::NEED_MORE_REFRESH;
1326                 refresh_row = cursor.row;
1327                 refresh_y = cursor.y - cursor.row->baseline;
1328                 if (cursor.par->footnoteflag ==
1329                     sel_start_cursor.par->footnoteflag) {
1330                         if (type == LyXParagraph::PEXTRA_NONE) {
1331                                 if (cursor.par->pextra_type != LyXParagraph::PEXTRA_NONE) {
1332                                         cursor.par->UnsetPExtraType();
1333                                         cursor.par->pextra_type = LyXParagraph::PEXTRA_NONE;
1334                                 }
1335                         } else {
1336                                 cursor.par->SetPExtraType(type, width, widthp);
1337                                 cursor.par->pextra_hfill = hfill;
1338                                 cursor.par->pextra_start_minipage = start_minipage;
1339                                 cursor.par->pextra_alignment = alignment;
1340                         }
1341                 }
1342                 tmppar = cursor.par->FirstPhysicalPar()->Previous();
1343         }
1344         RedoParagraphs(sel_start_cursor, endpar);
1345         ClearSelection();
1346         SetCursor(sel_start_cursor.par, sel_start_cursor.pos);
1347         sel_cursor = cursor;
1348         SetCursor(sel_end_cursor.par, sel_end_cursor.pos);
1349         SetSelection();
1350         SetCursor(tmpcursor.par, tmpcursor.pos);
1351 }
1352
1353
1354 char loweralphaCounter(int n)
1355 {
1356         if (n < 1 || n > 26)
1357                 return '?';
1358         else
1359                 return 'a' + n - 1;
1360 }
1361
1362 char alphaCounter(int n)
1363 {
1364         if (n < 1 || n > 26)
1365                 return '?';
1366         else
1367                 return 'A' + n - 1;
1368 }
1369
1370 char hebrewCounter(int n)
1371 {
1372         static const char hebrew[22] = {
1373                 'à', 'á', 'â', 'ã', 'ä', 'Ã¥', 'æ', 'ç', 'è',
1374                 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö', 
1375                 '÷', 'ø', 'ù', 'ú'
1376         };
1377         if (n < 1 || n > 22)
1378                 return '?';
1379         else
1380                 return hebrew[n-1];
1381 }
1382
1383 static char const * romanCounter(int n)
1384 {
1385         static char const * roman[20] = {
1386                 "i",   "ii",  "iii", "iv", "v",
1387                 "vi",  "vii", "viii", "ix", "x",
1388                 "xi",  "xii", "xiii", "xiv", "xv",
1389                 "xvi", "xvii", "xviii", "xix", "xx"
1390         };
1391         if (n < 1 || n > 20)
1392                 return "??";
1393         else
1394                 return roman[n-1];
1395 }
1396
1397 // set the counter of a paragraph. This includes the labels
1398 void LyXText::SetCounter(LyXParagraph * par) const
1399 {
1400         // this is only relevant for the beginning of paragraph
1401         par = par->FirstPhysicalPar();
1402
1403         LyXLayout const & layout = textclasslist.Style(parameters->textclass, 
1404                                                        par->GetLayout());
1405
1406         LyXTextClass const & textclass =
1407                 textclasslist.TextClass(parameters->textclass);
1408
1409         /* copy the prev-counters to this one, unless this is the start of a 
1410            footnote or of a bibliography or the very first paragraph */
1411         if (par->Previous()
1412             && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE 
1413                     && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1414                     && par->footnotekind == LyXParagraph::FOOTNOTE)
1415             && !(textclasslist.Style(parameters->textclass,
1416                                 par->Previous()->GetLayout()
1417                                 ).labeltype != LABEL_BIBLIO
1418                  && layout.labeltype == LABEL_BIBLIO)) {
1419                 for (int i = 0; i < 10; ++i) {
1420                         par->setCounter(i, par->Previous()->GetFirstCounter(i));
1421                 }
1422                 par->appendix = par->Previous()->FirstPhysicalPar()->appendix;
1423                 if (!par->appendix && par->start_of_appendix){
1424                   par->appendix = true;
1425                   for (int i = 0; i < 10; ++i) {
1426                     par->setCounter(i, 0);
1427                   }  
1428                 }
1429                 par->enumdepth = par->Previous()->FirstPhysicalPar()->enumdepth;
1430                 par->itemdepth = par->Previous()->FirstPhysicalPar()->itemdepth;
1431         }
1432         else {
1433                 for (int i = 0; i < 10; ++i) {
1434                         par->setCounter(i, 0);
1435                 }  
1436                 par->appendix = par->start_of_appendix;
1437                 par->enumdepth = 0;
1438                 par->itemdepth = 0;
1439         }
1440
1441         // if this is an open marginnote and this is the first
1442         // entry in the marginnote and the enclosing
1443         // environment is an enum/item then correct for the
1444         // LaTeX behaviour (ARRae)
1445         if(par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1446            && par->footnotekind == LyXParagraph::MARGIN
1447            && par->Previous()
1448            && par->Previous()->footnoteflag != LyXParagraph::OPEN_FOOTNOTE
1449            && (par->PreviousBeforeFootnote()
1450                && textclasslist.Style(parameters->textclass,
1451                                  par->PreviousBeforeFootnote()->GetLayout()
1452                                  ).labeltype >= LABEL_COUNTER_ENUMI)) {
1453                 // Any itemize or enumerate environment in a marginnote
1454                 // that is embedded in an itemize or enumerate
1455                 // paragraph is seen by LaTeX as being at a deeper
1456                 // level within that enclosing itemization/enumeration
1457                 // even if there is a "standard" layout at the start of
1458                 // the marginnote.
1459                 par->enumdepth++;
1460                 par->itemdepth++;
1461         }
1462
1463         /* Maybe we have to increment the enumeration depth.
1464          * BUT, enumeration in a footnote is considered in isolation from its
1465          *      surrounding paragraph so don't increment if this is the
1466          *      first line of the footnote
1467          * AND, bibliographies can't have their depth changed ie. they
1468          *      are always of depth 0
1469          */
1470         if (par->Previous()
1471             && par->Previous()->GetDepth() < par->GetDepth()
1472             && textclasslist.Style(parameters->textclass,
1473                               par->Previous()->GetLayout()
1474                              ).labeltype == LABEL_COUNTER_ENUMI
1475             && par->enumdepth < 3
1476             && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE 
1477                     && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1478                     && par->footnotekind == LyXParagraph::FOOTNOTE)
1479             && layout.labeltype != LABEL_BIBLIO) {
1480                 par->enumdepth++;
1481         }
1482
1483         /* Maybe we have to decrement the enumeration depth, see note above */
1484         if (par->Previous()
1485             && par->Previous()->GetDepth() > par->GetDepth()
1486             && !(par->Previous()->footnoteflag == LyXParagraph::NO_FOOTNOTE
1487                     && par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1488                     && par->footnotekind == LyXParagraph::FOOTNOTE)
1489             && layout.labeltype != LABEL_BIBLIO) {
1490                 par->enumdepth = par->DepthHook(par->GetDepth())->enumdepth;
1491                 par->setCounter(6 + par->enumdepth,
1492                         par->DepthHook(par->GetDepth())->getCounter(6 + par->enumdepth));
1493                 /* reset the counters.
1494                  * A depth change is like a breaking layout
1495                  */
1496                 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1497                         par->setCounter(i, 0);
1498         }
1499    
1500         if (!par->labelstring.empty()) {
1501                 par->labelstring.clear();
1502         }
1503    
1504         if (layout.margintype == MARGIN_MANUAL) {
1505                 if (par->labelwidthstring.empty()) {
1506                         par->SetLabelWidthString(layout.labelstring());
1507                 }
1508         } else {
1509                 par->SetLabelWidthString(string());
1510         }
1511    
1512         /* is it a layout that has an automatic label ? */ 
1513         if (layout.labeltype >=  LABEL_FIRST_COUNTER) {
1514       
1515                 int i = layout.labeltype - LABEL_FIRST_COUNTER;
1516                 if (i >= 0 && i<= parameters->secnumdepth) {
1517                         par->incCounter(i);     // increment the counter  
1518          
1519                         // Is there a label? Useful for Chapter layout
1520                         if (!par->appendix){
1521                                 if (!layout.labelstring().empty())
1522                                         par->labelstring = layout.labelstring();
1523                                 else
1524                                         par->labelstring.clear();
1525                         } else {
1526                                 if (!layout.labelstring_appendix().empty())
1527                                         par->labelstring = layout.labelstring_appendix();
1528                                 else
1529                                         par->labelstring.clear();
1530                         }
1531
1532 #ifdef HAVE_SSTREAM
1533                         std::ostringstream s;
1534 #else
1535                         ostrstream s;
1536 #endif
1537                         if (!par->appendix) {
1538                                 switch (2 * LABEL_FIRST_COUNTER -
1539                                         textclass.maxcounter() + i) {
1540                                 case LABEL_COUNTER_CHAPTER:
1541                                         s << par->getCounter(i);
1542                                         break;
1543                                 case LABEL_COUNTER_SECTION:
1544                                         s << par->getCounter(i - 1) << '.'
1545                                            << par->getCounter(i);
1546                                         break;
1547                                 case LABEL_COUNTER_SUBSECTION:
1548                                         s << par->getCounter(i - 2) << '.'
1549                                           << par->getCounter(i - 1) << '.'
1550                                           << par->getCounter(i);
1551                                         break;
1552                                 case LABEL_COUNTER_SUBSUBSECTION:
1553                                         s << par->getCounter(i - 3) << '.'
1554                                           << par->getCounter(i - 2) << '.'
1555                                           << par->getCounter(i - 1) << '.'
1556                                           << par->getCounter(i);
1557                                         
1558                                         break;
1559                                 case LABEL_COUNTER_PARAGRAPH:
1560                                         s << par->getCounter(i - 4) << '.'
1561                                           << par->getCounter(i - 3) << '.'
1562                                           << par->getCounter(i - 2) << '.'
1563                                           << par->getCounter(i - 1) << '.'
1564                                           << par->getCounter(i);
1565                                         break;
1566                                 case LABEL_COUNTER_SUBPARAGRAPH:
1567                                         s << par->getCounter(i - 5) << '.'
1568                                           << par->getCounter(i - 4) << '.'
1569                                           << par->getCounter(i - 3) << '.'
1570                                           << par->getCounter(i - 2) << '.'
1571                                           << par->getCounter(i - 1) << '.'
1572                                           << par->getCounter(i);
1573
1574                                         break;
1575                                 default:
1576                                         s << par->getCounter(i) << '.';
1577                                         break;
1578                                 }
1579                         } else { // appendix
1580                                 switch (2 * LABEL_FIRST_COUNTER - textclass.maxcounter() + i) {
1581                                 case LABEL_COUNTER_CHAPTER:
1582                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1583                                                 s << alphaCounter(par->getCounter(i));
1584                                         else
1585                                                 s << hebrewCounter(par->getCounter(i));
1586                                         break;
1587                                 case LABEL_COUNTER_SECTION:
1588                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1589                                                 s << alphaCounter(par->getCounter(i - 1));
1590                                         else
1591                                                 s << hebrewCounter(par->getCounter(i - 1));
1592
1593                                         s << '.'
1594                                           << par->getCounter(i);
1595
1596                                         break;
1597                                 case LABEL_COUNTER_SUBSECTION:
1598                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1599                                                 s << alphaCounter(par->getCounter(i - 2));
1600                                         else
1601                                                 s << hebrewCounter(par->getCounter(i - 2));
1602
1603                                         s << '.'
1604                                           << par->getCounter(i-1) << '.'
1605                                           << par->getCounter(i);
1606
1607                                         break;
1608                                 case LABEL_COUNTER_SUBSUBSECTION:
1609                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1610                                                 s << alphaCounter(par->getCounter(i-3));
1611                                         else
1612                                                 s << hebrewCounter(par->getCounter(i-3));
1613
1614                                         s << '.'
1615                                           << par->getCounter(i-2) << '.'
1616                                           << par->getCounter(i-1) << '.'
1617                                           << par->getCounter(i);
1618
1619                                         break;
1620                                 case LABEL_COUNTER_PARAGRAPH:
1621                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1622                                                 s << alphaCounter(par->getCounter(i-4));
1623                                         else
1624                                                 s << hebrewCounter(par->getCounter(i-4));
1625
1626                                         s << '.'
1627                                           << par->getCounter(i-3) << '.'
1628                                           << par->getCounter(i-2) << '.'
1629                                           << par->getCounter(i-1) << '.'
1630                                           << par->getCounter(i);
1631
1632                                         break;
1633                                 case LABEL_COUNTER_SUBPARAGRAPH:
1634                                         if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1635                                                 s << alphaCounter(par->getCounter(i-5));
1636                                         else
1637                                                 s << hebrewCounter(par->getCounter(i-5));
1638
1639                                         s << '.'
1640                                           << par->getCounter(i-4) << '.'
1641                                           << par->getCounter(i-3) << '.'
1642                                           << par->getCounter(i-2) << '.'
1643                                           << par->getCounter(i-1) << '.'
1644                                           << par->getCounter(i);
1645
1646                                         break;
1647                                 default:
1648                                         // Can this ever be reached? And in the
1649                                         // case it is, how can this be correct?
1650                                         // (Lgb)
1651                                         s << static_cast<unsigned char>(par->getCounter(i)) << '.';
1652                                         
1653                                         break;
1654                                 }
1655                         }
1656 #ifdef HAVE_SSTREAM
1657                         par->labelstring += s.str().c_str();
1658                         // We really want to remove the c_str as soon as
1659                         // possible...
1660 #else
1661                         s << '\0';
1662                         char * tmps = s.str();
1663                         par->labelstring += tmps;
1664                         delete [] tmps;
1665 #endif
1666                         
1667                         for (i++; i < 10; ++i) {
1668                                 // reset the following counters
1669                                 par->setCounter(i, 0);
1670                         }
1671                 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1672                         for (i++; i < 10; ++i) {
1673                                 // reset the following counters
1674                                 par->setCounter(i, 0);
1675                         }
1676                 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1677                         par->incCounter(i + par->enumdepth);
1678                         int number = par->getCounter(i + par->enumdepth);
1679
1680 #ifdef HAVE_SSTREAM
1681                         std::ostringstream s;
1682 #else
1683                         ostrstream s;
1684 #endif
1685                         switch (par->enumdepth) {
1686                         case 1:
1687                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1688                                         s << '('
1689                                           << loweralphaCounter(number)
1690                                           << ')';
1691                                 else
1692                                         s << '('
1693                                           << hebrewCounter(number)
1694                                           << ')';
1695                                 break;
1696                         case 2:
1697                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1698                                         s << romanCounter(number) << '.';
1699                                 else
1700                                         s << '.' << romanCounter(number);
1701                                 break;
1702                         case 3:
1703                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1704                                         s << alphaCounter(number)
1705                                           << '.';
1706                                 else
1707                                         s << '.'
1708                                           << alphaCounter(number);
1709                                 break;
1710                         default:
1711                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1712                                         s << number << '.';
1713                                 else
1714                                         s << '.' << number;
1715                                 break;
1716                         }
1717 #ifdef HAVE_SSTREAM
1718                         par->labelstring = s.str().c_str();
1719                         // we really want to get rid of that c_str()
1720 #else
1721                         s << '\0';
1722                         char * tmps = s.str();
1723                         par->labelstring = tmps;
1724                         delete [] tmps;
1725 #endif
1726
1727                         for (i += par->enumdepth + 1; i < 10; ++i)
1728                                 par->setCounter(i, 0);  /* reset the following counters  */
1729          
1730                 } 
1731         } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1732                 int i = LABEL_COUNTER_ENUMI - LABEL_FIRST_COUNTER + par->enumdepth;
1733                 par->incCounter(i);
1734                 int number = par->getCounter(i);
1735                 if (!par->bibkey)
1736                         par->bibkey = new InsetBibKey();
1737                 par->bibkey->setCounter(number);
1738                 par->labelstring = layout.labelstring();
1739                 
1740                 // In biblio should't be following counters but...
1741         } else {
1742                 string s = layout.labelstring();
1743                 
1744                 // the caption hack:
1745       
1746                 if (layout.labeltype == LABEL_SENSITIVE) {
1747                         if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1748                             && (par->footnotekind == LyXParagraph::FIG
1749                                 || par->footnotekind == LyXParagraph::WIDE_FIG))
1750                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1751                                         s = "Figure:";
1752                                 else
1753                                         s = ":øåéà";
1754                         else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1755                                  && (par->footnotekind == LyXParagraph::TAB
1756                                      || par->footnotekind == LyXParagraph::WIDE_TAB))
1757                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1758                                         s = "Table:";
1759                                 else
1760                                         s = ":äìáè";
1761                         else if (par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1762                                  && par->footnotekind == LyXParagraph::ALGORITHM)
1763                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1764                                         s = "Algorithm:";
1765                                 else
1766                                         s = ":íúéøåâìà";
1767                         else {
1768                                 /* par->SetLayout(0); 
1769                                    s = layout->labelstring;  */
1770                                 if (par->getParDirection() == LYX_DIR_LEFT_TO_RIGHT)
1771                                         s = "Senseless: ";
1772                                 else
1773                                         s = " :úåòîùî Ã¸Ã±Ã§";
1774            
1775                         }
1776                 }
1777                 par->labelstring = s;
1778                 
1779                 /* reset the enumeration counter. They are always resetted
1780                  * when there is any other layout between */ 
1781                 for (int i = 6 + par->enumdepth; i < 10; ++i)
1782                         par->setCounter(i, 0);
1783         }
1784 }
1785
1786
1787 /* Updates all counters BEHIND the row. Changed paragraphs
1788 * with a dynamic left margin will be rebroken. */ 
1789 void LyXText::UpdateCounters(Row * row) const
1790 {
1791         LyXParagraph * par;
1792         if (!row) {
1793                 row = firstrow;
1794                 par = row->par;
1795         }
1796         else {
1797                 if (row->par->next
1798                     && row->par->next->footnoteflag != LyXParagraph::OPEN_FOOTNOTE) {
1799                         par = row->par->LastPhysicalPar()->Next();
1800                 } else {
1801                         par = row->par->next;
1802                 }
1803         }
1804
1805         while (par) {
1806                 while (row->par != par)
1807                         row = row->next;
1808                 
1809                 SetCounter(par);
1810                 
1811                 /* now  check for the headline layouts. remember that they
1812                  * have a dynamic left margin */ 
1813                 if (!par->IsDummy()
1814                     && ( textclasslist.Style(parameters->textclass, par->layout).margintype == MARGIN_DYNAMIC
1815                          || textclasslist.Style(parameters->textclass, par->layout).labeltype == LABEL_SENSITIVE)
1816                         ){
1817          
1818                         /* Rebreak the paragraph */ 
1819                         RemoveParagraph(row);
1820                         AppendParagraph(row);
1821        
1822                         /* think about the damned open footnotes! */ 
1823                         while (par->Next() &&
1824                                (par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
1825                                 || par->Next()->IsDummy())){
1826                                 par = par->Next();
1827                                 if (par->IsDummy()) {
1828                                         while (row->par != par)
1829                                                 row = row->next;
1830                                         RemoveParagraph(row);
1831                                         AppendParagraph(row);
1832                                 }
1833                         }
1834                 }
1835      
1836                 par = par->LastPhysicalPar()->Next();
1837      
1838         }
1839 }
1840
1841
1842 /* insets an inset. */ 
1843 void LyXText::InsertInset(Inset *inset)
1844 {
1845         SetUndo(Undo::INSERT, 
1846                 cursor.par->ParFromPos(cursor.pos)->previous, 
1847                 cursor.par->ParFromPos(cursor.pos)->next);
1848         cursor.par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1849         cursor.par->InsertInset(cursor.pos, inset);
1850         InsertChar(LyXParagraph::META_INSET);  /* just to rebreak and refresh correctly.
1851                                       * The character will not be inserted a
1852                                       * second time */
1853 }
1854
1855
1856 // this is for the simple cut and paste mechanism
1857 static LyXParagraph * simple_cut_buffer = 0;
1858 static char simple_cut_buffer_textclass = 0;
1859
1860 void DeleteSimpleCutBuffer()
1861 {
1862         if (!simple_cut_buffer)
1863                 return;
1864         LyXParagraph * tmppar;
1865
1866         while (simple_cut_buffer) {
1867                 tmppar =  simple_cut_buffer;
1868                 simple_cut_buffer = simple_cut_buffer->next;
1869                 delete tmppar;
1870         }
1871         simple_cut_buffer = 0;
1872 }
1873
1874
1875 void LyXText::copyEnvironmentType()
1876 {
1877         copylayouttype = cursor.par->GetLayout();
1878 }
1879
1880
1881 void LyXText::pasteEnvironmentType()
1882 {
1883         SetLayout(copylayouttype);
1884 }
1885
1886
1887 void LyXText::CutSelection(bool doclear)
1888 {
1889         // This doesn't make sense, if there is no selection
1890         if (!selection)
1891                 return;
1892    
1893         // OK, we have a selection. This is always between sel_start_cursor
1894         // and sel_end cursor
1895         LyXParagraph * tmppar;
1896    
1897         // Check whether there are half footnotes in the selection
1898         if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
1899             || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
1900                 tmppar = sel_start_cursor.par;
1901                 while (tmppar != sel_end_cursor.par){
1902                         if (tmppar->footnoteflag != sel_end_cursor.par->footnoteflag) {
1903                                 WriteAlert(_("Impossible operation"),
1904                                            _("Don't know what to do with half floats."),
1905                                            _("sorry."));
1906                                 return;
1907                         }
1908                         tmppar = tmppar->Next();
1909                 }
1910         }
1911
1912         /* table stuff -- begin */
1913         if (sel_start_cursor.par->table || sel_end_cursor.par->table) {
1914                 if ( sel_start_cursor.par != sel_end_cursor.par) {
1915                         WriteAlert(_("Impossible operation"),
1916                                    _("Don't know what to do with half tables."),
1917                                    _("sorry."));
1918                         return;
1919                 }
1920                 sel_start_cursor.par->table->Reinit();
1921         }
1922         /* table stuff -- end */
1923
1924         // make sure that the depth behind the selection are restored, too
1925         LyXParagraph * endpar = sel_end_cursor.par->LastPhysicalPar()->Next();
1926         LyXParagraph * undoendpar = endpar;
1927
1928         if (endpar && endpar->GetDepth()) {
1929                 while (endpar && endpar->GetDepth()) {
1930                         endpar = endpar->LastPhysicalPar()->Next();
1931                         undoendpar = endpar;
1932                 }
1933         } else if (endpar) {
1934                 endpar = endpar->Next(); // because of parindents etc.
1935         }
1936    
1937         SetUndo(Undo::DELETE, 
1938                 sel_start_cursor
1939                 .par->ParFromPos(sel_start_cursor.pos)->previous, 
1940                 undoendpar);
1941    
1942         // clear the simple_cut_buffer
1943         DeleteSimpleCutBuffer();
1944    
1945         // set the textclass
1946         simple_cut_buffer_textclass = parameters->textclass;
1947
1948 #ifdef WITH_WARNINGS
1949 #warning Asger: Make cut more intelligent here.
1950 #endif
1951         /* 
1952            White paper for "intelligent" cutting:
1953            
1954            Example: "This is our text."
1955            Using " our " as selection, cutting will give "This istext.".
1956            Using "our" as selection, cutting will give "This is text.".
1957            Using " our" as selection, cutting will give "This is text.".
1958            Using "our " as selection, cutting will give "This is text.".
1959            
1960            All those four selections will (however) paste identically:
1961            Pasting with the cursor right after the "is" will give the
1962            original text with all four selections.
1963            
1964            The rationale is to be intelligent such that words are copied,
1965            cut and pasted in a functional manner.
1966            
1967            This is not implemented yet. (Asger)
1968
1969            The changes below sees to do a lot of what you want. However
1970            I have not verified that all cases work as they should:
1971                      - cut in single row
1972                      - cut in multiple row
1973                      - cut with insets
1974                      - cut across footnotes and paragraph
1975            My simplistic tests show that the idea are basically sound but
1976            there are some items to fix up...we only need to find them
1977            first.
1978
1979            As do redo Asger's example above (with | beeing the cursor in the
1980            result after cutting.):
1981            
1982            Example: "This is our text."
1983            Using " our " as selection, cutting will give "This is|text.".
1984            Using "our"   as selection, cutting will give "This is | text.".
1985            Using " our"  as selection, cutting will give "This is| text.".
1986            Using "our "  as selection, cutting will give "This is |text.".
1987
1988            (Lgb)
1989         */
1990
1991 #ifndef FIX_DOUBLE_SPACE
1992         bool space_wrapped =
1993                 sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos);
1994         if (sel_end_cursor.pos > 0
1995             && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1)) {
1996                 // please break before a space at the end
1997                 sel_end_cursor.pos--;
1998                 space_wrapped = true;
1999         }
2000         // cut behind a space if there is one
2001         while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2002                && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2003                && (sel_start_cursor.par != sel_end_cursor.par
2004                    || sel_start_cursor.pos < sel_end_cursor.pos))
2005                 sel_start_cursor.pos++; 
2006 #endif
2007         // there are two cases: cut only within one paragraph or
2008         // more than one paragraph
2009    
2010         if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos) 
2011             == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2012                 // only within one paragraph
2013                 simple_cut_buffer = new LyXParagraph;
2014                 LyXParagraph::size_type i =
2015                         sel_start_cursor.pos;
2016                 for (; i < sel_end_cursor.pos; ++i) {
2017                         /* table stuff -- begin */
2018                         if (sel_start_cursor.par->table
2019                             && sel_start_cursor.par->IsNewline(sel_start_cursor.pos)) {
2020                                 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2021                                 sel_start_cursor.pos++;
2022                         } else {
2023                                 /* table stuff -- end */
2024                                 sel_start_cursor.par->CopyIntoMinibuffer(sel_start_cursor.pos);
2025                                 sel_start_cursor.par->Erase(sel_start_cursor.pos);
2026                         }
2027                         simple_cut_buffer->InsertFromMinibuffer(simple_cut_buffer->Last());
2028                 }
2029 #ifndef FIX_DOUBLE_SPACE
2030                 // check for double spaces
2031                 if (sel_start_cursor.pos &&
2032                     sel_start_cursor.par->Last() > sel_start_cursor.pos
2033                     && sel_start_cursor.par
2034                     ->IsLineSeparator(sel_start_cursor.pos - 1)
2035                     && sel_start_cursor.par
2036                     ->IsLineSeparator(sel_start_cursor.pos)) {
2037                         sel_start_cursor.par->Erase(sel_start_cursor.pos);
2038                 }
2039                 if (space_wrapped)
2040                         simple_cut_buffer->InsertChar(i - sel_start_cursor.pos,
2041                                                       ' ');
2042 #endif
2043                 endpar = sel_end_cursor.par->Next();
2044         } else {
2045                 // cut more than one paragraph
2046    
2047                 sel_end_cursor.par
2048                         ->BreakParagraphConservative(sel_end_cursor.pos);
2049 #ifndef FIX_DOUBLE_SPACE
2050                 // insert a space at the end if there was one
2051                 if (space_wrapped)
2052                         sel_end_cursor.par
2053                                 ->InsertChar(sel_end_cursor.par->Last(), ' ');
2054 #endif
2055                 sel_end_cursor.par = sel_end_cursor.par->Next();
2056                 sel_end_cursor.pos = 0;
2057    
2058                 cursor = sel_end_cursor;
2059
2060 #ifndef FIX_DOUBLE_SPACE
2061                 // please break behind a space, if there is one.
2062                 // The space should be copied too
2063                 if (sel_start_cursor.par
2064                     ->IsLineSeparator(sel_start_cursor.pos))
2065                         sel_start_cursor.pos++;
2066 #endif   
2067                 sel_start_cursor.par
2068                         ->BreakParagraphConservative(sel_start_cursor.pos);
2069 #ifndef FIX_DOUBLE_SPACE
2070                 if (!sel_start_cursor.pos
2071                     || sel_start_cursor.par
2072                     ->IsLineSeparator(sel_start_cursor.pos - 1)
2073                     || sel_start_cursor.par
2074                     ->IsNewline(sel_start_cursor.pos - 1)) {
2075                         sel_start_cursor.par->Next()->InsertChar(0, ' ');
2076                 }
2077 #endif
2078                 // store the endparagraph for redoing later
2079                 endpar = sel_end_cursor.par->Next(); /* needed because
2080                                                         the sel_end_
2081                                                         cursor.par
2082                                                         will be pasted! */
2083    
2084                 // store the selection
2085                 simple_cut_buffer = sel_start_cursor.par
2086                         ->ParFromPos(sel_start_cursor.pos)->next;
2087                 simple_cut_buffer->previous = 0;
2088                 sel_end_cursor.par->previous->next = 0;
2089
2090                 // cut the selection
2091                 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->next 
2092                         = sel_end_cursor.par;
2093    
2094                 sel_end_cursor.par->previous 
2095                         = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2096
2097                 // care about footnotes
2098                 if (simple_cut_buffer->footnoteflag) {
2099                         LyXParagraph * tmppar = simple_cut_buffer;
2100                         while (tmppar){
2101                                 tmppar->footnoteflag = LyXParagraph::NO_FOOTNOTE;
2102                                 tmppar = tmppar->next;
2103                         }
2104                 }
2105
2106                 // the cut selection should begin with standard layout
2107                 simple_cut_buffer->Clear(); 
2108    
2109                 // paste the paragraphs again, if possible
2110                 if (doclear)
2111                         sel_start_cursor.par->Next()->ClearParagraph();
2112                 if (sel_start_cursor.par->FirstPhysicalPar()->HasSameLayout(sel_start_cursor.par->Next())
2113                     || 
2114                     !sel_start_cursor.par->Next()->Last())
2115                         sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->PasteParagraph();
2116
2117 #ifndef FIX_DOUBLE_SPACE
2118                 // maybe a forgotten blank
2119                 if (sel_start_cursor.pos 
2120                     && sel_start_cursor.par
2121                     ->IsLineSeparator(sel_start_cursor.pos)
2122                     && sel_start_cursor.par
2123                     ->IsLineSeparator(sel_start_cursor.pos - 1)) {
2124                         sel_start_cursor.par->Erase(sel_start_cursor.pos);
2125                 }
2126 #endif
2127         }
2128
2129         // sometimes necessary
2130         if (doclear)
2131                 sel_start_cursor.par->ClearParagraph();
2132
2133         RedoParagraphs(sel_start_cursor, endpar);
2134    
2135         ClearSelection();
2136         cursor = sel_start_cursor;
2137         SetCursor(cursor.par, cursor.pos);
2138         sel_cursor = cursor;
2139         UpdateCounters(cursor.row);
2140 }
2141
2142     
2143 void LyXText::CopySelection()
2144 {
2145         // this doesnt make sense, if there is no selection
2146         if (!selection)
2147                 return;
2148
2149         // ok we have a selection. This is always between sel_start_cursor
2150         // and sel_end cursor
2151         LyXParagraph * tmppar;
2152    
2153         /* check wether there are half footnotes in the selection */
2154         if (sel_start_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
2155             || sel_end_cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2156                 tmppar = sel_start_cursor.par;
2157                 while (tmppar != sel_end_cursor.par) {
2158                         if (tmppar->footnoteflag !=
2159                             sel_end_cursor.par->footnoteflag) {
2160                                 WriteAlert(_("Impossible operation"),
2161                                            _("Don't know what to do"
2162                                              " with half floats."),
2163                                            _("sorry."));
2164                                 return;
2165                         }
2166                         tmppar = tmppar->Next();
2167                 }
2168         }
2169
2170         /* table stuff -- begin */
2171         if (sel_start_cursor.par->table || sel_end_cursor.par->table){
2172                 if ( sel_start_cursor.par != sel_end_cursor.par){
2173                         WriteAlert(_("Impossible operation"),
2174                                    _("Don't know what to do with half tables."),
2175                                    _("sorry."));
2176                         return;
2177                 }
2178         }
2179         /* table stuff -- end */
2180    
2181         // delete the simple_cut_buffer
2182         DeleteSimpleCutBuffer();
2183
2184         // set the textclass
2185         simple_cut_buffer_textclass = parameters->textclass;
2186
2187 #ifdef FIX_DOUBLE_SPACE
2188         // copy behind a space if there is one
2189         while (sel_start_cursor.par->Last() > sel_start_cursor.pos
2190                && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)
2191                && (sel_start_cursor.par != sel_end_cursor.par
2192                    || sel_start_cursor.pos < sel_end_cursor.pos))
2193                 sel_start_cursor.pos++; 
2194 #endif
2195         // there are two cases: copy only within one paragraph
2196         // or more than one paragraph
2197         if (sel_start_cursor.par->ParFromPos(sel_start_cursor.pos) 
2198             == sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)) {
2199                 // only within one paragraph
2200                 simple_cut_buffer = new LyXParagraph;
2201                 LyXParagraph::size_type i = 0;
2202                 for (i = sel_start_cursor.pos; i < sel_end_cursor.pos; ++i){
2203                         sel_start_cursor.par->CopyIntoMinibuffer(i);
2204                         simple_cut_buffer->InsertFromMinibuffer(i - sel_start_cursor.pos);
2205                 }
2206         } else {
2207                 // copy more than one paragraph
2208                 // clone the paragraphs within the selection
2209                 tmppar =
2210                         sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
2211                 simple_cut_buffer = tmppar->Clone();
2212                 LyXParagraph *tmppar2 = simple_cut_buffer;
2213      
2214                 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
2215                        && tmppar->next) {
2216                         tmppar = tmppar->next;
2217                         tmppar2->next = tmppar->Clone();
2218                         tmppar2->next->previous = tmppar2;
2219                         tmppar2 = tmppar2->next;
2220                 }
2221                 tmppar2->next = 0;
2222
2223                 // care about footnotes
2224                 if (simple_cut_buffer->footnoteflag) {
2225                         tmppar = simple_cut_buffer;
2226                         while (tmppar){
2227                                 tmppar->footnoteflag =
2228                                         LyXParagraph::NO_FOOTNOTE;
2229                                 tmppar = tmppar->next;
2230                         }
2231                 }
2232                 
2233                 // the simple_cut_buffer paragraph is too big
2234                 LyXParagraph::size_type tmpi2 =
2235                         sel_start_cursor.par->PositionInParFromPos(sel_start_cursor.pos);
2236                 for (; tmpi2; --tmpi2)
2237                         simple_cut_buffer->Erase(0);
2238                 
2239                 // now tmppar 2 is too big, delete all after sel_end_cursor.pos
2240      
2241                 tmpi2 = sel_end_cursor.par->PositionInParFromPos(sel_end_cursor.pos);
2242                 while (tmppar2->size() > tmpi2) {
2243                         tmppar2->Erase(tmppar2->size() - 1);
2244                 }
2245         }
2246 }
2247           
2248
2249 void LyXText::PasteSelection()
2250 {
2251         // this does not make sense, if there is nothing to paste
2252         if (!simple_cut_buffer)
2253                 return;
2254
2255         LyXParagraph * tmppar;
2256         LyXParagraph * endpar;
2257
2258         LyXCursor tmpcursor;
2259
2260         // be carefull with footnotes in footnotes
2261         if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
2262       
2263                 // check whether the cut_buffer includes a footnote
2264                 tmppar = simple_cut_buffer;
2265                 while (tmppar
2266                        && tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
2267                         tmppar = tmppar->next;
2268       
2269                 if (tmppar) {
2270                         WriteAlert(_("Impossible operation"),
2271                                    _("Can't paste float into float!"),
2272                                    _("Sorry."));
2273                         return;
2274                 }
2275         }
2276
2277         /* table stuff -- begin */
2278         if (cursor.par->table) {
2279                 if (simple_cut_buffer->next) {
2280                         WriteAlert(_("Impossible operation"),
2281                                    _("Table cell cannot include more than one paragraph!"),
2282                                    _("Sorry."));
2283                         return;
2284                 }
2285         }
2286         /* table stuff -- end */
2287    
2288         SetUndo(Undo::INSERT, 
2289                 cursor.par->ParFromPos(cursor.pos)->previous, 
2290                 cursor.par->ParFromPos(cursor.pos)->next); 
2291
2292         tmpcursor = cursor;
2293
2294         // There are two cases: cutbuffer only one paragraph or many
2295         if (!simple_cut_buffer->next) {
2296                 // only within a paragraph
2297
2298 #ifndef FIX_DOUBLE_SPACE
2299                 // please break behind a space, if there is one
2300                 while (tmpcursor.par->Last() > tmpcursor.pos
2301                        && tmpcursor.par->IsLineSeparator(tmpcursor.pos))
2302                         tmpcursor.pos++; 
2303 #endif
2304                 tmppar = simple_cut_buffer->Clone();
2305                 /* table stuff -- begin */
2306                 bool table_too_small = false;
2307                 if (tmpcursor.par->table) {
2308                         while (simple_cut_buffer->size()
2309                                && !table_too_small) {
2310                                 if (simple_cut_buffer->IsNewline(0)){
2311                                         while(tmpcursor.pos < tmpcursor.par->Last() && !tmpcursor.par->IsNewline(tmpcursor.pos))
2312                                                 tmpcursor.pos++;
2313                                         simple_cut_buffer->Erase(0);
2314                                         if (tmpcursor.pos < tmpcursor.par->Last())
2315                                                 tmpcursor.pos++;
2316                                         else
2317                                                 table_too_small = true;
2318                                 } else {
2319 #ifdef FIX_DOUBLE_SPACE
2320                                         // This is an attempt to fix the
2321                                         // "never insert a space at the
2322                                         // beginning of a paragraph" problem.
2323                                         if (tmpcursor.pos == 0
2324                                             && simple_cut_buffer->IsLineSeparator(0)) {
2325                                                 simple_cut_buffer->Erase(0);
2326                                         } else {
2327                                                 simple_cut_buffer->CutIntoMinibuffer(0);
2328                                                 simple_cut_buffer->Erase(0);
2329                                                 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2330                                                 tmpcursor.pos++;
2331                                         }
2332 #else
2333                                         simple_cut_buffer->CutIntoMinibuffer(0);
2334                                         simple_cut_buffer->Erase(0);
2335                                         tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2336                                         tmpcursor.pos++;
2337 #endif
2338                                 }
2339                         }
2340                 } else {
2341                         /* table stuff -- end */
2342                         // Some provisions should be done here for checking
2343                         // if we are inserting at the beginning of a
2344                         // paragraph. If there are a space at the beginning
2345                         // of the text to insert and we are inserting at
2346                         // the beginning of the paragraph the space should
2347                         // be removed.
2348                         while (simple_cut_buffer->size()) {
2349 #ifdef FIX_DOUBLE_SPACE
2350                                 // This is an attempt to fix the
2351                                 // "never insert a space at the
2352                                 // beginning of a paragraph" problem.
2353                                 if (tmpcursor.pos == 0
2354                                     && simple_cut_buffer->IsLineSeparator(0)) {
2355                                         simple_cut_buffer->Erase(0);
2356                                 } else {
2357                                         simple_cut_buffer->CutIntoMinibuffer(0);
2358                                         simple_cut_buffer->Erase(0);
2359                                         tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2360                                         tmpcursor.pos++;
2361                                 }
2362 #else
2363                                 simple_cut_buffer->CutIntoMinibuffer(0);
2364                                 simple_cut_buffer->Erase(0);
2365                                 tmpcursor.par->InsertFromMinibuffer(tmpcursor.pos);
2366                                 tmpcursor.pos++;
2367 #endif
2368                         }
2369                 }
2370                 delete simple_cut_buffer;
2371                 simple_cut_buffer = tmppar;
2372                 endpar = tmpcursor.par->Next();
2373         } else {
2374                 // many paragraphs
2375
2376                 // make a copy of the simple cut_buffer
2377                 tmppar = simple_cut_buffer;
2378                 LyXParagraph * simple_cut_clone = tmppar->Clone();
2379                 LyXParagraph * tmppar2 = simple_cut_clone;
2380                 if (cursor.par->footnoteflag){
2381                         tmppar->footnoteflag = cursor.par->footnoteflag;
2382                         tmppar->footnotekind = cursor.par->footnotekind;
2383                 }
2384                 while (tmppar->next) {
2385                         tmppar = tmppar->next;
2386                         tmppar2->next = tmppar->Clone();
2387                         tmppar2->next->previous = tmppar2;
2388                         tmppar2 = tmppar2->next;
2389                         if (cursor.par->footnoteflag){
2390                                 tmppar->footnoteflag = cursor.par->footnoteflag;
2391                                 tmppar->footnotekind = cursor.par->footnotekind;
2392                         }
2393                 }
2394      
2395                 // make sure there is no class difference
2396                 SwitchLayoutsBetweenClasses(simple_cut_buffer_textclass,
2397                                             parameters->textclass,
2398                                             simple_cut_buffer);
2399      
2400                 // make the simple_cut_buffer exactly the same layout than
2401                 // the cursor paragraph
2402                 simple_cut_buffer->MakeSameLayout(cursor.par);
2403      
2404                 // find the end of the buffer
2405                 LyXParagraph * lastbuffer = simple_cut_buffer;
2406                 while (lastbuffer->Next())
2407                         lastbuffer = lastbuffer->Next();
2408      
2409 #ifndef FIX_DOUBLE_SPACE
2410                 // Please break behind a space, if there is one. The space 
2411                 // should be copied too.
2412                 if (cursor.par->Last() > cursor.pos
2413                     && cursor.par->IsLineSeparator(cursor.pos))
2414                         cursor.pos++; 
2415 #endif
2416                 bool paste_the_end = false;
2417
2418                 // open the paragraph for inserting the simple_cut_buffer
2419                 // if necessary
2420                 if (cursor.par->Last() > cursor.pos || !cursor.par->Next()){
2421                         cursor.par->BreakParagraphConservative(cursor.pos);
2422                         paste_the_end = true;
2423                 }
2424
2425 #ifndef FIX_DOUBLE_SPACE
2426                 // be careful with double spaces
2427                 if ((!cursor.par->Last()
2428                      || cursor.par->IsLineSeparator(cursor.pos - 1)
2429                      || cursor.par->IsNewline(cursor.pos - 1))
2430                     && simple_cut_buffer->text.size()
2431                     && simple_cut_buffer->IsLineSeparator(0))
2432                         simple_cut_buffer->Erase(0);
2433 #endif
2434                 // set the end for redoing later
2435                 endpar = cursor.par->ParFromPos(cursor.pos)->next->Next();
2436      
2437                 // paste it!
2438                 lastbuffer->ParFromPos(lastbuffer->Last())->next =
2439                         cursor.par->ParFromPos(cursor.pos)->next;
2440                 cursor.par->ParFromPos(cursor.pos)->next->previous =
2441                         lastbuffer->ParFromPos(lastbuffer->Last());
2442      
2443                 cursor.par->ParFromPos(cursor.pos)->next = simple_cut_buffer;
2444                 simple_cut_buffer->previous =
2445                         cursor.par->ParFromPos(cursor.pos);
2446    
2447                 if (cursor.par->ParFromPos(cursor.pos)->Next() == lastbuffer)
2448                         lastbuffer = cursor.par;
2449      
2450                 cursor.par->ParFromPos(cursor.pos)->PasteParagraph();
2451      
2452                 // store the new cursor position
2453                 tmpcursor.par = lastbuffer;
2454                 tmpcursor.pos = lastbuffer->Last();
2455      
2456                 // maybe some pasting
2457                 if (lastbuffer->Next() && paste_the_end) {
2458                         if (lastbuffer->Next()->HasSameLayout(lastbuffer)) {
2459 #ifndef FIX_DOUBLE_SPACE
2460                                 // be careful with double spaces
2461                                 if ((!lastbuffer->Last()
2462                                      || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2463                                      || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2464                                     && lastbuffer->Next()->Last()
2465                                     && lastbuffer->Next()->IsLineSeparator(0))
2466                                         lastbuffer->Next()->Erase(0);
2467 #endif
2468                                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2469          
2470                         } else if (!lastbuffer->Next()->Last()) {
2471                                 lastbuffer->Next()->MakeSameLayout(lastbuffer);
2472 #ifndef FIX_DOUBLE_SPACE
2473                                 // be careful witth double spaces
2474                                 if ((!lastbuffer->Last()
2475                                      || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2476                                      || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2477                                     && lastbuffer->Next()->Last()
2478                                     && lastbuffer->Next()->IsLineSeparator(0))
2479                                         lastbuffer->Next()->Erase(0);
2480 #endif
2481                                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2482          
2483                         } else if (!lastbuffer->Last()) {
2484                                 lastbuffer->MakeSameLayout(lastbuffer->next);
2485 #ifndef FIX_DOUBLE_SPACE
2486                                 // be careful witth double spaces
2487                                 if ((!lastbuffer->Last()
2488                                      || lastbuffer->IsLineSeparator(lastbuffer->Last() - 1)
2489                                      || lastbuffer->IsNewline(lastbuffer->Last() - 1))
2490                                     && lastbuffer->Next()->Last()
2491                                     && lastbuffer->Next()->IsLineSeparator(0))
2492                                         lastbuffer->Next()->Erase(0);
2493 #endif
2494                                 lastbuffer->ParFromPos(lastbuffer->Last())->PasteParagraph();
2495          
2496                         } else
2497                                 lastbuffer->Next()->ClearParagraph();
2498                 }
2499
2500                 // restore the simple cut buffer
2501                 simple_cut_buffer = simple_cut_clone;
2502         }
2503
2504         RedoParagraphs(cursor, endpar);
2505     
2506         SetCursor(cursor.par, cursor.pos);
2507         ClearSelection();
2508    
2509         sel_cursor = cursor;
2510         SetCursor(tmpcursor.par, tmpcursor.pos);
2511         SetSelection();
2512         UpdateCounters(cursor.row);
2513 }
2514    
2515
2516 // returns a pointer to the very first LyXParagraph
2517 LyXParagraph * LyXText::FirstParagraph() const
2518 {
2519         return params->paragraph;
2520 }
2521
2522
2523 // returns true if the specified string is at the specified position
2524 bool LyXText::IsStringInText(LyXParagraph * par,
2525                              LyXParagraph::size_type pos,
2526                              char const * str) const
2527 {
2528         if (par) {
2529                 int i = 0;
2530                 while (pos + i < par->Last() && str[i] && 
2531                        str[i] == par->GetChar(pos + i)) {
2532                         ++i;
2533                 }
2534                 if (!str[i])
2535                         return true;
2536         }
2537         return false;
2538 }
2539
2540
2541 // sets the selection over the number of characters of string, no check!!
2542 void LyXText::SetSelectionOverString(char const * string)
2543 {
2544         sel_cursor = cursor;
2545         for (int i = 0; string[i]; ++i)
2546                 CursorRight();
2547         SetSelection();
2548 }
2549
2550
2551 // simple replacing. The font of the first selected character is used
2552 void LyXText::ReplaceSelectionWithString(char const * str)
2553 {
2554         SetCursorParUndo();
2555         FreezeUndo();
2556
2557         if (!selection) { // create a dummy selection
2558                 sel_end_cursor = cursor;
2559                 sel_start_cursor = cursor;
2560         }
2561
2562         // Get font setting before we cut
2563         LyXParagraph::size_type pos = sel_end_cursor.pos;
2564         LyXFont font = sel_start_cursor.par->GetFontSettings(sel_start_cursor.pos);
2565
2566         // Insert the new string
2567         for (int i = 0; str[i]; ++i) {
2568                 sel_end_cursor.par->InsertChar(pos, str[i]);
2569                 sel_end_cursor.par->SetFont(pos, font);
2570                 ++pos;
2571         }
2572
2573         // Cut the selection
2574         CutSelection();
2575
2576         UnFreezeUndo();
2577 }
2578
2579
2580 // if the string can be found: return true and set the cursor to
2581 // the new position
2582 bool LyXText::SearchForward(char const * str) const
2583 {
2584         LyXParagraph * par = cursor.par;
2585         LyXParagraph::size_type pos = cursor.pos;
2586         while (par && !IsStringInText(par, pos, str)) {
2587                 if (pos < par->Last() - 1)
2588                         ++pos;
2589                 else {
2590                         pos = 0;
2591                         par = par->Next();
2592                 }
2593         }
2594         if (par) {
2595                 SetCursor(par, pos);
2596                 return true;
2597         }
2598         else
2599                 return false;
2600 }
2601
2602
2603 bool LyXText::SearchBackward(char const * string) const
2604 {
2605         LyXParagraph * par = cursor.par;
2606         int pos = cursor.pos;
2607
2608         do {
2609                 if (pos > 0)
2610                         --pos;
2611                 else {
2612                         // We skip empty paragraphs (Asger)
2613                         do {
2614                                 par = par->Previous();
2615                                 if (par)
2616                                         pos = par->Last() - 1;
2617                         } while (par && pos < 0);
2618                 }
2619         } while (par && !IsStringInText(par, pos, string));
2620   
2621         if (par) {
2622                 SetCursor(par, pos);
2623                 return true;
2624         } else
2625                 return false;
2626 }
2627
2628
2629 // needed to insert the selection
2630 void LyXText::InsertStringA(string const & str)
2631 {
2632         LyXParagraph * par = cursor.par;
2633         LyXParagraph::size_type pos = cursor.pos;
2634         LyXParagraph::size_type a = 0;
2635         int cell = 0;
2636         LyXParagraph * endpar = cursor.par->Next();
2637         
2638         SetCursorParUndo();
2639         
2640         bool flag =
2641                 textclasslist.Style(parameters->textclass, 
2642                                     cursor.par->GetLayout()).isEnvironment();
2643         // only to be sure, should not be neccessary
2644         ClearSelection();
2645         
2646         // insert the string, don't insert doublespace
2647         string::size_type i = 0;
2648         while (i < str.length()) {
2649                 if (str[i] != '\n') {
2650                         if (str[i] == ' ' 
2651                             && i + 1 < str.length() && str[i + 1] != ' '
2652                             && pos && par->GetChar(pos - 1)!= ' ') {
2653                                 par->InsertChar(pos,' ');
2654                                 par->SetFont(pos, current_font);
2655                                 ++pos;
2656                         } else if (par->table) {
2657                                 if (str[i] == '\t') {
2658                                         while((pos < par->size()) &&
2659                                               (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2660                                                 ++pos;
2661                                         if (pos < par->size())
2662                                                 ++pos;
2663                                         else // no more fields to fill skip the rest
2664                                                 break;
2665                                 } else if ((str[i] != 13) &&
2666                                            ((str[i] & 127) >= ' ')) {
2667                                         par->InsertChar(pos, str[i]);
2668                                         par->SetFont(pos, current_font);
2669                                         ++pos;
2670                                 }
2671                         } else if (str[i] == ' ') {
2672 #if 1
2673                                 InsetSpecialChar * new_inset =
2674                                         new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2675                                 par->InsertChar(pos, LyXParagraph::META_INSET);
2676                                 par->SetFont(pos, current_font);
2677                                 par->InsertInset(pos, new_inset);
2678 #else
2679                                 par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2680                                 par->SetFont(pos, current_font);
2681 #endif
2682                                 ++pos;
2683                         } else if (str[i] == '\t') {
2684                                 for (a = pos; a < (pos / 8 + 1) * 8 ; ++a) {
2685 #if 1
2686                                 InsetSpecialChar * new_inset =
2687                                         new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2688                                 par->InsertChar(pos, LyXParagraph::META_INSET);
2689                                 par->SetFont(pos, current_font);
2690                                 par->InsertInset(pos, new_inset);
2691 #else
2692                                         par->InsertChar(a, LyXParagraph::META_PROTECTED_SEPARATOR);
2693                                         par->SetFont(a, current_font);
2694 #endif
2695                                 }
2696                                 pos = a;
2697                         } else if (str[i] != 13 && 
2698                                    // Ignore unprintables
2699                                    (str[i] & 127) >= ' ') {
2700                                 par->InsertChar(pos, str[i]);
2701                                 par->SetFont(pos, current_font);
2702                                 ++pos;
2703                         }
2704                 } else {
2705                         if (par->table) {
2706                                 if (i + 1 >= str.length()) {
2707                                         ++pos;
2708                                         break;
2709                                 }
2710                                 while((pos < par->size()) &&
2711                                       (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2712                                         ++pos;
2713                                 ++pos;
2714                                 cell = NumberOfCell(par, pos);
2715                                 while((pos < par->size()) &&
2716                                       !(par->table->IsFirstCell(cell))) {
2717
2718                                         while((pos < par->size()) &&
2719                                               (par->GetChar(pos) != LyXParagraph::META_NEWLINE))
2720                                                 ++pos;
2721                                         ++pos;
2722                                         cell = NumberOfCell(par, pos);
2723                                 }
2724                                 if (pos >= par->size())
2725                                         // no more fields to fill skip the rest
2726                                         break;
2727                         } else {
2728                                 if (!par->size()) { // par is empty
2729 #if 1
2730                                         InsetSpecialChar * new_inset =
2731                                                 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
2732                                         par->InsertChar(pos, LyXParagraph::META_INSET);
2733                                         par->SetFont(pos, current_font);
2734                                         par->InsertInset(pos, new_inset);
2735 #else
2736                                         par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
2737                                         par->SetFont(pos, current_font);
2738 #endif
2739                                         ++pos;
2740                                 }
2741                                 par->BreakParagraph(pos, flag);
2742                                 par = par->Next();
2743                                 pos = 0;
2744                         }
2745                 }
2746                 ++i;
2747         }
2748         
2749         RedoParagraphs(cursor, endpar);
2750         SetCursor(cursor.par, cursor.pos);
2751         sel_cursor = cursor;
2752         SetCursor(par, pos);
2753         SetSelection();
2754 }
2755
2756
2757 /* turns double-CR to single CR, others where converted into one blank and 13s 
2758  * that are ignored .Double spaces are also converted into one. Spaces at
2759  * the beginning of a paragraph are forbidden. tabs are converted into one
2760  * space. then InsertStringA is called */ 
2761 void LyXText::InsertStringB(string const & s)
2762 {
2763         string str(s);
2764         LyXParagraph * par = cursor.par;
2765         string::size_type i = 1;
2766         while (i < str.length()) {
2767                 if (str[i] == '\t' && !par->table)
2768                         str[i] = ' ';
2769                 if (str[i] == ' ' && i + 1 < str.length() && str[i + 1] == ' ')
2770                         str[i] = 13;
2771                 if (str[i] == '\n' && i + 1 < str.length() && !par->table){
2772                         if (str[i + 1] != '\n') {
2773                                 if (str[i - 1] != ' ')
2774                                         str[i] = ' ';
2775                                 else
2776                                         str[i] = 13;
2777                         }
2778                         while (i + 1 < str.length() 
2779                                && (str[i + 1] == ' ' 
2780                                    || str[i + 1] == '\t'
2781                                    || str[i + 1] == '\n' 
2782                                    || str[i + 1] == 13)) {
2783                                 str[i + 1] = 13;
2784                                 ++i;
2785                         }
2786                 }
2787                 ++i;
2788         }
2789         InsertStringA(str);
2790 }
2791
2792
2793 bool LyXText::GotoNextError() const
2794 {
2795         LyXCursor res = cursor;
2796         do {
2797                 if (res.pos < res.par->Last() - 1) {
2798                         res.pos++;
2799                 }
2800                 else  {
2801                         res.par = res.par->Next();
2802                         res.pos = 0;
2803                 }
2804       
2805         } while (res.par && 
2806                  !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2807                    && res.par->GetInset(res.pos)->AutoDelete()));
2808    
2809         if (res.par) {
2810                 SetCursor(res.par, res.pos);
2811                 return true;
2812         }
2813         return false;
2814 }
2815
2816
2817 bool LyXText::GotoNextNote() const
2818 {
2819         LyXCursor res = cursor;
2820         do {
2821                 if (res.pos < res.par->Last() - 1) {
2822                         res.pos++;
2823                 } else  {
2824                         res.par = res.par->Next();
2825                         res.pos = 0;
2826                 }
2827       
2828         } while (res.par && 
2829                  !(res.par->GetChar(res.pos) == LyXParagraph::META_INSET
2830                    && res.par->GetInset(res.pos)->LyxCode() == Inset::IGNORE_CODE));
2831    
2832         if (res.par) {
2833                 SetCursor(res.par, res.pos);
2834                 return true;
2835         }
2836         return false;
2837 }
2838
2839
2840 int LyXText::SwitchLayoutsBetweenClasses(LyXTextClassList::size_type class1,
2841                                          LyXTextClassList::size_type class2,
2842                                          LyXParagraph * par)
2843 {
2844         int ret = 0;
2845         if (!par || class1 == class2)
2846                 return ret;
2847         par = par->FirstPhysicalPar();
2848         while (par) {
2849                 string name = textclasslist.NameOfLayout(class1, par->layout);
2850                 int lay = 0;
2851                 pair<bool, LyXTextClass::LayoutList::size_type> pp =
2852                         textclasslist.NumberOfLayout(class2, name);
2853                 if (pp.first) {
2854                         lay = pp.second;
2855                 } else { // layout not found
2856                         // use default layout "Standard" (0)
2857                         lay = 0;
2858                 }
2859                 par->layout = lay;
2860       
2861                 if (name != textclasslist.NameOfLayout(class2, par->layout)) {
2862                         ++ret;
2863                         string s = "Layout had to be changed from\n"
2864                                 + name + " to " + textclasslist.NameOfLayout(class2, par->layout)
2865                                 + "\nbecause of class conversion from\n"
2866                                 + textclasslist.NameOfClass(class1) + " to "
2867                                 + textclasslist.NameOfClass(class2);
2868                         InsetError * new_inset = new InsetError(s);
2869                         par->InsertChar(0, LyXParagraph::META_INSET);
2870                         par->InsertInset(0, new_inset);
2871                 }
2872       
2873                 par = par->next;
2874         }
2875         return ret;
2876 }
2877
2878
2879 void LyXText::CheckParagraph(LyXParagraph * par,
2880                              LyXParagraph::size_type pos)
2881 {
2882   
2883         LyXCursor tmpcursor;
2884
2885         /* table stuff -- begin*/
2886    
2887         if (par->table) {
2888                 CheckParagraphInTable(par, pos);
2889         }
2890         else {
2891                 /* table stuff -- end*/
2892      
2893                 long y = 0;
2894                 LyXParagraph::size_type z;
2895                 Row * row = GetRow(par, pos, y);
2896      
2897                 // is there a break one row above
2898                 if (row->previous && row->previous->par == row->par) {
2899                         z = NextBreakPoint(row->previous, paperwidth);
2900                         if ( z >= row->pos) {
2901                                 // set the dimensions of the row above
2902                                 y -= row->previous->height;
2903                                 refresh_y = y;
2904                                 refresh_row = row->previous;
2905                                 status = LyXText::NEED_MORE_REFRESH;
2906        
2907                                 BreakAgain(row->previous);
2908
2909                                 // set the cursor again. Otherwise
2910                                 // dangling pointers are possible
2911                                 SetCursor(cursor.par, cursor.pos);
2912                                 sel_cursor = cursor;
2913                                 return;
2914                         }
2915                 }
2916
2917                 int tmpheight = row->height;
2918                 LyXParagraph::size_type tmplast = RowLast(row);
2919                 refresh_y = y;
2920                 refresh_row = row;
2921
2922                 BreakAgain(row);
2923                 if (row->height == tmpheight && RowLast(row) == tmplast)
2924                         status = LyXText::NEED_VERY_LITTLE_REFRESH;
2925                 else
2926                         status = LyXText::NEED_MORE_REFRESH; 
2927    
2928                 // check the special right address boxes
2929                 if (textclasslist.Style(parameters->textclass,
2930                                         par->GetLayout()).margintype
2931                     == MARGIN_RIGHT_ADDRESS_BOX) {
2932                         tmpcursor.par = par;
2933                         tmpcursor.row = row;
2934                         tmpcursor.y = y;
2935                         tmpcursor.x = 0;
2936                         tmpcursor.x_fix = 0;
2937                         tmpcursor.pos = pos;
2938                         RedoDrawingOfParagraph(tmpcursor); 
2939                 }
2940    
2941         }
2942
2943         // set the cursor again. Otherwise dangling pointers are possible
2944         // also set the selection
2945    
2946         if (selection) {
2947                 tmpcursor = cursor;
2948                 SetCursorIntern(sel_cursor.par, sel_cursor.pos);
2949                 sel_cursor = cursor; 
2950                 SetCursorIntern(sel_start_cursor.par, sel_start_cursor.pos);
2951                 sel_start_cursor = cursor; 
2952                 SetCursorIntern(sel_end_cursor.par, sel_end_cursor.pos);
2953                 sel_end_cursor = cursor; 
2954                 SetCursorIntern(last_sel_cursor.par, last_sel_cursor.pos);
2955                 last_sel_cursor = cursor; 
2956                 cursor = tmpcursor;
2957         }
2958         SetCursorIntern(cursor.par, cursor.pos);
2959 }
2960
2961
2962 // returns 0 if inset wasn't found
2963 int LyXText::UpdateInset(Inset * inset)
2964 {
2965         // first check the current paragraph
2966         int pos = cursor.par->GetPositionOfInset(inset);
2967         if (pos != -1){
2968                 CheckParagraph(cursor.par, pos);
2969                 return 1;
2970         }
2971   
2972         // check every paragraph
2973   
2974         LyXParagraph * par = FirstParagraph();
2975         do {
2976                 // make sure the paragraph is open
2977                 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
2978                         pos = par->GetPositionOfInset(inset);
2979                         if (pos != -1){
2980                                 CheckParagraph(par, pos);
2981                                 return 1;
2982                         }
2983                 }
2984                 par = par->Next();
2985         } while (par);
2986   
2987         return 0;
2988 }
2989
2990
2991 void LyXText::SetCursor(LyXParagraph * par,
2992                         LyXParagraph::size_type pos, bool setfont) const
2993 {
2994         LyXCursor old_cursor = cursor;
2995         SetCursorIntern(par, pos, setfont);
2996         DeleteEmptyParagraphMechanism(old_cursor);
2997 }
2998
2999
3000 void LyXText::SetCursorIntern(LyXParagraph * par,
3001                               LyXParagraph::size_type pos, bool setfont) const
3002 {
3003         // correct the cursor position if impossible
3004         if (pos > par->Last()){
3005                 LyXParagraph * tmppar = par->ParFromPos(pos);
3006                 pos = par->PositionInParFromPos(pos);
3007                 par = tmppar;
3008         }
3009         if (par->IsDummy() && par->previous &&
3010             par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) {
3011                 while (par->previous &&
3012                        ((par->previous->IsDummy() && par->previous->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE) ||
3013                         (par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE))) {
3014                         par = par->previous ;
3015                         if (par->IsDummy() &&
3016                             par->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3017                                 pos += par->size() + 1;
3018                 }
3019                 if (par->previous) {
3020                         par = par->previous;
3021                 }
3022                 pos += par->size() + 1;
3023         }
3024
3025         cursor.par = par;
3026         cursor.pos = pos;
3027
3028         if (setfont)
3029                 if (cursor.pos && 
3030                     (cursor.pos == cursor.par->Last() || cursor.par->IsSeparator(cursor.pos)
3031                      || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3032                          && !cursor.par->IsSeparator(cursor.pos))
3033                      || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3034                      )) {
3035                         current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3036                         real_current_font = GetFont(cursor.par, cursor.pos - 1);
3037                 } else {
3038                         current_font = cursor.par->GetFontSettings(cursor.pos);
3039                         real_current_font = GetFont(cursor.par, cursor.pos);
3040                 }
3041
3042         /* get the cursor y position in text  */
3043         long y = 0;
3044         Row * row = GetRow(par, pos, y);
3045         /* y is now the beginning of the cursor row */ 
3046         y += row->baseline;
3047         /* y is now the cursor baseline */ 
3048         cursor.y = y;
3049    
3050         /* now get the cursors x position */
3051         float x;
3052         float fill_separator, fill_hfill, fill_label_hfill;
3053         PrepareToPrint(row, x, fill_separator, fill_hfill, fill_label_hfill);
3054         LyXParagraph::size_type cursor_vpos;
3055         LyXParagraph::size_type last = RowLast(row);
3056         if (row->pos > last)
3057                 cursor_vpos = 0;
3058         else if (pos > last)
3059                 cursor_vpos = (row->par->getLetterDirection(last) == LYX_DIR_LEFT_TO_RIGHT)
3060                         ? log2vis(last)+1 : log2vis(last);
3061         else {      
3062                 LyXDirection letter_direction =
3063                         row->par->getLetterDirection(pos);
3064                 LyXDirection font_direction =
3065                         (real_current_font.isVisibleRightToLeft())
3066                         ?  LYX_DIR_RIGHT_TO_LEFT : LYX_DIR_LEFT_TO_RIGHT;
3067                 if (letter_direction == font_direction
3068                     || pos <= row->pos
3069                     || (row->par->table && row->par->IsNewline(pos-1)))
3070                         cursor_vpos = (letter_direction == LYX_DIR_LEFT_TO_RIGHT)
3071                                 ? log2vis(pos) : log2vis(pos) + 1;
3072                 else
3073                         cursor_vpos = (font_direction == LYX_DIR_LEFT_TO_RIGHT)
3074                                 ? log2vis(pos-1) + 1 : log2vis(pos - 1);
3075         }
3076         
3077         /* table stuff -- begin*/
3078         if (row->par->table) {
3079                 int cell = NumberOfCell(row->par, row->pos);
3080                 float x_old = x;
3081                 x += row->par->table->GetBeginningOfTextInCell(cell);
3082                 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos)  {
3083                         pos = vis2log(vpos);
3084                         if (row->par->IsNewline(pos)) {
3085                                 x = x_old + row->par->table->WidthOfColumn(cell);
3086                                 x_old = x;
3087                                 ++cell;
3088                                 x += row->par->table->GetBeginningOfTextInCell(cell);
3089                         } else {
3090                                 x += SingleWidth(row->par, pos);
3091                         }
3092                 }
3093         } else {
3094                 /* table stuff -- end*/
3095                 LyXParagraph::size_type main_body =
3096                         BeginningOfMainBody(row->par);
3097                 if (main_body > 0 &&
3098                     (main_body-1 > last || 
3099                      !row->par->IsLineSeparator(main_body-1)))
3100                         main_body = 0;
3101
3102                 for (LyXParagraph::size_type vpos = row->pos; vpos < cursor_vpos; ++vpos)  {
3103                         pos = vis2log(vpos);
3104                         if (main_body > 0 && pos == main_body-1) {
3105                                 x += fill_label_hfill +
3106                                         lyxfont::width(textclasslist
3107                                                        .Style(parameters->textclass,
3108                                                               row->par->GetLayout())
3109                                                        .labelsep,
3110                                                        GetFont(row->par, -2));
3111                                 if (row->par->IsLineSeparator(main_body-1))
3112                                         x -= SingleWidth(row->par, main_body-1);
3113                         }
3114                         if (HfillExpansion(row, pos)) {
3115                                 x += SingleWidth(row->par, pos);
3116                                 if (pos >= main_body)
3117                                         x += fill_hfill;
3118                                 else 
3119                                         x += fill_label_hfill;
3120                         }
3121                         else if (row->par->IsSeparator(pos)) {
3122                                 if (pos != last ||
3123                                     !row->next ||
3124                                     row->next->par != row->par ||
3125                                     row->par->getParDirection() ==
3126                                     row->par->getLetterDirection(last)) {
3127                                         x += SingleWidth(row->par, pos);
3128                                         if (pos >= main_body)
3129                                                 x += fill_separator;
3130                                 }
3131                         } else
3132                                 x += SingleWidth(row->par, pos);
3133                 }
3134         }
3135    
3136         cursor.x = int(x);
3137    
3138         cursor.x_fix = cursor.x;
3139         cursor.row = row;
3140 }
3141
3142
3143 void LyXText::SetCursorFromCoordinates(int x, long y) const
3144 {
3145         LyXCursor old_cursor = cursor;
3146    
3147         /* get the row first */ 
3148    
3149         Row * row = GetRowNearY(y);
3150    
3151         cursor.par = row->par;
3152    
3153         int column = GetColumnNearX(row, x);
3154         cursor.pos = row->pos + column;
3155         cursor.x = x;
3156         cursor.y = y + row->baseline;
3157    
3158         cursor.row = row;
3159     
3160         if (cursor.pos && 
3161             (cursor.pos == cursor.par->Last()
3162              || cursor.par->IsSeparator(cursor.pos)
3163              || (cursor.pos && cursor.pos == BeginningOfMainBody(cursor.par)
3164                  && !cursor.par->IsSeparator(cursor.pos))
3165              || (cursor.par->table && cursor.par->IsNewline(cursor.pos))
3166                     )) {
3167                 current_font = cursor.par->GetFontSettings(cursor.pos - 1);
3168                 real_current_font = GetFont(cursor.par, cursor.pos - 1);
3169         } else {
3170                 current_font = cursor.par->GetFontSettings(cursor.pos);
3171                 real_current_font = GetFont(cursor.par, cursor.pos);
3172         }
3173         DeleteEmptyParagraphMechanism(old_cursor);
3174 }
3175
3176 void LyXText::SetCursorFromCoordinates(LyXCursor & cur, int x, long y) const
3177 {
3178         /* get the row first */ 
3179    
3180         Row * row = GetRowNearY(y);
3181         int column = GetColumnNearX(row, x);
3182    
3183         cur.par = row->par;
3184         cur.pos = row->pos + column;
3185         cur.x = x;
3186         cur.y = y + row->baseline;
3187         cur.row = row;
3188 }
3189
3190
3191 void LyXText::CursorLeft() const
3192 {
3193         CursorLeftIntern();
3194         if (cursor.par->table) {
3195                 int cell = NumberOfCell(cursor.par, cursor.pos);
3196                 if (cursor.par->table->IsContRow(cell) &&
3197                     cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell)) < 0) {
3198                         CursorUp();
3199                 }
3200         }
3201 }
3202
3203
3204 void LyXText::CursorLeftIntern() const
3205 {
3206         if (cursor.pos > 0) {
3207                 SetCursor(cursor.par, cursor.pos - 1);
3208         }
3209         else if (cursor.par->Previous()) { // steps into the above paragraph.
3210                 SetCursor(cursor.par->Previous(), cursor.par->Previous()->Last());
3211         }
3212 }
3213
3214
3215 void LyXText::CursorRight() const
3216 {
3217         CursorRightIntern();
3218         if (cursor.par->table) {
3219                 int cell = NumberOfCell(cursor.par, cursor.pos);
3220                 if (cursor.par->table->IsContRow(cell) &&
3221                     cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3222                         CursorUp();
3223                 }
3224         }
3225 }
3226
3227
3228 void LyXText::CursorRightIntern() const
3229 {
3230         if (cursor.pos < cursor.par->Last()) {
3231                 SetCursor(cursor.par, cursor.pos + 1);
3232         }
3233         else if (cursor.par->Next()) {
3234                 SetCursor(cursor.par->Next(), 0);
3235         }
3236 }
3237
3238
3239 void LyXText::CursorUp() const
3240 {
3241         SetCursorFromCoordinates(cursor.x_fix, 
3242                                  cursor.y - cursor.row->baseline - 1);
3243         if (cursor.par->table) {
3244                 int cell = NumberOfCell(cursor.par, cursor.pos);
3245                 if (cursor.par->table->IsContRow(cell) &&
3246                     cursor.par->table->CellHasContRow(cursor.par->table->GetCellAbove(cell))<0) {
3247                         CursorUp();
3248                 }
3249         }
3250 }
3251
3252
3253 void LyXText::CursorDown() const
3254 {
3255         if (cursor.par->table &&
3256             cursor.par->table->ShouldBeVeryLastRow(NumberOfCell(cursor.par, cursor.pos)) &&
3257             !cursor.par->next)
3258                 return;
3259         SetCursorFromCoordinates(cursor.x_fix, 
3260                                  cursor.y - cursor.row->baseline
3261                                  + cursor.row->height + 1);
3262         if (cursor.par->table) {
3263                 int cell = NumberOfCell(cursor.par, cursor.pos);
3264                 int cell_above = cursor.par->table->GetCellAbove(cell);
3265                 while(cursor.par->table &&
3266                       cursor.par->table->IsContRow(cell) &&
3267                       (cursor.par->table->CellHasContRow(cell_above)<0)) {
3268                     SetCursorFromCoordinates(cursor.x_fix, 
3269                                              cursor.y - cursor.row->baseline
3270                                              + cursor.row->height + 1);
3271                     if (cursor.par->table) {
3272                         cell = NumberOfCell(cursor.par, cursor.pos);
3273                         cell_above = cursor.par->table->GetCellAbove(cell);
3274                     }
3275                 }
3276         }
3277 }
3278
3279
3280 void LyXText::CursorUpParagraph() const
3281 {
3282         if (cursor.pos > 0) {
3283                 SetCursor(cursor.par, 0);
3284         }
3285         else if (cursor.par->Previous()) {
3286                 SetCursor(cursor.par->Previous(), 0);
3287         }
3288 }
3289
3290
3291 void LyXText::CursorDownParagraph() const
3292 {
3293         if (cursor.par->Next()) {
3294                 SetCursor(cursor.par->Next(), 0);
3295         } else {
3296                 SetCursor(cursor.par, cursor.par->Last());
3297         }
3298 }
3299
3300
3301
3302 void LyXText::DeleteEmptyParagraphMechanism(LyXCursor const & old_cursor) const
3303 {
3304         // Would be wrong to delete anything if we have a selection.
3305         if (selection) return;
3306
3307         // We allow all kinds of "mumbo-jumbo" when freespacing.
3308         if (textclasslist.Style(parameters->textclass,
3309                                 old_cursor.par->GetLayout()).free_spacing)
3310                 return;
3311
3312         bool deleted = false;
3313         
3314 #ifdef FIX_DOUBLE_SPACE
3315         /* Ok I'll put some comments here about what is missing.
3316            I have fixed BackSpace (and thus Delete) to not delete
3317            double-spaces automagically. I have also changed Cut,
3318            Copy and Paste to hopefully do some sensible things.
3319            There are still some small problems that can lead to
3320            double spaces stored in the document file or space at
3321            the beginning of paragraphs. This happens if you have
3322            the cursor betwenn to spaces and then save. Or if you
3323            cut and paste and the selection have a space at the
3324            beginning and then save right after the paste. I am
3325            sure none of these are very hard to fix, but I will
3326            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
3327            that I can get some feedback. (Lgb)
3328         */
3329
3330         // If old_cursor.pos == 0 and old_cursor.pos(1) == LineSeparator
3331         // delete the LineSeparator.
3332         // MISSING
3333
3334         // If old_cursor.pos == 1 and old_cursor.pos(0) == LineSeparator
3335         // delete the LineSeparator.
3336         // MISSING
3337
3338         // If the pos around the old_cursor were spaces, delete one of them.
3339         if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) { // Only if the cursor has really moved
3340                 
3341                 if (old_cursor.pos > 0
3342                     && old_cursor.pos < old_cursor.par->Last()
3343                     && old_cursor.par->IsLineSeparator(old_cursor.pos)
3344                     && old_cursor.par->IsLineSeparator(old_cursor.pos - 1)) {
3345                         old_cursor.par->Erase(old_cursor.pos - 1);
3346                         status = LyXText::NEED_MORE_REFRESH;
3347                         // correct cursor
3348                         if (old_cursor.par == cursor.par &&
3349                             cursor.pos > old_cursor.pos) {
3350                                 //SetCursor(cursor.par, cursor.pos - 1);
3351                                 cursor.pos--;
3352                         } //else
3353                         //      SetCursor(cursor.par, cursor.pos);
3354                         return;
3355                 }
3356         }
3357 #endif
3358 #if 1
3359         // Do not delete empty paragraphs with keepempty set.
3360         if ((textclasslist.Style(parameters->textclass,
3361                                  old_cursor.par->GetLayout())).keepempty)
3362                 return;
3363
3364         LyXCursor tmpcursor;
3365
3366         if (old_cursor.par != cursor.par) {
3367                 if ( (old_cursor.par->Last() == 0
3368                       || (old_cursor.par->Last() == 1
3369                           && old_cursor.par->IsLineSeparator(0)))
3370                      && old_cursor.par->FirstPhysicalPar()
3371                      == old_cursor.par->LastPhysicalPar()) {
3372                         // ok, we will delete anything
3373                         
3374                         // make sure that you do not delete any environments
3375                         if ((old_cursor.par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE &&
3376                              !(old_cursor.row->previous 
3377                                && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3378                              && !(old_cursor.row->next 
3379                                   && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3380                             || (old_cursor.par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
3381                                 && ((old_cursor.row->previous 
3382                                      && old_cursor.row->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
3383                                     || (old_cursor.row->next
3384                                         && old_cursor.row->next->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE))
3385                                     )) {
3386                                 status = LyXText::NEED_MORE_REFRESH;
3387                                 deleted = true;
3388                                 
3389                                 if (old_cursor.row->previous) {
3390                                         refresh_row = old_cursor.row->previous;
3391                                         refresh_y = old_cursor.y - old_cursor.row->baseline - refresh_row->height;
3392                                         tmpcursor = cursor;
3393                                         cursor = old_cursor; // that undo can restore the right cursor position
3394                                         LyXParagraph * endpar = old_cursor.par->next;
3395                                         if (endpar && endpar->GetDepth()) {
3396                                                 while (endpar && endpar->GetDepth()) {
3397                                                         endpar = endpar->LastPhysicalPar()->Next();
3398                                                 }
3399                                         }
3400                                         SetUndo(Undo::DELETE,
3401                                                 old_cursor.par->previous,
3402                                                 endpar);
3403                                         cursor = tmpcursor;
3404
3405                                         // delete old row
3406                                         RemoveRow(old_cursor.row);
3407                                         if (params->paragraph == old_cursor.par) {
3408                                                 params->paragraph = params->paragraph->next;
3409                                         }
3410                                         // delete old par
3411                                         delete old_cursor.par;
3412                                         
3413                                         /* Breakagain the next par. Needed
3414                                          * because of the parindent that
3415                                          * can occur or dissappear. The
3416                                          * next row can change its height,
3417                                          * if there is another layout before */
3418                                         if (refresh_row->next) {
3419                                                 BreakAgain(refresh_row->next);
3420                                                 UpdateCounters(refresh_row);
3421                                         }
3422                                         SetHeightOfRow(refresh_row);
3423                                 } else {
3424                                         refresh_row = old_cursor.row->next;
3425                                         refresh_y = old_cursor.y - old_cursor.row->baseline;
3426                                         
3427                                         tmpcursor = cursor;
3428                                         cursor = old_cursor; // that undo can restore the right cursor position
3429                                         LyXParagraph *endpar = old_cursor.par->next;
3430                                         if (endpar && endpar->GetDepth()) {
3431                                                 while (endpar && endpar->GetDepth()) {
3432                                                         endpar = endpar->LastPhysicalPar()->Next();
3433                                                 }
3434                                         }
3435                                         SetUndo(Undo::DELETE,
3436                                                 old_cursor.par->previous,
3437                                                 endpar);
3438                                         cursor = tmpcursor;
3439
3440                                         // delete old row
3441                                         RemoveRow(old_cursor.row);
3442                                         // delete old par
3443                                         if (params->paragraph == old_cursor.par) {
3444                                                 params->paragraph = params->paragraph->next;
3445                                         }
3446                                         delete old_cursor.par;
3447                                         
3448                                         /* Breakagain the next par. Needed
3449                                            because of the parindent that can
3450                                            occur or dissappear.
3451                                            The next row can change its height,
3452                                            if there is another layout before
3453                                         */ 
3454                                         if (refresh_row) {
3455                                                 BreakAgain(refresh_row);
3456                                                 UpdateCounters(refresh_row->previous);
3457                                         }
3458                                 }
3459                                 
3460                                 // correct cursor y
3461                                 SetCursor(cursor.par, cursor.pos);
3462                                 
3463                                 /* if (cursor.y > old_cursor.y)
3464                                    cursor.y -= old_cursor.row->height; */ 
3465          
3466                                 if (sel_cursor.par  == old_cursor.par
3467                                     && sel_cursor.pos == sel_cursor.pos) {
3468                                         // correct selection
3469                                         sel_cursor = cursor;
3470                                 }
3471                         }
3472                 }
3473                 if (!deleted) {
3474                         if (old_cursor.par->ClearParagraph()) {
3475                                 RedoParagraphs(old_cursor, old_cursor.par->Next());
3476                                 // correct cursor y
3477                                 SetCursor(cursor.par, cursor.pos);
3478                                 sel_cursor = cursor;
3479                         }
3480                 }
3481         }
3482 #endif
3483 }
3484
3485
3486 LyXParagraph * LyXText::GetParFromID(int id)
3487 {
3488         LyXParagraph * result = FirstParagraph();
3489         while (result && result->id() != id)
3490                 result = result->next;
3491         return result;
3492 }
3493
3494
3495 // undo functions
3496 bool LyXText::TextUndo()
3497 {
3498         // returns false if no undo possible
3499         Undo * undo = params->undostack.pop();
3500         if (undo) {
3501                 FinishUndo();
3502                 if (!undo_frozen)
3503                         params->redostack
3504                                 .push(CreateUndo(undo->kind, 
3505                                                  GetParFromID(undo->number_of_before_par),
3506                                                  GetParFromID(undo->number_of_behind_par)));
3507         }
3508         return TextHandleUndo(undo);
3509 }
3510
3511
3512 bool LyXText::TextRedo()
3513 {
3514         // returns false if no redo possible
3515         Undo * undo = params->redostack.pop();
3516         if (undo) {
3517                 FinishUndo();
3518                 if (!undo_frozen)
3519                         params->undostack
3520                                 .push(CreateUndo(undo->kind, 
3521                                                  GetParFromID(undo->number_of_before_par),
3522                                                  GetParFromID(undo->number_of_behind_par)));
3523         }
3524         return TextHandleUndo(undo);
3525 }
3526
3527
3528 bool LyXText::TextHandleUndo(Undo * undo)
3529 {
3530         // returns false if no undo possible
3531         bool result = false;
3532         if (undo) {
3533                 LyXParagraph * before =
3534                         GetParFromID(undo->number_of_before_par); 
3535                 LyXParagraph * behind =
3536                         GetParFromID(undo->number_of_behind_par); 
3537                 LyXParagraph * tmppar;
3538                 LyXParagraph * tmppar2;
3539                 LyXParagraph * endpar;
3540                 LyXParagraph * tmppar5;
3541     
3542                 // if there's no before take the beginning
3543                 // of the document for redoing
3544                 if (!before)
3545                         SetCursorIntern(FirstParagraph(), 0);
3546
3547                 // replace the paragraphs with the undo informations
3548
3549                 LyXParagraph * tmppar3 = undo->par;
3550                 undo->par = 0; // otherwise the undo destructor would delete the paragraph
3551                 LyXParagraph * tmppar4 = tmppar3;
3552                 if (tmppar4){
3553                         while (tmppar4->next)
3554                                 tmppar4 = tmppar4->next;
3555                 } // get last undo par
3556     
3557                 // now remove the old text if there is any
3558                 if (before != behind || (!behind && !before)){
3559                         if (before)
3560                                 tmppar5 = before->next;
3561                         else
3562                                 tmppar5 = params->paragraph;
3563                         tmppar2 = tmppar3;
3564                         while (tmppar5 && tmppar5 != behind){
3565                                 tmppar = tmppar5;
3566                                 tmppar5 = tmppar5->next;
3567                                 // a memory optimization for edit: Only layout information
3568                                 // is stored in the undo. So restore the text informations.
3569                                 if (undo->kind == Undo::EDIT) {
3570                                         tmppar2->setContentsFromPar(tmppar);
3571                                         tmppar->clearContents();
3572                                         //tmppar2->text = tmppar->text;
3573                                         //tmppar->text.clear();
3574                                         tmppar2 = tmppar2->next;
3575                                 }
3576                                 if ( currentrow && currentrow->par == tmppar )
3577                                         currentrow = currentrow -> previous;
3578                                 // Commenting out this might remove the error
3579                                 // reported by Purify, but it might also
3580                                 // introduce a memory leak. We need to
3581                                 // check this (Lgb)
3582                                 //delete tmppar;
3583                         }
3584                 }
3585     
3586                 // put the new stuff in the list if there is one
3587                 if (tmppar3){
3588                         if (before)
3589                                 before->next = tmppar3;
3590                         else
3591                                 params->paragraph = tmppar3;
3592                         tmppar3->previous = before;
3593                 }
3594                 else {
3595                         if (!before)
3596                                 params->paragraph = behind;
3597                 }
3598                 if (tmppar4) {
3599                         tmppar4->next = behind;
3600                         if (behind)
3601                                 behind->previous = tmppar4;
3602                 }
3603     
3604     
3605                 // Set the cursor for redoing
3606                 if (before) {
3607                         SetCursorIntern(before->FirstSelfrowPar(), 0);
3608                         // check wether before points to a closed float and open it if necessary
3609                         if (before && before->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
3610                             && before->next && before->next->footnoteflag != LyXParagraph::NO_FOOTNOTE){
3611                                 tmppar4 = before;
3612                                 while (tmppar4->previous && 
3613                                        tmppar4->previous->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE)
3614                                         tmppar4 = tmppar4->previous;
3615                                 while (tmppar4 && tmppar4->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3616                                         tmppar4->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3617                                         tmppar4 = tmppar4->next;
3618                                 }
3619                         }
3620                 }
3621     
3622                 // open a cosed footnote at the end if necessary
3623                 if (behind && behind->previous && 
3624                     behind->previous->footnoteflag != LyXParagraph::NO_FOOTNOTE &&
3625                     behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3626                         while (behind && behind->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE){
3627                                 behind->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
3628                                 behind = behind->next;
3629                         }
3630                 }
3631     
3632                 // calculate the endpar for redoing the paragraphs.
3633                 if (behind){
3634                         if (behind->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
3635                                 endpar = behind->LastPhysicalPar()->Next();
3636                         else
3637                                 endpar = behind->NextAfterFootnote()->LastPhysicalPar()->Next();
3638                 }
3639                 else
3640                         endpar = behind;
3641     
3642                 tmppar = GetParFromID(undo->number_of_cursor_par);
3643                 RedoParagraphs(cursor, endpar); 
3644                 if (tmppar){
3645                         SetCursorIntern(tmppar, undo->cursor_pos);
3646                         UpdateCounters(cursor.row);
3647                 }
3648                 result = true;
3649                 delete undo;
3650         }
3651         FinishUndo();
3652         return result;
3653 }
3654
3655
3656 void LyXText::FinishUndo()
3657 {
3658         // makes sure the next operation will be stored
3659         undo_finished = True;
3660 }
3661
3662
3663 void LyXText::FreezeUndo()
3664 {
3665         // this is dangerous and for internal use only
3666         undo_frozen = True;
3667 }
3668
3669
3670 void LyXText::UnFreezeUndo()
3671 {
3672         // this is dangerous and for internal use only
3673         undo_frozen = false;
3674 }
3675
3676
3677 void LyXText::SetUndo(Undo::undo_kind kind, LyXParagraph const * before,
3678                       LyXParagraph const * behind) const
3679 {
3680         if (!undo_frozen)
3681                 params->undostack.push(CreateUndo(kind, before, behind));
3682         params->redostack.clear();
3683 }
3684
3685
3686 void LyXText::SetRedo(Undo::undo_kind kind, LyXParagraph const * before,
3687                       LyXParagraph const * behind)
3688 {
3689         params->redostack.push(CreateUndo(kind, before, behind));
3690 }
3691
3692
3693 Undo * LyXText::CreateUndo(Undo::undo_kind kind, LyXParagraph const * before,
3694                           LyXParagraph const * behind) const
3695 {
3696         int before_number = -1;
3697         int behind_number = -1;
3698         if (before)
3699                 before_number = before->id();
3700         if (behind)
3701                 behind_number = behind->id();
3702         // Undo::EDIT  and Undo::FINISH are
3703         // always finished. (no overlapping there)
3704         // overlapping only with insert and delete inside one paragraph: 
3705         // Nobody wants all removed  character
3706         // appear one by one when undoing. 
3707         // EDIT is special since only layout information, not the
3708         // contents of a paragaph are stored.
3709         if (!undo_finished && kind != Undo::EDIT && 
3710             kind != Undo::FINISH){
3711                 // check wether storing is needed
3712                 if (!params->undostack.empty() && 
3713                     params->undostack.top()->kind == kind &&
3714                     params->undostack.top()->number_of_before_par ==  before_number &&
3715                     params->undostack.top()->number_of_behind_par ==  behind_number ){
3716                         // no undo needed
3717                         return 0;
3718                 }
3719         }
3720         // create a new Undo
3721         LyXParagraph * undopar;
3722         LyXParagraph * tmppar;
3723         LyXParagraph * tmppar2;
3724
3725         LyXParagraph * start = 0;
3726         LyXParagraph * end = 0;
3727   
3728         if (before)
3729                 start = before->next;
3730         else
3731                 start = FirstParagraph();
3732         if (behind)
3733                 end = behind->previous;
3734         else {
3735                 end = FirstParagraph();
3736                 while (end->next)
3737                         end = end->next;
3738         }
3739
3740         if (start && end
3741             && start != end->next
3742             && (before != behind || (!before && !behind))) {
3743                 tmppar = start;
3744                 tmppar2 = tmppar->Clone();
3745                 tmppar2->id(tmppar->id());
3746
3747                 // a memory optimization: Just store the layout information
3748                 // when only edit
3749                 if (kind == Undo::EDIT){
3750                         //tmppar2->text.clear();
3751                         tmppar2->clearContents();
3752                 }
3753
3754                 undopar = tmppar2;
3755   
3756                 while (tmppar != end && tmppar->next) {
3757                         tmppar = tmppar->next;
3758                         tmppar2->next = tmppar->Clone();
3759                         tmppar2->next->id(tmppar->id());
3760                         // a memory optimization: Just store the layout
3761                         // information when only edit
3762                         if (kind == Undo::EDIT){
3763                                 //tmppar2->next->text.clear();
3764                                 tmppar2->clearContents();
3765                         }
3766                         tmppar2->next->previous = tmppar2;
3767                         tmppar2 = tmppar2->next;
3768                 }
3769                 tmppar2->next = 0;
3770         } else
3771                 undopar = 0; // nothing to replace (undo of delete maybe)
3772   
3773         int cursor_par = cursor.par->ParFromPos(cursor.pos)->id();
3774         int cursor_pos =  cursor.par->PositionInParFromPos(cursor.pos);
3775
3776         Undo * undo = new Undo(kind, 
3777                                before_number, behind_number,  
3778                                cursor_par, cursor_pos, 
3779                                undopar);
3780   
3781         undo_finished = false;
3782         return undo;
3783 }
3784
3785
3786 void LyXText::SetCursorParUndo()
3787 {
3788         SetUndo(Undo::FINISH, 
3789                 cursor.par->ParFromPos(cursor.pos)->previous, 
3790                 cursor.par->ParFromPos(cursor.pos)->next); 
3791 }
3792
3793
3794 void LyXText::RemoveTableRow(LyXCursor * cur) const
3795 {
3796         int cell = -1;
3797         int cell_org = 0;
3798         int ocell = 0;
3799     
3800         // move to the previous row
3801         int cell_act = NumberOfCell(cur->par, cur->pos);
3802         if (cell < 0)
3803                 cell = cell_act;
3804         while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3805                 cur->pos--;
3806         while (cur->pos && 
3807                !cur->par->table->IsFirstCell(cell_act)) {
3808                 cur->pos--;
3809                 while (cur->pos && !cur->par->IsNewline(cur->pos - 1))
3810                         cur->pos--;
3811                 --cell;
3812                 --cell_act;
3813         }
3814         // now we have to pay attention if the actual table is the
3815         //   main row of TableContRows and if yes to delete all of them
3816         if (!cell_org)
3817                 cell_org = cell;
3818         do {
3819                 ocell = cell;
3820                 // delete up to the next row
3821                 while (cur->pos < cur->par->Last() && 
3822                        (cell_act == ocell
3823                         || !cur->par->table->IsFirstCell(cell_act))) {
3824                         while (cur->pos < cur->par->Last() &&
3825                                !cur->par->IsNewline(cur->pos))
3826                                 cur->par->Erase(cur->pos);
3827                         ++cell;
3828                         ++cell_act;
3829                         if (cur->pos < cur->par->Last())
3830                                 cur->par->Erase(cur->pos);
3831                 }
3832                 if (cur->pos && cur->pos == cur->par->Last()) {
3833                         cur->pos--;
3834                         cur->par->Erase(cur->pos); // no newline at very end!
3835                 }
3836         } while (((cell + 1) < cur->par->table->GetNumberOfCells()) &&
3837                  !cur->par->table->IsContRow(cell_org) &&
3838                  cur->par->table->IsContRow(cell));
3839         cur->par->table->DeleteRow(cell_org);
3840         return;
3841 }
3842
3843
3844 bool LyXText::IsEmptyTableCell() const
3845 {
3846         LyXParagraph::size_type pos = cursor.pos - 1;
3847         while (pos >= 0 && pos < cursor.par->Last()
3848                && !cursor.par->IsNewline(pos))
3849                 --pos;
3850         return cursor.par->IsNewline(pos + 1);
3851 }
3852
3853
3854 void LyXText::toggleAppendix(){
3855         LyXParagraph * par = cursor.par->FirstPhysicalPar();
3856         bool start = !par->start_of_appendix;
3857
3858         // ensure that we have only one start_of_appendix in this document
3859         LyXParagraph * tmp = FirstParagraph();
3860         for (; tmp; tmp = tmp->next)
3861                 tmp->start_of_appendix = 0;
3862         par->start_of_appendix = start;
3863
3864         // we can set the refreshing parameters now
3865         status = LyXText::NEED_MORE_REFRESH;
3866         refresh_y = 0;
3867         refresh_row = 0; // not needed for full update
3868         UpdateCounters(0);
3869         SetCursor(cursor.par, cursor.pos);
3870 }
3871