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