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