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