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