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