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