]> git.lyx.org Git - lyx.git/blob - src/text2.C
Fixed cut&paste bugs and added freespacing for ERT Insets.
[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-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation "lyxtext.h"
15 #endif
16
17 #include "lyxtext.h"
18 #include "LString.h"
19 #include "paragraph.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
25 #include "layout.h"
26 #include "LyXView.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
30 #include "buffer.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
33 #include "gettext.h"
34 #include "BufferView.h"
35 #include "LyXView.h"
36 #include "CutAndPaste.h"
37 #include "Painter.h"
38 #include "font.h"
39 #include "debug.h"
40 #include "lyxrc.h"
41 #include "lyxrow.h"
42 #include "FloatList.h"
43 #include "language.h"
44 #include "ParagraphParameters.h"
45 #include "support/LAssert.h"
46
47 using std::copy;
48 using std::find;
49 using std::endl;
50 using std::find;
51 using std::pair;
52 using lyx::pos_type;
53
54
55 LyXText::LyXText(BufferView * bv)
56         : number_of_rows(0), height(0), width(0), first(0),
57           bv_owner(bv), inset_owner(0), the_locking_inset(0),
58           need_break_row(0), refresh_y(0), refresh_row(0),
59           status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
60           copylayouttype(0)
61 {}
62
63
64 LyXText::LyXText(InsetText * inset)
65         :  number_of_rows(0),  height(0), width(0), first(0),
66            bv_owner(0), inset_owner(inset), the_locking_inset(0),
67            need_break_row(0), refresh_y(0), refresh_row(0),
68            status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
69            copylayouttype(0)
70 {}
71
72 void LyXText::init(BufferView * bview, bool reinit)
73 {
74         if (reinit) {
75                 // Delete all rows, this does not touch the paragraphs!
76                 Row * tmprow = firstrow;
77                 while (firstrow) {
78                         tmprow = firstrow->next();
79                         delete firstrow;
80                         firstrow = tmprow;
81                 }
82                 lastrow = refresh_row = need_break_row = 0;
83                 width = height = copylayouttype = 0;
84                 number_of_rows = first = refresh_y = 0;
85                 status_ = LyXText::UNCHANGED;
86         } else if (firstrow)
87                 return;
88
89         Paragraph * par = ownerParagraph();
90         current_font = getFont(bview->buffer(), par, 0);
91         while (par) {
92                 insertParagraph(bview, par, lastrow);
93                 par = par->next();
94         }
95         setCursorIntern(bview, firstrow->par(), 0);
96         selection.cursor = cursor;
97 }
98
99
100 LyXText::~LyXText()
101 {
102         // Delete all rows, this does not touch the paragraphs!
103         Row * tmprow = firstrow;
104         while (firstrow) {
105                 tmprow = firstrow->next();
106                 delete firstrow;
107                 firstrow = tmprow;
108         }
109 }
110
111
112 namespace {
113
114 LyXFont const realizeFont(LyXFont const & font,
115                           Buffer const * buf,
116                           Paragraph * par)
117 {
118         LyXFont tmpfont(font);
119         Paragraph::depth_type par_depth = par->getDepth();
120         
121         // Resolve against environment font information
122         while (par && par_depth && !tmpfont.resolved()) {
123                 par = par->outerHook();
124                 if (par) {
125 #ifndef INHERIT_LANGUAGE
126                         tmpfont.realize(textclasslist.
127                                         Style(buf->params.textclass,
128                                               par->getLayout()).font);
129 #else
130                         tmpfont.realize(textclasslist.
131                                         Style(buf->params.textclass,
132                                               par->getLayout()).font,
133                                         buf->params.language);
134 #endif
135                         par_depth = par->getDepth();
136                 }
137         }
138
139 #ifndef INHERIT_LANGUAGE
140         tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
141 #else
142         tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
143                         buf->params.language);
144 #endif
145
146         return tmpfont;
147 }
148
149 }
150
151
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
156 // smaller. (Asger)
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
160                                pos_type pos) const
161 {
162         lyx::Assert(pos >= 0);
163         
164         LyXLayout const & layout = 
165                 textclasslist.Style(buf->params.textclass, par->getLayout());
166         
167         Paragraph::depth_type par_depth = par->getDepth();
168         // We specialize the 95% common case:
169         if (!par_depth) {
170                 if (layout.labeltype == LABEL_MANUAL
171                     && pos < beginningOfMainBody(buf, par)) {
172                                 // 1% goes here
173                         LyXFont f = par->getFontSettings(buf->params,
174                                                          pos);
175 #ifndef INHERIT_LANGUAGE
176                         return f.realize(layout.reslabelfont);
177 #else
178                         return f.realize(layout.reslabelfont, buf->params.language);
179 #endif
180                 } else {
181                         LyXFont f = par->getFontSettings(buf->params, pos);
182 #ifndef INHERIT_LANGUAGE
183                         return f.realize(layout.resfont);
184 #else
185                         return f.realize(layout.resfont, buf->params.language);
186 #endif
187                 }
188         }
189         
190         // The uncommon case need not be optimized as much
191         
192         LyXFont layoutfont;
193         
194         if (pos < beginningOfMainBody(buf, par)) {
195                 // 1% goes here
196                 layoutfont = layout.labelfont;
197         } else {
198                 // 99% goes here
199                 layoutfont = layout.font;
200         }
201
202         LyXFont tmpfont = par->getFontSettings(buf->params, pos);
203 #ifndef INHERIT_LANGUAGE
204         tmpfont.realize(layoutfont);
205 #else
206         tmpfont.realize(layoutfont, buf->params.language);
207 #endif
208         
209         return realizeFont(tmpfont, buf, par);
210 }
211
212
213 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
214 {
215         LyXLayout const & layout = 
216                 textclasslist.Style(buf->params.textclass, par->getLayout());
217
218         Paragraph::depth_type par_depth = par->getDepth();
219
220         if (!par_depth) {
221                 return layout.resfont;
222         }
223
224         return realizeFont(layout.font, buf, par);
225 }
226
227
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
229 {
230         LyXLayout const & layout = 
231                 textclasslist.Style(buf->params.textclass, par->getLayout());
232
233         Paragraph::depth_type par_depth = par->getDepth();
234
235         if (!par_depth) {
236                 return layout.reslabelfont;
237         }
238
239         return realizeFont(layout.labelfont, buf, par);
240 }
241
242
243 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
244                           pos_type pos, LyXFont const & fnt,
245                           bool toggleall)
246 {
247         Buffer const * buf = bv->buffer();
248         LyXFont font = getFont(buf, par, pos);
249         font.update(fnt, buf->params.language, toggleall);
250         // Let the insets convert their font
251         if (par->isInset(pos)) {
252                 Inset * inset = par->getInset(pos);
253                 if (isEditableInset(inset)) {
254                         UpdatableInset * uinset =
255                                 static_cast<UpdatableInset *>(inset);
256                         uinset->setFont(bv, fnt, toggleall, true);
257                 }
258         }
259
260         LyXLayout const & layout =
261                 textclasslist.Style(buf->params.textclass,
262                                     par->getLayout());
263
264         // Get concrete layout font to reduce against
265         LyXFont layoutfont;
266
267         if (pos < beginningOfMainBody(buf, par))
268                 layoutfont = layout.labelfont;
269         else
270                 layoutfont = layout.font;
271
272         // Realize against environment font information
273         if (par->getDepth()){
274                 Paragraph * tp = par;
275                 while (!layoutfont.resolved() && tp && tp->getDepth()) {
276                         tp = tp->outerHook();
277                         if (tp)
278 #ifndef INHERIT_LANGUAGE
279                                 layoutfont.realize(textclasslist.
280                                                    Style(buf->params.textclass,
281                                                          tp->getLayout()).font);
282 #else
283                                 layoutfont.realize(textclasslist.
284                                                    Style(buf->params.textclass,
285                                                          tp->getLayout()).font,
286                                                    buf->params.language);
287 #endif
288                 }
289         }
290
291 #ifndef INHERIT_LANGUAGE
292         layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
293 #else
294         layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
295                            buf->params.language);
296 #endif
297
298         // Now, reduce font against full layout font
299         font.reduce(layoutfont);
300
301         par->setFont(pos, font);
302 }
303
304
305 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
306                           pos_type pos, LyXFont const & fnt)
307 {
308         LyXFont font(fnt);
309
310         LyXLayout const & layout =
311                 textclasslist.Style(buf->params.textclass,
312                                     par->getLayout());
313
314         // Get concrete layout font to reduce against
315         LyXFont layoutfont;
316
317         if (pos < beginningOfMainBody(buf, par))
318                 layoutfont = layout.labelfont;
319         else
320                 layoutfont = layout.font;
321
322         // Realize against environment font information
323         if (par->getDepth()){
324                 Paragraph * tp = par;
325                 while (!layoutfont.resolved() && tp && tp->getDepth()) {
326                         tp = tp->outerHook();
327                         if (tp)
328 #ifndef INHERIT_LANGUAGE
329                                 layoutfont.realize(textclasslist.
330                                                    Style(buf->params.textclass,
331                                                          tp->getLayout()).font);
332 #else
333                                 layoutfont.realize(textclasslist.
334                                                    Style(buf->params.textclass,
335                                                          tp->getLayout()).font,
336                                                    buf->params.language);
337 #endif
338                 }
339         }
340
341 #ifndef INHERIT_LANGUAGE
342         layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
343 #else
344         layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
345                            buf->params.language);
346 #endif
347
348         // Now, reduce font against full layout font
349         font.reduce(layoutfont);
350
351         par->setFont(pos, font);
352 }
353
354
355 // inserts a new row behind the specified row, increments
356 // the touched counters
357 void LyXText::insertRow(Row * row, Paragraph * par,
358                         pos_type pos) const
359 {
360         Row * tmprow = new Row;
361         if (!row) {
362                 tmprow->previous(0);
363                 tmprow->next(firstrow);
364                 firstrow = tmprow;
365         } else {
366                 tmprow->previous(row);
367                 tmprow->next(row->next());
368                 row->next(tmprow);
369         }
370    
371         if (tmprow->next())
372                 tmprow->next()->previous(tmprow);
373    
374         if (tmprow->previous())
375                 tmprow->previous()->next(tmprow);
376    
377    
378         tmprow->par(par);
379         tmprow->pos(pos);
380
381         if (row == lastrow)
382                 lastrow = tmprow;
383         ++number_of_rows;
384 }
385
386
387 // removes the row and reset the touched counters
388 void LyXText::removeRow(Row * row) const
389 {
390         if (row->next())
391                 row->next()->previous(row->previous());
392         if (!row->previous()) {
393                 firstrow = row->next();
394 //              lyx::Assert(firstrow);
395         } else  {
396                 row->previous()->next(row->next());
397         }
398         if (row == lastrow)
399                 lastrow = row->previous();
400    
401         height -= row->height(); // the text becomes smaller
402    
403         delete row;
404         --number_of_rows;       // one row less
405 }
406
407
408 // remove all following rows of the paragraph of the specified row.
409 void LyXText::removeParagraph(Row * row) const
410 {
411         Paragraph * tmppar = row->par();
412         row = row->next();
413     
414         Row * tmprow;
415         while (row && row->par() == tmppar) {
416                 tmprow = row->next();
417                 removeRow(row);
418                 row = tmprow;
419         }
420 }
421    
422
423 // insert the specified paragraph behind the specified row
424 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
425                               Row * row) const
426 {
427         insertRow(row, par, 0);            /* insert a new row, starting 
428                                             * at postition 0 */
429
430         setCounter(bview->buffer(), par);  // set the counters
431    
432         // and now append the whole paragraph behind the new row
433         if (!row) {
434                 firstrow->height(0);
435                 appendParagraph(bview, firstrow);
436         } else {
437                 row->next()->height(0);
438                 appendParagraph(bview, row->next());
439         }
440 }
441
442
443 Inset * LyXText::getInset() const
444 {
445         Inset * inset = 0;
446         if (cursor.pos() == 0 && cursor.par()->bibkey) {
447                 inset = cursor.par()->bibkey;
448         } else if (cursor.pos() < cursor.par()->size() 
449                    && cursor.par()->isInset(cursor.pos())) {
450                 inset = cursor.par()->getInset(cursor.pos());
451         }
452         return inset;
453 }
454
455
456 void LyXText::toggleInset(BufferView * bview)
457 {
458         Inset * inset = getInset();
459         if (!isEditableInset(inset))
460                 return;
461         //bview->owner()->message(inset->editMessage());
462
463         // do we want to keep this?? (JMarc)
464         if (!isHighlyEditableInset(inset))
465                 setCursorParUndo(bview);
466
467         if (inset->isOpen()) {
468                 inset->close(bview);
469         } else {
470                 inset->open(bview);
471         }
472 #if 0
473         inset->open(bview, !inset->isOpen());
474 #endif
475 }
476
477
478 /* used in setlayout */
479 // Asger is not sure we want to do this...
480 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
481                                             Paragraph * par)
482 {
483         LyXLayout const & layout =
484                 textclasslist.Style(buf->params.textclass, par->getLayout());
485
486         LyXFont layoutfont;
487         for (pos_type pos = 0; pos < par->size(); ++pos) {
488                 if (pos < beginningOfMainBody(buf, par))
489                         layoutfont = layout.labelfont;
490                 else
491                         layoutfont = layout.font;
492       
493                 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
494                 tmpfont.reduce(layoutfont);
495                 par->setFont(pos, tmpfont);
496         }
497 }
498
499
500 Paragraph * LyXText::setLayout(BufferView * bview,
501                                LyXCursor & cur, LyXCursor & sstart_cur,
502                                LyXCursor & send_cur,
503                                lyx::layout_type layout)
504 {
505         Paragraph * endpar = send_cur.par()->next();
506         Paragraph * undoendpar = endpar;
507         
508         if (endpar && endpar->getDepth()) {
509                 while (endpar && endpar->getDepth()) {
510                         endpar = endpar->next();
511                         undoendpar = endpar;
512                 }
513         } else if (endpar) {
514                 endpar = endpar->next(); // because of parindents etc.
515         }
516         
517         setUndo(bview, Undo::EDIT,
518                 sstart_cur.par(), undoendpar);
519         
520         // ok we have a selection. This is always between sstart_cur
521         // and sel_end cursor
522         cur = sstart_cur;
523         
524         LyXLayout const & lyxlayout =
525                 textclasslist.Style(bview->buffer()->params.textclass, layout);
526         
527         while (cur.par() != send_cur.par()) {
528                 cur.par()->setLayout(layout);
529                 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
530                 Paragraph * fppar = cur.par();
531                 fppar->params().spaceTop(lyxlayout.fill_top ?
532                                          VSpace(VSpace::VFILL)
533                                          : VSpace(VSpace::NONE));
534                 fppar->params().spaceBottom(lyxlayout.fill_bottom ? 
535                                             VSpace(VSpace::VFILL)
536                                             : VSpace(VSpace::NONE));
537                 if (lyxlayout.margintype == MARGIN_MANUAL)
538                         cur.par()->setLabelWidthString(lyxlayout.labelstring());
539                 if (lyxlayout.labeltype != LABEL_BIBLIO
540                     && fppar->bibkey) {
541                         delete fppar->bibkey;
542                         fppar->bibkey = 0;
543                 }
544                 cur.par(cur.par()->next());
545         }
546         cur.par()->setLayout(layout);
547         makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
548         Paragraph * fppar = cur.par();
549         fppar->params().spaceTop(lyxlayout.fill_top ?
550                                  VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
551         fppar->params().spaceBottom(lyxlayout.fill_bottom ? 
552                                     VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
553         if (lyxlayout.margintype == MARGIN_MANUAL)
554                 cur.par()->setLabelWidthString(lyxlayout.labelstring());
555         if (lyxlayout.labeltype != LABEL_BIBLIO
556             && fppar->bibkey) {
557                 delete fppar->bibkey;
558                 fppar->bibkey = 0;
559         }
560         return endpar;
561 }
562
563
564 // set layout over selection and make a total rebreak of those paragraphs
565 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
566 {
567         LyXCursor tmpcursor = cursor;  /* store the current cursor  */
568
569         // if there is no selection just set the layout
570         // of the current paragraph  */
571         if (!selection.set()) {
572                 selection.start = cursor;  // dummy selection
573                 selection.end = cursor;
574         }
575         Paragraph * endpar = setLayout(bview, cursor, selection.start,
576                                        selection.end, layout);
577         redoParagraphs(bview, selection.start, endpar);
578    
579         // we have to reset the selection, because the
580         // geometry could have changed
581         setCursor(bview, selection.start.par(),
582                   selection.start.pos(), false);
583         selection.cursor = cursor;
584         setCursor(bview, selection.end.par(), selection.end.pos(), false);
585         updateCounters(bview, cursor.row());
586         clearSelection();
587         setSelection(bview);
588         setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
589 }
590
591
592 // increment depth over selection and
593 // make a total rebreak of those paragraphs
594 void  LyXText::incDepth(BufferView * bview)
595 {
596         // If there is no selection, just use the current paragraph
597         if (!selection.set()) {
598                 selection.start = cursor; // dummy selection
599                 selection.end = cursor;
600         }
601
602         // We end at the next paragraph with depth 0
603         Paragraph * endpar = selection.end.par()->next();
604
605         Paragraph * undoendpar = endpar;
606
607         if (endpar && endpar->getDepth()) {
608                 while (endpar && endpar->getDepth()) {
609                         endpar = endpar->next();
610                         undoendpar = endpar;
611                 }
612         } else if (endpar) {
613                 endpar = endpar->next(); // because of parindents etc.
614         }
615         
616         setUndo(bview, Undo::EDIT,
617                 selection.start.par(), undoendpar);
618
619         LyXCursor tmpcursor = cursor; // store the current cursor
620
621         // ok we have a selection. This is always between sel_start_cursor
622         // and sel_end cursor
623         cursor = selection.start;
624    
625         bool anything_changed = false;
626    
627         while (true) {
628                 // NOTE: you can't change the depth of a bibliography entry
629                 if (
630                         textclasslist.Style(bview->buffer()->params.textclass,
631                                             cursor.par()->getLayout()
632                                 ).labeltype != LABEL_BIBLIO) {
633                         Paragraph * prev = cursor.par()->previous();
634
635                         if (prev 
636                             && (prev->getDepth() - cursor.par()->getDepth() > 0
637                                 || (prev->getDepth() == cursor.par()->getDepth()
638                                     && textclasslist.Style(bview->buffer()->params.textclass,
639                                                            prev->getLayout()).isEnvironment()))) {
640                                 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
641                                 anything_changed = true;
642                         }
643                 }
644                 if (cursor.par() == selection.end.par())
645                         break;
646                 cursor.par(cursor.par()->next());
647         }
648    
649         // if nothing changed set all depth to 0
650         if (!anything_changed) {
651                 cursor = selection.start;
652                 while (cursor.par() != selection.end.par()) {
653                         cursor.par()->params().depth(0);
654                         cursor.par(cursor.par()->next());
655                 }
656                 cursor.par()->params().depth(0);
657         }
658    
659         redoParagraphs(bview, selection.start, endpar);
660    
661         // we have to reset the selection, because the
662         // geometry could have changed
663         setCursor(bview, selection.start.par(), selection.start.pos());
664         selection.cursor = cursor;
665         setCursor(bview, selection.end.par(), selection.end.pos());
666         updateCounters(bview, cursor.row());
667         clearSelection();
668         setSelection(bview);
669         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
670 }
671
672
673 // decrement depth over selection and
674 // make a total rebreak of those paragraphs
675 void  LyXText::decDepth(BufferView * bview)
676 {
677         // if there is no selection just set the layout
678         // of the current paragraph
679         if (!selection.set()) {
680                 selection.start = cursor; // dummy selection
681                 selection.end = cursor;
682         }
683         Paragraph * endpar = selection.end.par()->next();
684         Paragraph * undoendpar = endpar;
685
686         if (endpar && endpar->getDepth()) {
687                 while (endpar && endpar->getDepth()) {
688                         endpar = endpar->next();
689                         undoendpar = endpar;
690                 }
691         } else if (endpar) {
692                 endpar = endpar->next(); // because of parindents etc.
693         }
694    
695         setUndo(bview, Undo::EDIT,
696                 selection.start.par(), undoendpar);
697
698         LyXCursor tmpcursor = cursor; // store the current cursor
699
700         // ok we have a selection. This is always between sel_start_cursor
701         // and sel_end cursor
702         cursor = selection.start;
703
704         while (true) {
705                 if (cursor.par()->params().depth()) {
706                         cursor.par()->params()
707                                 .depth(cursor.par()->params().depth() - 1);
708                 }
709                 if (cursor.par() == selection.end.par()) {
710                         break;
711                 }
712                 cursor.par(cursor.par()->next());
713         }
714
715         redoParagraphs(bview, selection.start, endpar);
716    
717         // we have to reset the selection, because the
718         // geometry could have changed
719         setCursor(bview, selection.start.par(),
720                   selection.start.pos());
721         selection.cursor = cursor;
722         setCursor(bview, selection.end.par(), selection.end.pos());
723         updateCounters(bview, cursor.row());
724         clearSelection();
725         setSelection(bview);
726         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
727 }
728
729
730 // set font over selection and make a total rebreak of those paragraphs
731 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
732 {
733         // if there is no selection just set the current_font
734         if (!selection.set()) {
735                 // Determine basis font
736                 LyXFont layoutfont;
737                 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
738                                                        cursor.par())) {
739                         layoutfont = getLabelFont(bview->buffer(),
740                                                   cursor.par());
741                 } else {
742                         layoutfont = getLayoutFont(bview->buffer(),
743                                                    cursor.par());
744                 }
745                 // Update current font
746                 real_current_font.update(font, 
747                                          bview->buffer()->params.language,
748                                          toggleall);
749
750                 // Reduce to implicit settings
751                 current_font = real_current_font;
752                 current_font.reduce(layoutfont);
753                 // And resolve it completely
754 #ifndef INHERIT_LANGUAGE
755                 real_current_font.realize(layoutfont);
756 #else
757                 real_current_font.realize(layoutfont,
758                                           bview->buffer()->params.language);
759 #endif
760                 return;
761         }
762
763         LyXCursor tmpcursor = cursor; // store the current cursor
764    
765         // ok we have a selection. This is always between sel_start_cursor
766         // and sel_end cursor
767    
768         setUndo(bview, Undo::EDIT,
769                 selection.start.par(), selection.end.par()->next()); 
770         freezeUndo();
771         cursor = selection.start;
772         while (cursor.par() != selection.end.par() ||
773                (cursor.pos() < selection.end.pos()))
774         {
775                 if (cursor.pos() < cursor.par()->size()) {
776                         // an open footnote should behave
777                         // like a closed one
778                         setCharFont(bview, cursor.par(), cursor.pos(),
779                                     font, toggleall);
780                         cursor.pos(cursor.pos() + 1);
781                 } else {
782                         cursor.pos(0);
783                         cursor.par(cursor.par()->next());
784                 }
785         }
786         unFreezeUndo();
787    
788         redoParagraphs(bview, selection.start, selection.end.par()->next());
789    
790         // we have to reset the selection, because the
791         // geometry could have changed
792         setCursor(bview, selection.start.par(), selection.start.pos());
793         selection.cursor = cursor;
794         setCursor(bview, selection.end.par(), selection.end.pos());
795         clearSelection();
796         setSelection(bview);
797         setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
798                   tmpcursor.boundary());
799 }
800
801
802 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
803 {
804         Row * tmprow = cur.row();
805         int y = cur.y() - tmprow->baseline();
806
807         setHeightOfRow(bview, tmprow);
808         
809         while (tmprow->previous()
810                && tmprow->previous()->par() == tmprow->par()) {
811                 tmprow = tmprow->previous();
812                 y -= tmprow->height();
813                 setHeightOfRow(bview, tmprow);
814         }
815         
816         // we can set the refreshing parameters now
817         status(bview, LyXText::NEED_MORE_REFRESH);
818         refresh_y = y;
819         refresh_row = tmprow;
820         setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
821 }
822
823
824 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
825 {
826         Row * tmprow = cur.row();
827    
828         int y = cur.y() - tmprow->baseline();
829         setHeightOfRow(bview, tmprow);
830
831         while (tmprow->previous()
832                && tmprow->previous()->par() == tmprow->par())  {
833                 tmprow = tmprow->previous();
834                 y -= tmprow->height();
835         }
836
837         // we can set the refreshing parameters now
838         if (status_ == LyXText::UNCHANGED || y < refresh_y) {
839                 refresh_y = y;
840                 refresh_row = tmprow;
841         }
842         status(bview, LyXText::NEED_MORE_REFRESH);
843         setCursor(bview, cur.par(), cur.pos());
844 }
845
846
847 // deletes and inserts again all paragaphs between the cursor
848 // and the specified par 
849 // This function is needed after SetLayout and SetFont etc.
850 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
851                              Paragraph const * endpar) const
852 {
853         Row * tmprow2;
854         Paragraph * tmppar = 0;
855         Paragraph * first_phys_par = 0;
856    
857         Row * tmprow = cur.row();
858    
859         int y = cur.y() - tmprow->baseline();
860
861         if (!tmprow->previous()) {
862                 // a trick/hack for UNDO
863                 // Can somebody please tell me _why_ this solves
864                 // anything. (Lgb)
865                 first_phys_par = firstParagraph();
866         } else {
867                 first_phys_par = tmprow->par();
868                 while (tmprow->previous()
869                        && tmprow->previous()->par() == first_phys_par)
870                 {
871                         tmprow = tmprow->previous();
872                         y -= tmprow->height();
873                 }
874         }
875         
876         // we can set the refreshing parameters now
877         status(bview, LyXText::NEED_MORE_REFRESH);
878         refresh_y = y;
879         refresh_row = tmprow->previous();        /* the real refresh row will
880                                                 be deleted, so I store
881                                                 the previous here */ 
882         // remove it
883         if (tmprow->next())
884                 tmppar = tmprow->next()->par();
885         else
886                 tmppar = 0;
887         while (tmppar != endpar) {
888                 removeRow(tmprow->next());
889                 if (tmprow->next())
890                         tmppar = tmprow->next()->par();
891                 else
892                         tmppar = 0;
893         }  
894    
895         // remove the first one
896         tmprow2 = tmprow;     /* this is because tmprow->previous()
897                                  can be 0 */
898         tmprow = tmprow->previous();
899         removeRow(tmprow2);
900    
901         tmppar = first_phys_par;
902
903         do {
904                 if (tmppar) {
905                         insertParagraph(bview, tmppar, tmprow);
906                         if (!tmprow) {
907                                 tmprow = firstrow;
908                         }
909                         while (tmprow->next()
910                                && tmprow->next()->par() == tmppar) {
911                                 tmprow = tmprow->next();
912                         }
913                         tmppar = tmppar->next();
914                 }
915         } while (tmppar && tmppar != endpar);
916    
917         // this is because of layout changes
918         if (refresh_row) {
919                 refresh_y -= refresh_row->height();
920                 setHeightOfRow(bview, refresh_row);   
921         } else {
922                 refresh_row = firstrow;
923                 refresh_y = 0;
924                 setHeightOfRow(bview, refresh_row);   
925         }
926    
927         if (tmprow && tmprow->next())
928                 setHeightOfRow(bview, tmprow->next());
929 }
930
931
932 bool LyXText::fullRebreak(BufferView * bview)
933 {
934         if (!firstrow) {
935                 init(bview);
936                 return true;
937         }
938         if (need_break_row) {
939                 breakAgain(bview, need_break_row);
940                 need_break_row = 0;
941                 return true;
942         }
943         return false;
944 }
945
946
947 // important for the screen
948
949
950 /* the cursor set functions have a special mechanism. When they
951  * realize, that you left an empty paragraph, they will delete it.
952  * They also delete the corresponding row */
953    
954 // need the selection cursor:
955 void LyXText::setSelection(BufferView * bview)
956 {
957         bool const lsel = selection.set();
958
959         if (!selection.set()) {
960                 last_sel_cursor = selection.cursor;
961                 selection.start = selection.cursor;
962                 selection.end = selection.cursor;
963         }
964    
965         selection.set(true);
966    
967         // first the toggling area
968         if (cursor.y() < last_sel_cursor.y()
969             || (cursor.y() == last_sel_cursor.y()
970                 && cursor.x() < last_sel_cursor.x())) {
971                 toggle_end_cursor = last_sel_cursor;
972                 toggle_cursor = cursor;
973         } else {
974                 toggle_end_cursor = cursor;
975                 toggle_cursor = last_sel_cursor;
976         }
977    
978         last_sel_cursor = cursor;
979    
980         // and now the whole selection
981
982         if (selection.cursor.par() == cursor.par())
983                 if (selection.cursor.pos() < cursor.pos()) {
984                         selection.end = cursor;
985                         selection.start = selection.cursor;
986                 } else {
987                         selection.end = selection.cursor; 
988                         selection.start = cursor;
989                 }
990         else if (selection.cursor.y() < cursor.y() ||
991                  (selection.cursor.y() == cursor.y()
992                   && selection.cursor.x() < cursor.x())) {
993                 selection.end = cursor;
994                 selection.start = selection.cursor;
995         }
996         else {
997                 selection.end = selection.cursor; 
998                 selection.start = cursor;
999         }
1000    
1001         // a selection with no contents is not a selection
1002         if (selection.start.par() == selection.end.par() && 
1003             selection.start.pos() == selection.end.pos())
1004                 selection.set(false);
1005
1006         if (inset_owner && (selection.set() || lsel))
1007                 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1008 }
1009
1010
1011 string const LyXText::selectionAsString(Buffer const * buffer,
1012                                         bool label) const
1013 {
1014         if (!selection.set()) return string();
1015         string result;
1016         
1017         // Special handling if the whole selection is within one paragraph
1018         if (selection.start.par() == selection.end.par()) {
1019                 result += selection.start.par()->asString(buffer,
1020                                                           selection.start.pos(),
1021                                                           selection.end.pos(),
1022                                                           label);
1023                 return result;
1024         }
1025         
1026         // The selection spans more than one paragraph
1027
1028         // First paragraph in selection
1029         result += selection.start.par()->asString(buffer,
1030                                                   selection.start.pos(),
1031                                                   selection.start.par()->size(),
1032                                                   label)
1033                 + "\n\n";
1034         
1035         // The paragraphs in between (if any)
1036         LyXCursor tmpcur(selection.start);
1037         tmpcur.par(tmpcur.par()->next());
1038         while (tmpcur.par() != selection.end.par()) {
1039                 result += tmpcur.par()->asString(buffer, 0,
1040                                                  tmpcur.par()->size(),
1041                                                  label) + "\n\n";
1042                 tmpcur.par(tmpcur.par()->next());
1043         }
1044
1045         // Last paragraph in selection
1046         result += selection.end.par()->asString(buffer, 0,
1047                                                 selection.end.pos(), label);
1048         
1049         return result;
1050 }
1051
1052
1053 void LyXText::clearSelection() const
1054 {
1055         selection.set(false);
1056         selection.mark(false);
1057         selection.end = selection.start = selection.cursor = cursor;
1058 }
1059
1060
1061 void LyXText::cursorHome(BufferView * bview) const
1062 {
1063         setCursor(bview, cursor.par(), cursor.row()->pos());
1064 }
1065
1066
1067 void LyXText::cursorEnd(BufferView * bview) const
1068 {
1069         if (!cursor.row()->next()
1070             || cursor.row()->next()->par() != cursor.row()->par()) {
1071                 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1072         } else {
1073                 if (cursor.par()->size() &&
1074                     (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1075                      || cursor.par()->isNewline(rowLast(cursor.row())))) {
1076                         setCursor(bview, cursor.par(), rowLast(cursor.row()));
1077                 } else {
1078                         setCursor(bview,cursor.par(),
1079                                   rowLast(cursor.row()) + 1);
1080                 }
1081         }
1082 }
1083
1084
1085 void LyXText::cursorTop(BufferView * bview) const
1086 {
1087         while (cursor.par()->previous())
1088                 cursor.par(cursor.par()->previous());
1089         setCursor(bview, cursor.par(), 0);
1090 }
1091
1092
1093 void LyXText::cursorBottom(BufferView * bview) const
1094 {
1095         while (cursor.par()->next())
1096                 cursor.par(cursor.par()->next());
1097         setCursor(bview, cursor.par(), cursor.par()->size());
1098 }
1099    
1100    
1101 void LyXText::toggleFree(BufferView * bview,
1102                          LyXFont const & font, bool toggleall)
1103 {
1104         // If the mask is completely neutral, tell user
1105         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1106                 // Could only happen with user style
1107                 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1108                 return;
1109         }
1110
1111         // Try implicit word selection
1112         // If there is a change in the language the implicit word selection 
1113         // is disabled.
1114         LyXCursor resetCursor = cursor;
1115         bool implicitSelection = (font.language() == ignore_language
1116                                   && font.number() == LyXFont::IGNORE)
1117                 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1118
1119         // Set font
1120         setFont(bview, font, toggleall);
1121
1122         // Implicit selections are cleared afterwards
1123         //and cursor is set to the original position.
1124         if (implicitSelection) {
1125                 clearSelection();
1126                 cursor = resetCursor;
1127                 setCursor(bview, cursor.par(), cursor.pos());
1128                 selection.cursor = cursor;
1129         }
1130         if (inset_owner)
1131                 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1132 }
1133
1134
1135 string LyXText::getStringToIndex(BufferView * bview)
1136 {
1137         string idxstring;
1138         
1139         // Try implicit word selection
1140         // If there is a change in the language the implicit word selection 
1141         // is disabled.
1142         LyXCursor resetCursor = cursor;
1143         bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1144
1145         if (!selection.set()) {
1146                 bview->owner()->message(_("Nothing to index!"));
1147                 return string();
1148         }
1149         if (selection.start.par() != selection.end.par()) {
1150                 bview->owner()->message(_("Cannot index more than one paragraph!"));
1151                 return string();
1152         }
1153
1154         idxstring = selectionAsString(bview->buffer(), false);
1155         
1156         // Implicit selections are cleared afterwards
1157         //and cursor is set to the original position.
1158         if (implicitSelection) {
1159                 clearSelection();
1160                 cursor = resetCursor;
1161                 setCursor(bview, cursor.par(), cursor.pos());
1162                 selection.cursor = cursor;
1163         }
1164         return idxstring;
1165 }
1166
1167
1168 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1169                              Paragraph const * par) const
1170 {
1171         if (textclasslist.Style(buf->params.textclass,
1172                                 par->getLayout()).labeltype != LABEL_MANUAL)
1173                 return 0;
1174         else
1175                 return par->beginningOfMainBody();
1176 }
1177
1178
1179 /* the DTP switches for paragraphs. LyX will store them in the 
1180 * first physicla paragraph. When a paragraph is broken, the top settings 
1181 * rest, the bottom settings are given to the new one. So I can make shure, 
1182 * they do not duplicate themself and you cannnot make dirty things with 
1183 * them!  */ 
1184
1185 void LyXText::setParagraph(BufferView * bview,
1186                            bool line_top, bool line_bottom,
1187                            bool pagebreak_top, bool pagebreak_bottom,
1188                            VSpace const & space_top,
1189                            VSpace const & space_bottom,
1190                            Spacing const & spacing,
1191                            LyXAlignment align, 
1192                            string labelwidthstring,
1193                            bool noindent) 
1194 {
1195         LyXCursor tmpcursor = cursor;
1196         if (!selection.set()) {
1197                 selection.start = cursor;
1198                 selection.end = cursor;
1199         }
1200
1201         // make sure that the depth behind the selection are restored, too
1202         Paragraph * endpar = selection.end.par()->next();
1203         Paragraph * undoendpar = endpar;
1204
1205         if (endpar && endpar->getDepth()) {
1206                 while (endpar && endpar->getDepth()) {
1207                         endpar = endpar->next();
1208                         undoendpar = endpar;
1209                 }
1210         }
1211         else if (endpar) {
1212                 // because of parindents etc.
1213                 endpar = endpar->next();
1214         }
1215    
1216         setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1217
1218         
1219         Paragraph * tmppar = selection.end.par();
1220         while (tmppar != selection.start.par()->previous()) {
1221                 setCursor(bview, tmppar, 0);
1222                 status(bview, LyXText::NEED_MORE_REFRESH);
1223                 refresh_row = cursor.row();
1224                 refresh_y = cursor.y() - cursor.row()->baseline();
1225                 cursor.par()->params().lineTop(line_top);
1226                 cursor.par()->params().lineBottom(line_bottom);
1227                 cursor.par()->params().pagebreakTop(pagebreak_top);
1228                 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1229                 cursor.par()->params().spaceTop(space_top);
1230                 cursor.par()->params().spaceBottom(space_bottom);
1231                 cursor.par()->params().spacing(spacing);
1232                 // does the layout allow the new alignment?
1233                 if (align == LYX_ALIGN_LAYOUT)
1234                         align = textclasslist
1235                                 .Style(bview->buffer()->params.textclass,
1236                                        cursor.par()->getLayout()).align;
1237                 if (align & textclasslist
1238                     .Style(bview->buffer()->params.textclass,
1239                            cursor.par()->getLayout()).alignpossible) {
1240                         if (align == textclasslist
1241                             .Style(bview->buffer()->params.textclass,
1242                                    cursor.par()->getLayout()).align)
1243                                 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1244                         else
1245                                 cursor.par()->params().align(align);
1246                 }
1247                 cursor.par()->setLabelWidthString(labelwidthstring);
1248                 cursor.par()->params().noindent(noindent);
1249                 tmppar = cursor.par()->previous();
1250         }
1251         
1252         redoParagraphs(bview, selection.start, endpar);
1253         
1254         clearSelection();
1255         setCursor(bview, selection.start.par(), selection.start.pos());
1256         selection.cursor = cursor;
1257         setCursor(bview, selection.end.par(), selection.end.pos());
1258         setSelection(bview);
1259         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1260         if (inset_owner)
1261                 bview->updateInset(inset_owner, true);
1262 }
1263
1264
1265 char loweralphaCounter(int n)
1266 {
1267         if (n < 1 || n > 26)
1268                 return '?';
1269         else
1270                 return 'a' + n - 1;
1271 }
1272
1273
1274 namespace {
1275
1276 inline
1277 char alphaCounter(int n)
1278 {
1279         if (n < 1 || n > 26)
1280                 return '?';
1281         else
1282                 return 'A' + n - 1;
1283 }
1284
1285
1286 inline
1287 char hebrewCounter(int n)
1288 {
1289         static const char hebrew[22] = {
1290                 'à', 'á', 'â', 'ã', 'ä', 'Ã¥', 'æ', 'ç', 'è',
1291                 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö', 
1292                 '÷', 'ø', 'ù', 'ú'
1293         };
1294         if (n < 1 || n > 22)
1295                 return '?';
1296         else
1297                 return hebrew[n-1];
1298 }
1299
1300
1301 inline
1302 string const romanCounter(int n)
1303 {
1304         static char const * roman[20] = {
1305                 "i",   "ii",  "iii", "iv", "v",
1306                 "vi",  "vii", "viii", "ix", "x",
1307                 "xi",  "xii", "xiii", "xiv", "xv",
1308                 "xvi", "xvii", "xviii", "xix", "xx"
1309         };
1310         if (n < 1 || n > 20)
1311                 return "??";
1312         else
1313                 return roman[n-1];
1314 }
1315
1316 } // namespace anon
1317
1318
1319 // set the counter of a paragraph. This includes the labels
1320 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1321 {
1322         LyXLayout const & layout =
1323                 textclasslist.Style(buf->params.textclass, 
1324                                     par->getLayout());
1325
1326         LyXTextClass const & textclass =
1327                 textclasslist.TextClass(buf->params.textclass);
1328
1329         // copy the prev-counters to this one,
1330         // unless this is the first paragraph
1331         if (par->previous()) {
1332                 for (int i = 0; i < 10; ++i) {
1333                         par->setCounter(i, par->previous()->getFirstCounter(i));
1334                 }
1335                 par->params().appendix(par->previous()->params().appendix());
1336                 if (!par->params().appendix() && par->params().startOfAppendix()) {
1337                         par->params().appendix(true);
1338                         for (int i = 0; i < 10; ++i) {
1339                                 par->setCounter(i, 0);
1340                         }  
1341                 }
1342                 par->enumdepth = par->previous()->enumdepth;
1343                 par->itemdepth = par->previous()->itemdepth;
1344         } else {
1345                 for (int i = 0; i < 10; ++i) {
1346                         par->setCounter(i, 0);
1347                 }  
1348                 par->params().appendix(par->params().startOfAppendix());
1349                 par->enumdepth = 0;
1350                 par->itemdepth = 0;
1351         }
1352
1353         /* Maybe we have to increment the enumeration depth.
1354          * BUT, enumeration in a footnote is considered in isolation from its
1355          *      surrounding paragraph so don't increment if this is the
1356          *      first line of the footnote
1357          * AND, bibliographies can't have their depth changed ie. they
1358          *      are always of depth 0
1359          */
1360         if (par->previous()
1361             && par->previous()->getDepth() < par->getDepth()
1362             && textclasslist.Style(buf->params.textclass,
1363                                    par->previous()->getLayout()
1364                     ).labeltype == LABEL_COUNTER_ENUMI
1365             && par->enumdepth < 3
1366             && layout.labeltype != LABEL_BIBLIO) {
1367                 par->enumdepth++;
1368         }
1369
1370         // Maybe we have to decrement the enumeration depth, see note above
1371         if (par->previous()
1372             && par->previous()->getDepth() > par->getDepth()
1373             && layout.labeltype != LABEL_BIBLIO) {
1374                 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1375                 par->setCounter(6 + par->enumdepth,
1376                                 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1377                 /* reset the counters.
1378                  * A depth change is like a breaking layout
1379                  */
1380                 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1381                         par->setCounter(i, 0);
1382         }
1383    
1384         if (!par->params().labelString().empty()) {
1385                 par->params().labelString(string());
1386         }
1387    
1388         if (layout.margintype == MARGIN_MANUAL) {
1389                 if (par->params().labelWidthString().empty()) {
1390                         par->setLabelWidthString(layout.labelstring());
1391                 }
1392         } else {
1393                 par->setLabelWidthString(string());
1394         }
1395    
1396         // is it a layout that has an automatic label?
1397         if (layout.labeltype >=  LABEL_COUNTER_CHAPTER) {
1398       
1399                 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1400                 if (i >= 0 && i<= buf->params.secnumdepth) {
1401                         par->incCounter(i);     // increment the counter  
1402          
1403                         // Is there a label? Useful for Chapter layout
1404                         if (!par->params().appendix()) {
1405                                 if (!layout.labelstring().empty())
1406                                         par->params().labelString(layout.labelstring());
1407                                 else
1408                                         par->params().labelString(string());
1409                         } else {
1410                                 if (!layout.labelstring_appendix().empty())
1411                                         par->params().labelString(layout.labelstring_appendix());
1412                                 else
1413                                         par->params().labelString(string());
1414                         }
1415
1416                         ostringstream s;
1417
1418                         if (!par->params().appendix()) {
1419                                 switch (2 * LABEL_COUNTER_CHAPTER -
1420                                         textclass.maxcounter() + i) {
1421                                 case LABEL_COUNTER_CHAPTER:
1422                                         s << par->getCounter(i);
1423                                         break;
1424                                 case LABEL_COUNTER_SECTION:
1425                                         s << par->getCounter(i - 1) << '.'
1426                                           << par->getCounter(i);
1427                                         break;
1428                                 case LABEL_COUNTER_SUBSECTION:
1429                                         s << par->getCounter(i - 2) << '.'
1430                                           << par->getCounter(i - 1) << '.'
1431                                           << par->getCounter(i);
1432                                         break;
1433                                 case LABEL_COUNTER_SUBSUBSECTION:
1434                                         s << par->getCounter(i - 3) << '.'
1435                                           << par->getCounter(i - 2) << '.'
1436                                           << par->getCounter(i - 1) << '.'
1437                                           << par->getCounter(i);
1438                                         
1439                                         break;
1440                                 case LABEL_COUNTER_PARAGRAPH:
1441                                         s << par->getCounter(i - 4) << '.'
1442                                           << par->getCounter(i - 3) << '.'
1443                                           << par->getCounter(i - 2) << '.'
1444                                           << par->getCounter(i - 1) << '.'
1445                                           << par->getCounter(i);
1446                                         break;
1447                                 case LABEL_COUNTER_SUBPARAGRAPH:
1448                                         s << par->getCounter(i - 5) << '.'
1449                                           << par->getCounter(i - 4) << '.'
1450                                           << par->getCounter(i - 3) << '.'
1451                                           << par->getCounter(i - 2) << '.'
1452                                           << par->getCounter(i - 1) << '.'
1453                                           << par->getCounter(i);
1454
1455                                         break;
1456                                 default:
1457                                         // Can this ever be reached? And in the
1458                                         // case it is, how can this be correct?
1459                                         // (Lgb)
1460                                         s << par->getCounter(i) << '.';
1461                                         break;
1462                                 }
1463                         } else { // appendix
1464                                 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1465                                 case LABEL_COUNTER_CHAPTER:
1466                                         if (par->isRightToLeftPar(buf->params))
1467                                                 s << hebrewCounter(par->getCounter(i));
1468                                         else
1469                                                 s << alphaCounter(par->getCounter(i));
1470                                         break;
1471                                 case LABEL_COUNTER_SECTION:
1472                                         if (par->isRightToLeftPar(buf->params))
1473                                                 s << hebrewCounter(par->getCounter(i - 1));
1474                                         else
1475                                                 s << alphaCounter(par->getCounter(i - 1));
1476
1477                                         s << '.'
1478                                           << par->getCounter(i);
1479
1480                                         break;
1481                                 case LABEL_COUNTER_SUBSECTION:
1482                                         if (par->isRightToLeftPar(buf->params))
1483                                                 s << hebrewCounter(par->getCounter(i - 2));
1484                                         else
1485                                                 s << alphaCounter(par->getCounter(i - 2));
1486
1487                                         s << '.'
1488                                           << par->getCounter(i-1) << '.'
1489                                           << par->getCounter(i);
1490
1491                                         break;
1492                                 case LABEL_COUNTER_SUBSUBSECTION:
1493                                         if (par->isRightToLeftPar(buf->params))
1494                                                 s << hebrewCounter(par->getCounter(i-3));
1495                                         else
1496                                                 s << alphaCounter(par->getCounter(i-3));
1497
1498                                         s << '.'
1499                                           << par->getCounter(i-2) << '.'
1500                                           << par->getCounter(i-1) << '.'
1501                                           << par->getCounter(i);
1502
1503                                         break;
1504                                 case LABEL_COUNTER_PARAGRAPH:
1505                                         if (par->isRightToLeftPar(buf->params))
1506                                                 s << hebrewCounter(par->getCounter(i-4));
1507                                         else
1508                                                 s << alphaCounter(par->getCounter(i-4));
1509
1510                                         s << '.'
1511                                           << par->getCounter(i-3) << '.'
1512                                           << par->getCounter(i-2) << '.'
1513                                           << par->getCounter(i-1) << '.'
1514                                           << par->getCounter(i);
1515
1516                                         break;
1517                                 case LABEL_COUNTER_SUBPARAGRAPH:
1518                                         if (par->isRightToLeftPar(buf->params))
1519                                                 s << hebrewCounter(par->getCounter(i-5));
1520                                         else
1521                                                 s << alphaCounter(par->getCounter(i-5));
1522
1523                                         s << '.'
1524                                           << par->getCounter(i-4) << '.'
1525                                           << par->getCounter(i-3) << '.'
1526                                           << par->getCounter(i-2) << '.'
1527                                           << par->getCounter(i-1) << '.'
1528                                           << par->getCounter(i);
1529
1530                                         break;
1531                                 default:
1532                                         // Can this ever be reached? And in the
1533                                         // case it is, how can this be correct?
1534                                         // (Lgb)
1535                                         s << par->getCounter(i) << '.';
1536                                         
1537                                         break;
1538                                 }
1539                         }
1540
1541                         par->params().labelString(par->params().labelString() +s.str().c_str());
1542                         // We really want to remove the c_str as soon as
1543                         // possible...
1544                         
1545                         for (i++; i < 10; ++i) {
1546                                 // reset the following counters
1547                                 par->setCounter(i, 0);
1548                         }
1549                 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1550                         for (i++; i < 10; ++i) {
1551                                 // reset the following counters
1552                                 par->setCounter(i, 0);
1553                         }
1554                 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1555                         par->incCounter(i + par->enumdepth);
1556                         int number = par->getCounter(i + par->enumdepth);
1557
1558                         ostringstream s;
1559
1560                         switch (par->enumdepth) {
1561                         case 1:
1562                                 if (par->isRightToLeftPar(buf->params))
1563                                         s << '('
1564                                           << hebrewCounter(number)
1565                                           << ')';
1566                                 else
1567                                         s << '('
1568                                           << loweralphaCounter(number)
1569                                           << ')';
1570                                 break;
1571                         case 2:
1572                                 if (par->isRightToLeftPar(buf->params))
1573                                         s << '.' << romanCounter(number);
1574                                 else
1575                                         s << romanCounter(number) << '.';
1576                                 break;
1577                         case 3:
1578                                 if (par->isRightToLeftPar(buf->params))
1579                                         s << '.'
1580                                           << alphaCounter(number);
1581                                 else
1582                                         s << alphaCounter(number)
1583                                           << '.';
1584                                 break;
1585                         default:
1586                                 if (par->isRightToLeftPar(buf->params))
1587                                         s << '.' << number;
1588                                 else
1589                                         s << number << '.';
1590                                 break;
1591                         }
1592
1593                         par->params().labelString(s.str().c_str());
1594
1595                         for (i += par->enumdepth + 1; i < 10; ++i) {
1596                                 // reset the following counters
1597                                 par->setCounter(i, 0);
1598                         }
1599                         
1600                 } 
1601         } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1602                 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1603                 par->incCounter(i);
1604                 int number = par->getCounter(i);
1605                 if (!par->bibkey) {
1606                         InsetCommandParams p( "bibitem" );
1607                         par->bibkey = new InsetBibKey(p);
1608                 }
1609                 par->bibkey->setCounter(number);
1610                 par->params().labelString(layout.labelstring());
1611                 
1612                 // In biblio should't be following counters but...
1613         } else {
1614                 string s = layout.labelstring();
1615                 
1616                 // the caption hack:
1617                 if (layout.labeltype == LABEL_SENSITIVE) {
1618                         bool isOK (par->inInset() && par->inInset()->owner() &&
1619                                    (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1620                         
1621                         if (isOK) {
1622                                 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1623                                 Floating const & fl
1624                                         = floatList.getType(tmp->type());
1625                                 // We should get the correct number here too.
1626                                 s = fl.name() + " #:";
1627                         } else {
1628                                 /* par->SetLayout(0); 
1629                                    s = layout->labelstring;  */
1630                                 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1631                                         ? " :úåòîùî Ã¸Ã±Ã§" : "Senseless: ";
1632                         }
1633                 }
1634                 par->params().labelString(s);
1635                 
1636                 /* reset the enumeration counter. They are always resetted
1637                  * when there is any other layout between */ 
1638                 for (int i = 6 + par->enumdepth; i < 10; ++i)
1639                         par->setCounter(i, 0);
1640         }
1641 }
1642
1643
1644 // Updates all counters BEHIND the row. Changed paragraphs
1645 // with a dynamic left margin will be rebroken.
1646 void LyXText::updateCounters(BufferView * bview, Row * row) const
1647 {
1648         Paragraph * par;
1649
1650         if (!row) {
1651                 row = firstrow;
1652                 par = row->par();
1653         } else {
1654                 par = row->par()->next();
1655         }
1656
1657         while (par) {
1658                 while (row->par() != par)
1659                         row = row->next();
1660                 
1661                 setCounter(bview->buffer(), par);
1662                 
1663                 // now check for the headline layouts. remember that they
1664                 // have a dynamic left margin
1665                 if ((textclasslist.Style(bview->buffer()->params.textclass,
1666                                          par->layout).margintype == MARGIN_DYNAMIC
1667                      || textclasslist.Style(bview->buffer()->params.textclass,
1668                                             par->layout).labeltype == LABEL_SENSITIVE)) {
1669                         
1670                         // Rebreak the paragraph
1671                         removeParagraph(row);
1672                         appendParagraph(bview, row);
1673                 }
1674                 par = par->next();
1675         }
1676 }
1677
1678
1679 void LyXText::insertInset(BufferView * bview, Inset * inset)
1680 {
1681         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1682                 return;
1683         setUndo(bview, Undo::INSERT,
1684                 cursor.par(), cursor.par()->next());
1685         cursor.par()->insertInset(cursor.pos(), inset);
1686         // Just to rebreak and refresh correctly.
1687         // The character will not be inserted a second time
1688         insertChar(bview, Paragraph::META_INSET);
1689 #if 1
1690         // If we enter a highly editable inset the cursor should be to before
1691         // the inset. This couldn't happen before as Undo was not handled inside
1692         // inset now after the Undo LyX tries to call inset->Edit(...) again
1693         // and cannot do this as the cursor is behind the inset and GetInset
1694         // does not return the inset!
1695         if (isHighlyEditableInset(inset)) {
1696                 cursorLeft(bview, true);
1697         }
1698 #endif
1699 }
1700
1701
1702 void LyXText::copyEnvironmentType()
1703 {
1704         copylayouttype = cursor.par()->getLayout();
1705 }
1706
1707
1708 void LyXText::pasteEnvironmentType(BufferView * bview)
1709 {
1710         setLayout(bview, copylayouttype);
1711 }
1712
1713
1714 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1715 {
1716         // Stuff what we got on the clipboard. Even if there is no selection.
1717
1718         // There is a problem with having the stuffing here in that the
1719         // larger the selection the slower LyX will get. This can be
1720         // solved by running the line below only when the selection has
1721         // finished. The solution used currently just works, to make it
1722         // faster we need to be more clever and probably also have more
1723         // calls to stuffClipboard. (Lgb)
1724         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1725
1726         // This doesn't make sense, if there is no selection
1727         if (!selection.set())
1728                 return;
1729    
1730         // OK, we have a selection. This is always between selection.start
1731         // and selection.end
1732
1733         // make sure that the depth behind the selection are restored, too
1734         Paragraph * endpar = selection.end.par()->next();
1735         Paragraph * undoendpar = endpar;
1736     
1737         if (endpar && endpar->getDepth()) {
1738                 while (endpar && endpar->getDepth()) {
1739                         endpar = endpar->next();
1740                         undoendpar = endpar;
1741                 }
1742         } else if (endpar) {
1743                 endpar = endpar->next(); // because of parindents etc.
1744         }
1745     
1746         setUndo(bview, Undo::DELETE,
1747                 selection.start.par(), undoendpar);
1748     
1749         // there are two cases: cut only within one paragraph or
1750         // more than one paragraph
1751         if (selection.start.par() == selection.end.par()) {
1752                 // only within one paragraph
1753                 endpar = selection.end.par();
1754                 int pos = selection.end.pos();
1755                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1756                                           selection.start.pos(), pos,
1757                                           bview->buffer()->params.textclass,
1758                                           doclear, realcut);
1759                 selection.end.pos(pos);
1760         } else {
1761                 endpar = selection.end.par();
1762                 int pos = selection.end.pos();
1763                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1764                                           selection.start.pos(), pos,
1765                                           bview->buffer()->params.textclass,
1766                                           doclear, realcut);
1767                 cursor.par(endpar);
1768                 selection.end.par(endpar);
1769                 selection.end.pos(pos);
1770                 cursor.pos(selection.end.pos());
1771         }
1772         endpar = endpar->next();
1773
1774         // sometimes necessary
1775         if (doclear)
1776                 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1777
1778         redoParagraphs(bview, selection.start, endpar);
1779
1780         // cutSelection can invalidate the cursor so we need to set
1781         // it anew. (Lgb)
1782         cursor = selection.start;
1783
1784         // need a valid cursor. (Lgb)
1785         clearSelection();
1786
1787         setCursor(bview, cursor.par(), cursor.pos());
1788         selection.cursor = cursor;
1789         updateCounters(bview, cursor.row());
1790 }
1791
1792
1793 void LyXText::copySelection(BufferView * bview)
1794 {
1795         // Stuff what we got on the clipboard. Even if there is no selection.
1796
1797         // There is a problem with having the stuffing here in that the
1798         // larger the selection the slower LyX will get. This can be
1799         // solved by running the line below only when the selection has
1800         // finished. The solution used currently just works, to make it
1801         // faster we need to be more clever and probably also have more
1802         // calls to stuffClipboard. (Lgb)
1803         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1804
1805         // this doesnt make sense, if there is no selection
1806         if (!selection.set())
1807                 return;
1808
1809         // ok we have a selection. This is always between selection.start
1810         // and sel_end cursor
1811
1812         // copy behind a space if there is one
1813         while (selection.start.par()->size() > selection.start.pos()
1814                && selection.start.par()->isLineSeparator(selection.start.pos())
1815                && (selection.start.par() != selection.end.par()
1816                    || selection.start.pos() < selection.end.pos()))
1817                 selection.start.pos(selection.start.pos() + 1); 
1818
1819         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1820                                    selection.start.pos(), selection.end.pos(),
1821                                    bview->buffer()->params.textclass);
1822 }
1823
1824
1825 void LyXText::pasteSelection(BufferView * bview)
1826 {
1827         // this does not make sense, if there is nothing to paste
1828         if (!CutAndPaste::checkPastePossible(cursor.par()))
1829                 return;
1830
1831         setUndo(bview, Undo::INSERT,
1832                 cursor.par(), cursor.par()->next()); 
1833
1834         Paragraph * endpar;
1835         Paragraph * actpar = cursor.par();
1836         int pos = cursor.pos();
1837
1838         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1839                                     bview->buffer()->params.textclass);
1840     
1841         redoParagraphs(bview, cursor, endpar);
1842         
1843         setCursor(bview, cursor.par(), cursor.pos());
1844         clearSelection();
1845    
1846         setCursor(bview, actpar, pos);
1847         updateCounters(bview, cursor.row());
1848 }
1849
1850
1851 // returns a pointer to the very first Paragraph
1852 Paragraph * LyXText::firstParagraph() const
1853 {
1854         return ownerParagraph();
1855 }
1856
1857
1858 // sets the selection over the number of characters of string, no check!!
1859 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1860 {
1861         if (str.empty())
1862                 return;
1863         
1864         selection.cursor = cursor;
1865         for (string::size_type i = 0; i < str.length(); ++i)
1866                 cursorRight(bview);
1867         setSelection(bview);
1868 }
1869
1870
1871 // simple replacing. The font of the first selected character is used
1872 void LyXText::replaceSelectionWithString(BufferView * bview,
1873                                          string const & str)
1874 {
1875         setCursorParUndo(bview);
1876         freezeUndo();
1877
1878         if (!selection.set()) { // create a dummy selection
1879                 selection.end = cursor;
1880                 selection.start = cursor;
1881         }
1882
1883         // Get font setting before we cut
1884         pos_type pos = selection.end.pos();
1885         LyXFont const font = selection.start.par()
1886                 ->getFontSettings(bview->buffer()->params,
1887                                   selection.start.pos());
1888
1889         // Insert the new string
1890         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1891                 selection.end.par()->insertChar(pos, (*cit), font);
1892                 ++pos;
1893         }
1894         
1895         // Cut the selection
1896         cutSelection(bview, true, false);
1897
1898         unFreezeUndo();
1899 }
1900
1901
1902 // needed to insert the selection
1903 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1904 {
1905         Paragraph * par = cursor.par();
1906         pos_type pos = cursor.pos();
1907         Paragraph * endpar = cursor.par()->next();
1908         
1909         setCursorParUndo(bview);
1910         
1911         // only to be sure, should not be neccessary
1912         clearSelection();
1913         
1914         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1915
1916         redoParagraphs(bview, cursor, endpar);
1917         setCursor(bview, cursor.par(), cursor.pos());
1918         selection.cursor = cursor;
1919         setCursor(bview, par, pos);
1920         setSelection(bview);
1921 }
1922
1923
1924 // turns double-CR to single CR, others where converted into one
1925 // blank. Then InsertStringAsLines is called
1926 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1927 {
1928         string linestr(str);
1929         bool newline_inserted = false;
1930         for (string::size_type i = 0; i < linestr.length(); ++i) {
1931                 if (linestr[i] == '\n') {
1932                         if (newline_inserted) {
1933                                 // we know that \r will be ignored by
1934                                 // InsertStringA. Of course, it is a dirty
1935                                 // trick, but it works...
1936                                 linestr[i - 1] = '\r';
1937                                 linestr[i] = '\n';
1938                         } else {
1939                                 linestr[i] = ' ';
1940                                 newline_inserted = true;
1941                         } 
1942                 } else if (IsPrintable(linestr[i])) {
1943                         newline_inserted = false;
1944                 }
1945         }
1946         insertStringAsLines(bview, linestr);
1947 }
1948
1949
1950 bool LyXText::gotoNextInset(BufferView * bview,
1951                             std::vector<Inset::Code> const & codes,
1952                             string const & contents) const
1953 {
1954         LyXCursor res = cursor;
1955         Inset * inset;
1956         do {
1957                 if (res.pos() < res.par()->size() - 1) {
1958                         res.pos(res.pos() + 1);
1959                 } else  {
1960                         res.par(res.par()->next());
1961                         res.pos(0);
1962                 }
1963       
1964         } while (res.par() && 
1965                  !(res.par()->isInset(res.pos())
1966                    && (inset = res.par()->getInset(res.pos())) != 0
1967                    && find(codes.begin(), codes.end(), inset->lyxCode())
1968                    != codes.end()
1969                    && (contents.empty() ||
1970                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1971                        == contents)));
1972
1973         if (res.par()) {
1974                 setCursor(bview, res.par(), res.pos(), false);
1975                 return true;
1976         }
1977         return false;
1978 }
1979
1980
1981 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1982                              pos_type pos)
1983 {
1984         LyXCursor tmpcursor;                    
1985
1986         int y = 0;
1987         pos_type z;
1988         Row * row = getRow(par, pos, y);
1989         
1990         // is there a break one row above
1991         if (row->previous() && row->previous()->par() == row->par()) {
1992                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1993                 if (z >= row->pos()) {
1994                         // set the dimensions of the row above
1995                         y -= row->previous()->height();
1996                         refresh_y = y;
1997                         refresh_row = row->previous();
1998                         status(bview, LyXText::NEED_MORE_REFRESH);
1999                         
2000                         breakAgain(bview, row->previous());
2001                         
2002                         // set the cursor again. Otherwise
2003                         // dangling pointers are possible
2004                         setCursor(bview, cursor.par(), cursor.pos(),
2005                                   false, cursor.boundary());
2006                         selection.cursor = cursor;
2007                         return;
2008                 }
2009         }
2010
2011         int const tmpheight = row->height();
2012         pos_type const tmplast = rowLast(row);
2013         refresh_y = y;
2014         refresh_row = row;
2015         
2016         breakAgain(bview, row);
2017         if (row->height() == tmpheight && rowLast(row) == tmplast)
2018                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2019         else
2020                 status(bview, LyXText::NEED_MORE_REFRESH); 
2021         
2022         // check the special right address boxes
2023         if (textclasslist.Style(bview->buffer()->params.textclass,
2024                                 par->getLayout()).margintype
2025             == MARGIN_RIGHT_ADDRESS_BOX)
2026         {
2027                 tmpcursor.par(par);
2028                 tmpcursor.row(row);
2029                 tmpcursor.y(y);
2030                 tmpcursor.x(0);
2031                 tmpcursor.x_fix(0);
2032                 tmpcursor.pos(pos);
2033                 redoDrawingOfParagraph(bview, tmpcursor); 
2034         }
2035
2036         // set the cursor again. Otherwise dangling pointers are possible
2037         // also set the selection
2038    
2039         if (selection.set()) {
2040                 tmpcursor = cursor;
2041                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2042                                 false, selection.cursor.boundary());
2043                 selection.cursor = cursor; 
2044                 setCursorIntern(bview, selection.start.par(),
2045                                 selection.start.pos(),
2046                                 false, selection.start.boundary());
2047                 selection.start = cursor; 
2048                 setCursorIntern(bview, selection.end.par(),
2049                                 selection.end.pos(),
2050                                 false, selection.end.boundary());
2051                 selection.end = cursor; 
2052                 setCursorIntern(bview, last_sel_cursor.par(),
2053                                 last_sel_cursor.pos(),
2054                                 false, last_sel_cursor.boundary());
2055                 last_sel_cursor = cursor; 
2056                 cursor = tmpcursor;
2057         }
2058         setCursorIntern(bview, cursor.par(), cursor.pos(),
2059                         false, cursor.boundary());
2060 }
2061
2062
2063 // returns false if inset wasn't found
2064 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2065 {
2066         // first check the current paragraph
2067         int pos = cursor.par()->getPositionOfInset(inset);
2068         if (pos != -1) {
2069                 checkParagraph(bview, cursor.par(), pos);
2070                 return true;
2071         }
2072   
2073         // check every paragraph
2074   
2075         Paragraph * par = firstParagraph();
2076         do {
2077                 pos = par->getPositionOfInset(inset);
2078                 if (pos != -1) {
2079                         checkParagraph(bview, par, pos);
2080                         return true;
2081                 }
2082                 par = par->next();
2083         } while (par);
2084   
2085         return false;
2086 }
2087
2088
2089 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2090                         pos_type pos, 
2091                         bool setfont, bool boundary) const
2092 {
2093         LyXCursor old_cursor = cursor;
2094         setCursorIntern(bview, par, pos, setfont, boundary);
2095         deleteEmptyParagraphMechanism(bview, old_cursor);
2096 }
2097
2098
2099 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2100                         pos_type pos, bool boundary) const
2101 {
2102         cur.par(par);
2103         cur.pos(pos);
2104         cur.boundary(boundary);
2105
2106         // get the cursor y position in text
2107         int y = 0;
2108         Row * row = getRow(par, pos, y);
2109         // y is now the beginning of the cursor row
2110         y += row->baseline();
2111         // y is now the cursor baseline 
2112         cur.y(y);
2113
2114         // now get the cursors x position
2115         float x;
2116         float fill_separator;
2117         float fill_hfill;
2118         float fill_label_hfill;
2119         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2120                        fill_label_hfill);
2121         pos_type cursor_vpos = 0;
2122         pos_type last = rowLastPrintable(row);
2123
2124         if (pos > last + 1)   // This shouldn't happen.
2125                 pos = last + 1;
2126         else if (pos < row->pos())
2127                 pos = row->pos();
2128
2129         if (last < row->pos())
2130                 cursor_vpos = row->pos();
2131         else if (pos > last && !boundary)
2132                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2133                         ? row->pos() : last + 1; 
2134         else if (pos > row->pos() &&
2135                  (pos > last || boundary))
2136                 /// Place cursor after char at (logical) position pos - 1
2137                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2138                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2139         else
2140                 /// Place cursor before char at (logical) position pos
2141                 cursor_vpos = (bidi_level(pos) % 2 == 0)
2142                         ? log2vis(pos) : log2vis(pos) + 1;
2143         
2144         pos_type main_body =
2145                 beginningOfMainBody(bview->buffer(), row->par());
2146         if ((main_body > 0) &&
2147             ((main_body-1 > last) || 
2148              !row->par()->isLineSeparator(main_body-1)))
2149                 main_body = 0;
2150         
2151         for (pos_type vpos = row->pos();
2152              vpos < cursor_vpos; ++vpos) {
2153                 pos = vis2log(vpos);
2154                 if (main_body > 0 && pos == main_body - 1) {
2155                         x += fill_label_hfill +
2156                                 lyxfont::width(textclasslist.Style(
2157                                         bview->buffer()->params.textclass,
2158                                         row->par()->getLayout())
2159                                                .labelsep,
2160                                                getLabelFont(bview->buffer(), row->par()));
2161                         if (row->par()->isLineSeparator(main_body-1))
2162                                 x -= singleWidth(bview, row->par(),main_body-1);
2163                 }
2164                 if (hfillExpansion(bview->buffer(), row, pos)) {
2165                         x += singleWidth(bview, row->par(), pos);
2166                         if (pos >= main_body)
2167                                 x += fill_hfill;
2168                         else 
2169                                 x += fill_label_hfill;
2170                 } else if (row->par()->isSeparator(pos)) {
2171                         x += singleWidth(bview, row->par(), pos);
2172                         if (pos >= main_body)
2173                                 x += fill_separator;
2174                 } else
2175                         x += singleWidth(bview, row->par(), pos);
2176         }
2177         
2178         cur.x(int(x));
2179         cur.x_fix(cur.x());
2180         cur.row(row);
2181 }
2182
2183
2184 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2185                               pos_type pos,
2186                               bool setfont, bool boundary) const
2187 {
2188         InsetText * it = static_cast<InsetText *>(par->inInset());
2189         if (it) {
2190                 if (it != inset_owner) {
2191                         lyxerr << "InsetText   is " << it << endl;
2192                         lyxerr << "inset_owner is " << inset_owner << endl;
2193 #warning I believe this code is wrong. (Lgb)
2194 #warning Jürgen, have a look at this. (Lgb)
2195 #warning Hmmm, I guess you are right but we
2196 #warning should verify when this is needed
2197                         // Jürgen, would you like to have a look?
2198                         // I guess we need to move the outer cursor
2199                         // and open and lock the inset (bla bla bla)
2200                         // stuff I don't know... so can you have a look?
2201                         // (Lgb)
2202                         // I moved the lyxerr stuff in here so we can see if
2203                         // this is actually really needed and where!
2204                         // (Jug)
2205                         it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2206                                                                boundary);
2207                         return;
2208                 }
2209         }
2210         
2211         setCursor(bview, cursor, par, pos, boundary);
2212         if (setfont)
2213                 setCurrentFont(bview);
2214 }
2215
2216
2217 void LyXText::setCurrentFont(BufferView * bview) const
2218 {
2219         pos_type pos = cursor.pos();
2220         if (cursor.boundary() && pos > 0)
2221                 --pos;
2222
2223         if (pos > 0) {
2224                 if (pos == cursor.par()->size())
2225                         --pos;
2226                 else // potentional bug... BUG (Lgb)
2227                         if (cursor.par()->isSeparator(pos)) {
2228                                 if (pos > cursor.row()->pos() &&
2229                                     bidi_level(pos) % 2 == 
2230                                     bidi_level(pos - 1) % 2)
2231                                         --pos;
2232                                 else if (pos + 1 < cursor.par()->size())
2233                                         ++pos;
2234                         }
2235         }
2236
2237         current_font =
2238                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2239         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2240
2241         if (cursor.pos() == cursor.par()->size() &&
2242             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2243             !cursor.boundary()) {
2244                 Language const * lang =
2245                         cursor.par()->getParLanguage(bview->buffer()->params);
2246                 current_font.setLanguage(lang);
2247                 current_font.setNumber(LyXFont::OFF);
2248                 real_current_font.setLanguage(lang);
2249                 real_current_font.setNumber(LyXFont::OFF);
2250         }
2251 }
2252
2253
2254 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2255 {
2256         LyXCursor old_cursor = cursor;
2257
2258         setCursorFromCoordinates(bview, cursor, x, y);
2259         setCurrentFont(bview);
2260         deleteEmptyParagraphMechanism(bview, old_cursor);
2261 }
2262
2263
2264 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2265                                        int x, int y) const
2266 {
2267         // Get the row first.
2268    
2269         Row * row = getRowNearY(y);
2270         bool bound = false;
2271         pos_type const column = getColumnNearX(bview, row, x, bound);
2272         cur.par(row->par());
2273         cur.pos(row->pos() + column);
2274         cur.x(x);
2275         cur.y(y + row->baseline());
2276         cur.row(row);
2277         cur.boundary(bound);
2278 }
2279
2280
2281 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2282 {
2283         if (cursor.pos() > 0) {
2284                 bool boundary = cursor.boundary();
2285                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2286                 if (!internal && !boundary &&
2287                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2288                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2289         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2290                 Paragraph * par = cursor.par()->previous();
2291                 setCursor(bview, par, par->size());
2292         }
2293 }
2294
2295
2296 void LyXText::cursorRight(BufferView * bview, bool internal) const
2297 {
2298         if (!internal && cursor.boundary() &&
2299             !cursor.par()->isNewline(cursor.pos()))
2300                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2301         else if (cursor.pos() < cursor.par()->size()) {
2302                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2303                 if (!internal &&
2304                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2305                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2306         } else if (cursor.par()->next())
2307                 setCursor(bview, cursor.par()->next(), 0);
2308 }
2309
2310
2311 void LyXText::cursorUp(BufferView * bview) const
2312 {
2313         setCursorFromCoordinates(bview, cursor.x_fix(), 
2314                                  cursor.y() - cursor.row()->baseline() - 1);
2315 }
2316
2317
2318 void LyXText::cursorDown(BufferView * bview) const
2319 {
2320         setCursorFromCoordinates(bview, cursor.x_fix(), 
2321                                  cursor.y() - cursor.row()->baseline()
2322                                  + cursor.row()->height() + 1);
2323 }
2324
2325
2326 void LyXText::cursorUpParagraph(BufferView * bview) const
2327 {
2328         if (cursor.pos() > 0) {
2329                 setCursor(bview, cursor.par(), 0);
2330         }
2331         else if (cursor.par()->previous()) {
2332                 setCursor(bview, cursor.par()->previous(), 0);
2333         }
2334 }
2335
2336
2337 void LyXText::cursorDownParagraph(BufferView * bview) const
2338 {
2339         if (cursor.par()->next()) {
2340                 setCursor(bview, cursor.par()->next(), 0);
2341         } else {
2342                 setCursor(bview, cursor.par(), cursor.par()->size());
2343         }
2344 }
2345
2346 // fix the cursor `cur' after a characters has been deleted at `where'
2347 // position. Called by deleteEmptyParagraphMechanism
2348 void LyXText::fixCursorAfterDelete(BufferView * bview,
2349                                    LyXCursor & cur,
2350                                    LyXCursor const & where) const
2351 {
2352         // if cursor is not in the paragraph where the delete occured,
2353         // do nothing
2354         if (cur.par() != where.par())
2355                 return;
2356
2357         // if cursor position is after the place where the delete occured,
2358         // update it
2359         if (cur.pos() > where.pos())
2360                 cur.pos(cur.pos()-1);
2361
2362         // recompute row et al. for this cursor
2363         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2364 }
2365
2366
2367 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2368                                             LyXCursor const & old_cursor) const
2369 {
2370         // don't delete anything if this is the ONLY paragraph!
2371         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2372                 return;
2373         
2374         // Would be wrong to delete anything if we have a selection.
2375         if (selection.set()) return;
2376
2377         // We allow all kinds of "mumbo-jumbo" when freespacing.
2378         if (textclasslist.Style(bview->buffer()->params.textclass,
2379                                                         old_cursor.par()->getLayout()).free_spacing ||
2380                 old_cursor.par()->isFreeSpacing())
2381         {
2382                 return;
2383         }
2384
2385         bool deleted = false;
2386         
2387         /* Ok I'll put some comments here about what is missing.
2388            I have fixed BackSpace (and thus Delete) to not delete
2389            double-spaces automagically. I have also changed Cut,
2390            Copy and Paste to hopefully do some sensible things.
2391            There are still some small problems that can lead to
2392            double spaces stored in the document file or space at
2393            the beginning of paragraphs. This happens if you have
2394            the cursor betwenn to spaces and then save. Or if you
2395            cut and paste and the selection have a space at the
2396            beginning and then save right after the paste. I am
2397            sure none of these are very hard to fix, but I will
2398            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2399            that I can get some feedback. (Lgb)
2400         */
2401
2402         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2403         // delete the LineSeparator.
2404         // MISSING
2405
2406         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2407         // delete the LineSeparator.
2408         // MISSING
2409
2410         // If the pos around the old_cursor were spaces, delete one of them.
2411         if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) { 
2412                 // Only if the cursor has really moved
2413                 
2414                 if (old_cursor.pos() > 0
2415                     && old_cursor.pos() < old_cursor.par()->size()
2416                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2417                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2418                         old_cursor.par()->erase(old_cursor.pos() - 1);
2419                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2420
2421 #ifdef WITH_WARNINGS
2422 #warning This will not work anymore when we have multiple views of the same buffer
2423 // In this case, we will have to correct also the cursors held by
2424 // other bufferviews. It will probably be easier to do that in a more
2425 // automated way in LyXCursor code. (JMarc 26/09/2001)
2426 #endif
2427                         // correct all cursors held by the LyXText
2428                         fixCursorAfterDelete(bview, cursor, old_cursor);
2429                         fixCursorAfterDelete(bview, selection.cursor,
2430                                              old_cursor);
2431                         fixCursorAfterDelete(bview, selection.start,
2432                                              old_cursor);
2433                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2434                         fixCursorAfterDelete(bview, last_sel_cursor,
2435                                              old_cursor);
2436                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2437                         fixCursorAfterDelete(bview, toggle_end_cursor,
2438                                              old_cursor);
2439                         return;
2440                 }
2441         }
2442
2443         // Do not delete empty paragraphs with keepempty set.
2444         if ((textclasslist.Style(bview->buffer()->params.textclass,
2445                                  old_cursor.par()->getLayout())).keepempty)
2446                 return;
2447
2448         // only do our magic if we changed paragraph
2449         if (old_cursor.par() == cursor.par()) 
2450                 return;
2451         
2452         if ((old_cursor.par()->size() == 0
2453              || (old_cursor.par()->size() == 1
2454                  && old_cursor.par()->isLineSeparator(0)))) {
2455                 // ok, we will delete anything
2456                 LyXCursor tmpcursor;
2457                 
2458                 // make sure that you do not delete any environments
2459                 status(bview, LyXText::NEED_MORE_REFRESH);
2460                 deleted = true;
2461                                 
2462                 if (old_cursor.row()->previous()) {
2463                         refresh_row = old_cursor.row()->previous();
2464                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2465                         tmpcursor = cursor;
2466                         cursor = old_cursor; // that undo can restore the right cursor position
2467                         Paragraph * endpar = old_cursor.par()->next();
2468                         if (endpar && endpar->getDepth()) {
2469                                 while (endpar && endpar->getDepth()) {
2470                                         endpar = endpar->next();
2471                                 }
2472                         }
2473                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2474                         cursor = tmpcursor;
2475
2476                                 // delete old row
2477                         removeRow(old_cursor.row());
2478                         if (ownerParagraph() == old_cursor.par()) {
2479                                 ownerParagraph(ownerParagraph()->next());
2480                         }
2481                                 // delete old par
2482                         delete old_cursor.par();
2483                                         
2484                         /* Breakagain the next par. Needed because of
2485                          * the parindent that can occur or dissappear.
2486                          * The next row can change its height, if
2487                          * there is another layout before */
2488                         if (refresh_row->next()) {
2489                                 breakAgain(bview, refresh_row->next());
2490                                 updateCounters(bview, refresh_row);
2491                         }
2492                         setHeightOfRow(bview, refresh_row);
2493                 } else {
2494                         refresh_row = old_cursor.row()->next();
2495                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2496                                         
2497                         tmpcursor = cursor;
2498                         cursor = old_cursor; // that undo can restore the right cursor position
2499                         Paragraph * endpar = old_cursor.par()->next();
2500                         if (endpar && endpar->getDepth()) {
2501                                 while (endpar && endpar->getDepth()) {
2502                                         endpar = endpar->next();
2503                                 }
2504                         }
2505                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2506                         cursor = tmpcursor;
2507
2508                         // delete old row
2509                         removeRow(old_cursor.row());
2510                         // delete old par
2511                         if (ownerParagraph() == old_cursor.par()) {
2512                                 ownerParagraph(ownerParagraph()->next());
2513                         }
2514
2515                         delete old_cursor.par();
2516                                         
2517                         /* Breakagain the next par. Needed because of
2518                            the parindent that can occur or dissappear.
2519                            The next row can change its height, if
2520                            there is another layout before */
2521                         if (refresh_row) {
2522                                 breakAgain(bview, refresh_row);
2523                                 updateCounters(bview, refresh_row->previous());
2524                         }
2525                 }
2526                                 
2527                 // correct cursor y
2528                 setCursorIntern(bview, cursor.par(), cursor.pos());
2529
2530                 if (selection.cursor.par()  == old_cursor.par()
2531                     && selection.cursor.pos() == selection.cursor.pos()) {
2532                         // correct selection
2533                         selection.cursor = cursor;
2534                 }
2535         }
2536         if (!deleted) {
2537                 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2538                         redoParagraphs(bview, old_cursor,
2539                                        old_cursor.par()->next());
2540                         // correct cursor y
2541                         setCursorIntern(bview, cursor.par(), cursor.pos());
2542                         selection.cursor = cursor;
2543                 }
2544         }
2545 }
2546
2547
2548 void LyXText::toggleAppendix(BufferView * bview)
2549 {
2550         Paragraph * par = cursor.par();
2551         bool start = !par->params().startOfAppendix();
2552
2553         // ensure that we have only one start_of_appendix in this document
2554         Paragraph * tmp = firstParagraph();
2555         for (; tmp; tmp = tmp->next()) {
2556                 tmp->params().startOfAppendix(false);
2557         }
2558         
2559         par->params().startOfAppendix(start);
2560
2561         // we can set the refreshing parameters now
2562         status(bview, LyXText::NEED_MORE_REFRESH);
2563         refresh_y = 0;
2564         refresh_row = 0; // not needed for full update
2565         updateCounters(bview, 0);
2566         setCursor(bview, cursor.par(), cursor.pos());
2567 }
2568
2569
2570 Paragraph * LyXText::ownerParagraph() const
2571 {
2572         if (inset_owner) {
2573                 return inset_owner->paragraph();
2574         }
2575         return bv_owner->buffer()->paragraph;
2576 }
2577
2578
2579 void LyXText::ownerParagraph(Paragraph * p) const
2580 {
2581         if (inset_owner) {
2582                 inset_owner->paragraph(p);
2583         } else {
2584                 bv_owner->buffer()->paragraph = p;
2585         }
2586 }
2587
2588
2589 void LyXText::ownerParagraph(int id, Paragraph * p) const
2590 {
2591         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2592         if (op && op->inInset()) {
2593                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2594         } else {
2595                 if (inset_owner) {
2596                         inset_owner->paragraph(p);
2597                 } else {
2598                         bv_owner->buffer()->paragraph = p;
2599                 }
2600         }
2601 }
2602
2603
2604 LyXText::text_status LyXText::status() const
2605 {
2606         return status_;
2607 }
2608
2609
2610 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2611 {
2612         // well as much as I know && binds more then || so the above and the
2613         // below are identical (this for your known use of parentesis!)
2614         // Now some explanation:
2615         // We should only go up with refreshing code so this means that if
2616         // we have a MORE refresh we should never set it to LITTLE if we still
2617         // didn't handle it (and then it will be UNCHANGED. Now as long as
2618         // we stay inside one LyXText this may work but we need to tell the
2619         // outermost LyXText that it should REALLY draw us if there is some
2620         // change in a Inset::LyXText. So you see that when we are inside a
2621         // inset's LyXText we give the LITTLE to the outermost LyXText to
2622         // tell'em that it should redraw the actual row (where the inset
2623         // resides! Capito?!
2624
2625         if ((status_ != NEED_MORE_REFRESH)
2626             || (status_ == NEED_MORE_REFRESH
2627                 && st != NEED_VERY_LITTLE_REFRESH))
2628         {
2629                 status_ = st;
2630                 if (inset_owner && st != UNCHANGED) {
2631                         bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2632                         if (!bview->text->refresh_row) {
2633                                 bview->text->refresh_row = bview->text->cursor.row();
2634                                 bview->text->refresh_y = bview->text->cursor.y() -
2635                                         bview->text->cursor.row()->baseline();
2636                         }
2637                 }
2638         }
2639 }