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