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