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