]> git.lyx.org Git - lyx.git/blob - src/text2.C
3dac232451dedf466b50c08dbf7c07b1f4dfd7bc
[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 first paragraph
1206         if (par->previous()) {
1207                 for (int i = 0; i < 10; ++i) {
1208                         par->setCounter(i, par->previous()->getFirstCounter(i));
1209                 }
1210                 par->params().appendix(par->previous()->params().appendix());
1211                 if (!par->params().appendix() && par->params().startOfAppendix()) {
1212                   par->params().appendix(true);
1213                   for (int i = 0; i < 10; ++i) {
1214                     par->setCounter(i, 0);
1215                   }  
1216                 }
1217                 par->enumdepth = par->previous()->enumdepth;
1218                 par->itemdepth = par->previous()->itemdepth;
1219         } else {
1220                 for (int i = 0; i < 10; ++i) {
1221                         par->setCounter(i, 0);
1222                 }  
1223                 par->params().appendix(par->params().startOfAppendix());
1224                 par->enumdepth = 0;
1225                 par->itemdepth = 0;
1226         }
1227
1228         /* Maybe we have to increment the enumeration depth.
1229          * BUT, enumeration in a footnote is considered in isolation from its
1230          *      surrounding paragraph so don't increment if this is the
1231          *      first line of the footnote
1232          * AND, bibliographies can't have their depth changed ie. they
1233          *      are always of depth 0
1234          */
1235         if (par->previous()
1236             && par->previous()->getDepth() < par->getDepth()
1237             && textclasslist.Style(buf->params.textclass,
1238                               par->previous()->getLayout()
1239                              ).labeltype == LABEL_COUNTER_ENUMI
1240             && par->enumdepth < 3
1241             && layout.labeltype != LABEL_BIBLIO) {
1242                 par->enumdepth++;
1243         }
1244
1245         /* Maybe we have to decrement the enumeration depth, see note above */
1246         if (par->previous()
1247             && par->previous()->getDepth() > par->getDepth()
1248             && layout.labeltype != LABEL_BIBLIO) {
1249                 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1250                 par->setCounter(6 + par->enumdepth,
1251                         par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1252                 /* reset the counters.
1253                  * A depth change is like a breaking layout
1254                  */
1255                 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1256                         par->setCounter(i, 0);
1257         }
1258    
1259         if (!par->params().labelString().empty()) {
1260                 par->params().labelString(string());
1261         }
1262    
1263         if (layout.margintype == MARGIN_MANUAL) {
1264                 if (par->params().labelWidthString().empty()) {
1265                         par->setLabelWidthString(layout.labelstring());
1266                 }
1267         } else {
1268                 par->setLabelWidthString(string());
1269         }
1270    
1271         /* is it a layout that has an automatic label ? */ 
1272         if (layout.labeltype >=  LABEL_COUNTER_CHAPTER) {
1273       
1274                 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1275                 if (i >= 0 && i<= buf->params.secnumdepth) {
1276                         par->incCounter(i);     // increment the counter  
1277          
1278                         // Is there a label? Useful for Chapter layout
1279                         if (!par->params().appendix()) {
1280                                 if (!layout.labelstring().empty())
1281                                         par->params().labelString(layout.labelstring());
1282                                 else
1283                                         par->params().labelString(string());
1284                         } else {
1285                                 if (!layout.labelstring_appendix().empty())
1286                                         par->params().labelString(layout.labelstring_appendix());
1287                                 else
1288                                         par->params().labelString(string());
1289                         }
1290
1291                         std::ostringstream s;
1292
1293                         if (!par->params().appendix()) {
1294                                 switch (2 * LABEL_COUNTER_CHAPTER -
1295                                         textclass.maxcounter() + i) {
1296                                 case LABEL_COUNTER_CHAPTER:
1297                                         s << par->getCounter(i);
1298                                         break;
1299                                 case LABEL_COUNTER_SECTION:
1300                                         s << par->getCounter(i - 1) << '.'
1301                                            << par->getCounter(i);
1302                                         break;
1303                                 case LABEL_COUNTER_SUBSECTION:
1304                                         s << par->getCounter(i - 2) << '.'
1305                                           << par->getCounter(i - 1) << '.'
1306                                           << par->getCounter(i);
1307                                         break;
1308                                 case LABEL_COUNTER_SUBSUBSECTION:
1309                                         s << par->getCounter(i - 3) << '.'
1310                                           << par->getCounter(i - 2) << '.'
1311                                           << par->getCounter(i - 1) << '.'
1312                                           << par->getCounter(i);
1313                                         
1314                                         break;
1315                                 case LABEL_COUNTER_PARAGRAPH:
1316                                         s << par->getCounter(i - 4) << '.'
1317                                           << par->getCounter(i - 3) << '.'
1318                                           << par->getCounter(i - 2) << '.'
1319                                           << par->getCounter(i - 1) << '.'
1320                                           << par->getCounter(i);
1321                                         break;
1322                                 case LABEL_COUNTER_SUBPARAGRAPH:
1323                                         s << par->getCounter(i - 5) << '.'
1324                                           << par->getCounter(i - 4) << '.'
1325                                           << par->getCounter(i - 3) << '.'
1326                                           << par->getCounter(i - 2) << '.'
1327                                           << par->getCounter(i - 1) << '.'
1328                                           << par->getCounter(i);
1329
1330                                         break;
1331                                 default:
1332                                         // Can this ever be reached? And in the
1333                                         // case it is, how can this be correct?
1334                                         // (Lgb)
1335                                         s << par->getCounter(i) << '.';
1336                                         break;
1337                                 }
1338                         } else { // appendix
1339                                 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1340                                 case LABEL_COUNTER_CHAPTER:
1341                                         if (par->isRightToLeftPar(buf->params))
1342                                                 s << hebrewCounter(par->getCounter(i));
1343                                         else
1344                                                 s << alphaCounter(par->getCounter(i));
1345                                         break;
1346                                 case LABEL_COUNTER_SECTION:
1347                                         if (par->isRightToLeftPar(buf->params))
1348                                                 s << hebrewCounter(par->getCounter(i - 1));
1349                                         else
1350                                                 s << alphaCounter(par->getCounter(i - 1));
1351
1352                                         s << '.'
1353                                           << par->getCounter(i);
1354
1355                                         break;
1356                                 case LABEL_COUNTER_SUBSECTION:
1357                                         if (par->isRightToLeftPar(buf->params))
1358                                                 s << hebrewCounter(par->getCounter(i - 2));
1359                                         else
1360                                                 s << alphaCounter(par->getCounter(i - 2));
1361
1362                                         s << '.'
1363                                           << par->getCounter(i-1) << '.'
1364                                           << par->getCounter(i);
1365
1366                                         break;
1367                                 case LABEL_COUNTER_SUBSUBSECTION:
1368                                         if (par->isRightToLeftPar(buf->params))
1369                                                 s << hebrewCounter(par->getCounter(i-3));
1370                                         else
1371                                                 s << alphaCounter(par->getCounter(i-3));
1372
1373                                         s << '.'
1374                                           << par->getCounter(i-2) << '.'
1375                                           << par->getCounter(i-1) << '.'
1376                                           << par->getCounter(i);
1377
1378                                         break;
1379                                 case LABEL_COUNTER_PARAGRAPH:
1380                                         if (par->isRightToLeftPar(buf->params))
1381                                                 s << hebrewCounter(par->getCounter(i-4));
1382                                         else
1383                                                 s << alphaCounter(par->getCounter(i-4));
1384
1385                                         s << '.'
1386                                           << par->getCounter(i-3) << '.'
1387                                           << par->getCounter(i-2) << '.'
1388                                           << par->getCounter(i-1) << '.'
1389                                           << par->getCounter(i);
1390
1391                                         break;
1392                                 case LABEL_COUNTER_SUBPARAGRAPH:
1393                                         if (par->isRightToLeftPar(buf->params))
1394                                                 s << hebrewCounter(par->getCounter(i-5));
1395                                         else
1396                                                 s << alphaCounter(par->getCounter(i-5));
1397
1398                                         s << '.'
1399                                           << par->getCounter(i-4) << '.'
1400                                           << par->getCounter(i-3) << '.'
1401                                           << par->getCounter(i-2) << '.'
1402                                           << par->getCounter(i-1) << '.'
1403                                           << par->getCounter(i);
1404
1405                                         break;
1406                                 default:
1407                                         // Can this ever be reached? And in the
1408                                         // case it is, how can this be correct?
1409                                         // (Lgb)
1410                                         s << par->getCounter(i) << '.';
1411                                         
1412                                         break;
1413                                 }
1414                         }
1415
1416                         par->params().labelString(par->params().labelString() +s.str().c_str());
1417                         // We really want to remove the c_str as soon as
1418                         // possible...
1419                         
1420                         for (i++; i < 10; ++i) {
1421                                 // reset the following counters
1422                                 par->setCounter(i, 0);
1423                         }
1424                 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
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                         par->incCounter(i + par->enumdepth);
1431                         int number = par->getCounter(i + par->enumdepth);
1432
1433                         std::ostringstream s;
1434
1435                         switch (par->enumdepth) {
1436                         case 1:
1437                                 if (par->isRightToLeftPar(buf->params))
1438                                         s << '('
1439                                           << hebrewCounter(number)
1440                                           << ')';
1441                                 else
1442                                         s << '('
1443                                           << loweralphaCounter(number)
1444                                           << ')';
1445                                 break;
1446                         case 2:
1447                                 if (par->isRightToLeftPar(buf->params))
1448                                         s << '.' << romanCounter(number);
1449                                 else
1450                                         s << romanCounter(number) << '.';
1451                                 break;
1452                         case 3:
1453                                 if (par->isRightToLeftPar(buf->params))
1454                                         s << '.'
1455                                           << alphaCounter(number);
1456                                 else
1457                                         s << alphaCounter(number)
1458                                           << '.';
1459                                 break;
1460                         default:
1461                                 if (par->isRightToLeftPar(buf->params))
1462                                         s << '.' << number;
1463                                 else
1464                                         s << number << '.';
1465                                 break;
1466                         }
1467
1468                         par->params().labelString(s.str().c_str());
1469                         // we really want to get rid of that c_str()
1470
1471                         for (i += par->enumdepth + 1; i < 10; ++i)
1472                                 par->setCounter(i, 0);  /* reset the following counters  */
1473          
1474                 } 
1475         } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1476                 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1477                 par->incCounter(i);
1478                 int number = par->getCounter(i);
1479                 if (!par->bibkey) {
1480                         InsetCommandParams p( "bibitem" );
1481                         par->bibkey = new InsetBibKey(p);
1482                 }
1483                 par->bibkey->setCounter(number);
1484                 par->params().labelString(layout.labelstring());
1485                 
1486                 // In biblio should't be following counters but...
1487         } else {
1488                 string s = layout.labelstring();
1489                 
1490                 // the caption hack:
1491                 if (layout.labeltype == LABEL_SENSITIVE) {
1492                         bool isOK (par->InInset() && par->InInset()->owner() &&
1493                                    (par->InInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1494                         
1495                         if (isOK) {
1496                                 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1497                                 Floating const & fl
1498                                         = floatList.getType(tmp->type());
1499                                 // We should get the correct number here too.
1500                                 s = fl.name() + " #:";
1501                         } else {
1502                                 /* par->SetLayout(0); 
1503                                    s = layout->labelstring;  */
1504                                 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1505                                         ? " :úåòîùî Ã¸Ã±Ã§" : "Senseless: ";
1506                         }
1507                 }
1508                 par->params().labelString(s);
1509                 
1510                 /* reset the enumeration counter. They are always resetted
1511                  * when there is any other layout between */ 
1512                 for (int i = 6 + par->enumdepth; i < 10; ++i)
1513                         par->setCounter(i, 0);
1514         }
1515 }
1516
1517
1518 /* Updates all counters BEHIND the row. Changed paragraphs
1519 * with a dynamic left margin will be rebroken. */ 
1520 void LyXText::updateCounters(BufferView * bview, Row * row) const
1521 {
1522         Paragraph * par;
1523
1524         if (!row) {
1525                 row = firstrow;
1526                 par = row->par();
1527         } else {
1528                 par = row->par()->next();
1529         }
1530
1531         while (par) {
1532                 while (row->par() != par)
1533                         row = row->next();
1534                 
1535                 setCounter(bview->buffer(), par);
1536                 
1537                 /* now  check for the headline layouts. remember that they
1538                  * have a dynamic left margin */ 
1539                 if ((textclasslist.Style(bview->buffer()->params.textclass,
1540                                          par->layout).margintype == MARGIN_DYNAMIC
1541                      || textclasslist.Style(bview->buffer()->params.textclass,
1542                                             par->layout).labeltype == LABEL_SENSITIVE)) {
1543                         
1544                         /* Rebreak the paragraph */ 
1545                         removeParagraph(row);
1546                         appendParagraph(bview, row);
1547                 }
1548                 par = par->next();
1549         }
1550 }
1551
1552
1553 /* insets an inset. */ 
1554 void LyXText::insertInset(BufferView * bview, Inset * inset)
1555 {
1556         if (!cursor.par()->insertInsetAllowed(inset))
1557                 return;
1558         setUndo(bview, Undo::INSERT,
1559                 cursor.par(), cursor.par()->next());
1560         cursor.par()->insertInset(cursor.pos(), inset);
1561         insertChar(bview, Paragraph::META_INSET);  /* just to rebreak and refresh correctly.
1562                                       * The character will not be inserted a
1563                                       * second time */
1564 #if 1
1565         // If we enter a highly editable inset the cursor should be to before
1566         // the inset. This couldn't happen before as Undo was not handled inside
1567         // inset now after the Undo LyX tries to call inset->Edit(...) again
1568         // and cannot do this as the cursor is behind the inset and GetInset
1569         // does not return the inset!
1570         if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1571                 cursorLeft(bview, true);
1572         }
1573 #endif
1574 }
1575
1576
1577 void LyXText::copyEnvironmentType()
1578 {
1579         copylayouttype = cursor.par()->getLayout();
1580 }
1581
1582
1583 void LyXText::pasteEnvironmentType(BufferView * bview)
1584 {
1585         setLayout(bview, copylayouttype);
1586 }
1587
1588
1589 void LyXText::cutSelection(BufferView * bview, bool doclear)
1590 {
1591         // Stuff what we got on the clipboard. Even if there is no selection.
1592
1593         // There is a problem with having the stuffing here in that the
1594         // larger the selection the slower LyX will get. This can be
1595         // solved by running the line below only when the selection has
1596         // finished. The solution used currently just works, to make it
1597         // faster we need to be more clever and probably also have more
1598         // calls to stuffClipboard. (Lgb)
1599         bview->stuffClipboard(selectionAsString(bview->buffer()));
1600
1601         // This doesn't make sense, if there is no selection
1602         if (!selection.set())
1603                 return;
1604    
1605         // OK, we have a selection. This is always between selection.start
1606         // and selection.end
1607
1608         // make sure that the depth behind the selection are restored, too
1609         Paragraph * endpar = selection.end.par()->next();
1610         Paragraph * undoendpar = endpar;
1611     
1612         if (endpar && endpar->getDepth()) {
1613                 while (endpar && endpar->getDepth()) {
1614                         endpar = endpar->next();
1615                         undoendpar = endpar;
1616                 }
1617         } else if (endpar) {
1618                 endpar = endpar->next(); // because of parindents etc.
1619         }
1620     
1621         setUndo(bview, Undo::DELETE,
1622                 selection.start.par(), undoendpar);
1623     
1624         // there are two cases: cut only within one paragraph or
1625         // more than one paragraph
1626         if (selection.start.par() == selection.end.par()) {
1627                 // only within one paragraph
1628                 endpar = selection.end.par();
1629                 int pos = selection.end.pos();
1630                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1631                                  selection.start.pos(), pos,
1632                                  bview->buffer()->params.textclass, doclear);
1633                 selection.end.pos(pos);
1634         } else {
1635                 endpar = selection.end.par();
1636                 int pos = selection.end.pos();
1637                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1638                                  selection.start.pos(), pos,
1639                                  bview->buffer()->params.textclass, doclear);
1640                 cursor.par(endpar);
1641                 selection.end.par(endpar);
1642                 selection.end.pos(pos);
1643                 cursor.pos(selection.end.pos());
1644         }
1645         endpar = endpar->next();
1646
1647         // sometimes necessary
1648         if (doclear)
1649                 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1650
1651         redoParagraphs(bview, selection.start, endpar);
1652
1653         // cutSelection can invalidate the cursor so we need to set
1654         // it anew. (Lgb)
1655         cursor = selection.start;
1656
1657         // need a valid cursor. (Lgb)
1658         clearSelection(bview);
1659
1660         setCursor(bview, cursor.par(), cursor.pos());
1661         selection.cursor = cursor;
1662         updateCounters(bview, cursor.row());
1663 }
1664
1665
1666 void LyXText::copySelection(BufferView * bview)
1667 {
1668         // Stuff what we got on the clipboard. Even if there is no selection.
1669
1670         // There is a problem with having the stuffing here in that the
1671         // larger the selection the slower LyX will get. This can be
1672         // solved by running the line below only when the selection has
1673         // finished. The solution used currently just works, to make it
1674         // faster we need to be more clever and probably also have more
1675         // calls to stuffClipboard. (Lgb)
1676         bview->stuffClipboard(selectionAsString(bview->buffer()));
1677
1678         // this doesnt make sense, if there is no selection
1679         if (!selection.set())
1680                 return;
1681
1682         // ok we have a selection. This is always between selection.start
1683         // and sel_end cursor
1684
1685         // copy behind a space if there is one
1686         while (selection.start.par()->size() > selection.start.pos()
1687                && selection.start.par()->isLineSeparator(selection.start.pos())
1688                && (selection.start.par() != selection.end.par()
1689                    || selection.start.pos() < selection.end.pos()))
1690                 selection.start.pos(selection.start.pos() + 1); 
1691
1692         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1693                                    selection.start.pos(), selection.end.pos(),
1694                                    bview->buffer()->params.textclass);
1695 }
1696
1697
1698 void LyXText::pasteSelection(BufferView * bview)
1699 {
1700         // this does not make sense, if there is nothing to paste
1701         if (!CutAndPaste::checkPastePossible(cursor.par()))
1702                 return;
1703
1704         setUndo(bview, Undo::INSERT,
1705                 cursor.par(), cursor.par()->next()); 
1706
1707         Paragraph * endpar;
1708         Paragraph * actpar = cursor.par();
1709
1710         int pos = cursor.pos();
1711         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1712                            bview->buffer()->params.textclass);
1713     
1714         redoParagraphs(bview, cursor, endpar);
1715         
1716         setCursor(bview, cursor.par(), cursor.pos());
1717         clearSelection(bview);
1718    
1719         selection.cursor = cursor;
1720         setCursor(bview, actpar, pos);
1721         setSelection(bview);
1722         updateCounters(bview, cursor.row());
1723 }
1724
1725
1726 // returns a pointer to the very first Paragraph
1727 Paragraph * LyXText::firstParagraph() const
1728 {
1729         return ownerParagraph();
1730 }
1731
1732
1733 // sets the selection over the number of characters of string, no check!!
1734 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1735 {
1736         if (str.empty())
1737                 return;
1738         
1739         selection.cursor = cursor;
1740         for (string::size_type i = 0; i < str.length(); ++i)
1741                 cursorRight(bview);
1742         setSelection(bview);
1743 }
1744
1745
1746 // simple replacing. The font of the first selected character is used
1747 void LyXText::replaceSelectionWithString(BufferView * bview,
1748                                          string const & str)
1749 {
1750         setCursorParUndo(bview);
1751         freezeUndo();
1752
1753         if (!selection.set()) { // create a dummy selection
1754                 selection.end = cursor;
1755                 selection.start = cursor;
1756         }
1757
1758         // Get font setting before we cut
1759         Paragraph::size_type pos = selection.end.pos();
1760         LyXFont const font = selection.start.par()
1761                 ->getFontSettings(bview->buffer()->params,
1762                                   selection.start.pos());
1763
1764         // Insert the new string
1765         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1766                 selection.end.par()->insertChar(pos, (*cit), font);
1767                 ++pos;
1768         }
1769         
1770         // Cut the selection
1771         cutSelection(bview);
1772
1773         unFreezeUndo();
1774 }
1775
1776
1777 // needed to insert the selection
1778 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1779 {
1780         Paragraph * par = cursor.par();
1781         Paragraph::size_type pos = cursor.pos();
1782         Paragraph * endpar = cursor.par()->next();
1783         
1784         setCursorParUndo(bview);
1785         
1786         bool isEnvironment =
1787                 textclasslist.Style(bview->buffer()->params.textclass, 
1788                                     cursor.par()->getLayout()).isEnvironment();
1789         bool free_spacing =
1790                 textclasslist.Style(bview->buffer()->params.textclass, 
1791                                     cursor.par()->getLayout()).free_spacing;
1792         bool keepempty =
1793                 textclasslist.Style(bview->buffer()->params.textclass, 
1794                                     cursor.par()->getLayout()).keepempty;
1795
1796         // only to be sure, should not be neccessary
1797         clearSelection(bview);
1798         
1799         // insert the string, don't insert doublespace
1800         bool space_inserted = true;
1801         for(string::const_iterator cit = str.begin(); 
1802             cit != str.end(); ++cit) {
1803                 if (*cit == '\n') {
1804                         if (par->size() || keepempty) { 
1805                                 par->breakParagraph(bview->buffer()->params, 
1806                                                     pos, isEnvironment);
1807                                 par = par->next();
1808                                 pos = 0;
1809                                 space_inserted = true;
1810                         } else {
1811                                 continue;
1812                         }
1813                 // do not insert consecutive spaces if !free_spacing
1814                 } else if ((*cit == ' ' || *cit == '\t')
1815                     && space_inserted && !free_spacing) {
1816                         continue;
1817                 } else if (*cit == '\t') {
1818                         if (!free_spacing) {
1819                                 // tabs are like spaces here
1820                                 par->insertChar(pos, ' ', 
1821                                                 current_font);
1822                                 ++pos;
1823                                 space_inserted = true;
1824                         } else {
1825                                 const Paragraph::value_type nb = 8 - pos % 8;
1826                                 for (Paragraph::size_type a = 0; 
1827                                      a < nb ; ++a) {
1828                                         par->insertChar(pos, ' ', 
1829                                                         current_font);
1830                                         ++pos;
1831                                 }
1832                                 space_inserted = true;
1833                         }
1834                 } else if (!IsPrintable(*cit)) {
1835                         // Ignore unprintables
1836                         continue;
1837                 } else {
1838                         // just insert the character
1839                         par->insertChar(pos, *cit, current_font);
1840                         ++pos;
1841                         space_inserted = (*cit == ' ');
1842                 }
1843
1844         }       
1845
1846         redoParagraphs(bview, cursor, endpar);
1847         setCursor(bview, cursor.par(), cursor.pos());
1848         selection.cursor = cursor;
1849         setCursor(bview, par, pos);
1850         setSelection(bview);
1851 }
1852
1853
1854 /* turns double-CR to single CR, others where converted into one
1855    blank. Then InsertStringAsLines is called */
1856 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1857 {
1858         string linestr(str);
1859         bool newline_inserted = false;
1860         for (string::size_type i = 0; i < linestr.length(); ++i) {
1861                 if (linestr[i] == '\n') {
1862                         if (newline_inserted) {
1863                                 // we know that \r will be ignored by
1864                                 // InsertStringA. Of course, it is a dirty
1865                                 // trick, but it works...
1866                                 linestr[i - 1] = '\r';
1867                                 linestr[i] = '\n';
1868                         } else {
1869                                 linestr[i] = ' ';
1870                                 newline_inserted = true;
1871                         } 
1872                 } else if (IsPrintable(linestr[i])) {
1873                         newline_inserted = false;
1874                 }
1875         }
1876         insertStringAsLines(bview, linestr);
1877 }
1878
1879
1880 bool LyXText::gotoNextInset(BufferView * bview,
1881                             std::vector<Inset::Code> const & codes,
1882                             string const & contents) const
1883 {
1884         LyXCursor res = cursor;
1885         Inset * inset;
1886         do {
1887                 if (res.pos() < res.par()->size() - 1) {
1888                         res.pos(res.pos() + 1);
1889                 } else  {
1890                         res.par(res.par()->next());
1891                         res.pos(0);
1892                 }
1893       
1894         } while (res.par() && 
1895                  !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1896                    && (inset = res.par()->getInset(res.pos())) != 0
1897                    && find(codes.begin(), codes.end(), inset->lyxCode())
1898                       != codes.end()
1899                    && (contents.empty() ||
1900                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1901                        == contents)));
1902
1903         if (res.par()) {
1904                 setCursor(bview, res.par(), res.pos());
1905                 return true;
1906         }
1907         return false;
1908 }
1909
1910
1911 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1912                              Paragraph::size_type pos)
1913 {
1914         LyXCursor tmpcursor;                    
1915
1916         int y = 0;
1917         Paragraph::size_type z;
1918         Row * row = getRow(par, pos, y);
1919         
1920         // is there a break one row above
1921         if (row->previous() && row->previous()->par() == row->par()) {
1922                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1923                 if (z >= row->pos()) {
1924                         // set the dimensions of the row above
1925                         y -= row->previous()->height();
1926                         refresh_y = y;
1927                         refresh_row = row->previous();
1928                         status(bview, LyXText::NEED_MORE_REFRESH);
1929                         
1930                         breakAgain(bview, row->previous());
1931                         
1932                         // set the cursor again. Otherwise
1933                         // dangling pointers are possible
1934                         setCursor(bview, cursor.par(), cursor.pos(),
1935                                   false, cursor.boundary());
1936                         selection.cursor = cursor;
1937                         return;
1938                 }
1939         }
1940
1941         int const tmpheight = row->height();
1942         Paragraph::size_type const tmplast = rowLast(row);
1943         refresh_y = y;
1944         refresh_row = row;
1945         
1946         breakAgain(bview, row);
1947         if (row->height() == tmpheight && rowLast(row) == tmplast)
1948                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1949         else
1950                 status(bview, LyXText::NEED_MORE_REFRESH); 
1951         
1952         // check the special right address boxes
1953         if (textclasslist.Style(bview->buffer()->params.textclass,
1954                                 par->getLayout()).margintype
1955             == MARGIN_RIGHT_ADDRESS_BOX) {
1956                 tmpcursor.par(par);
1957                 tmpcursor.row(row);
1958                 tmpcursor.y(y);
1959                 tmpcursor.x(0);
1960                 tmpcursor.x_fix(0);
1961                 tmpcursor.pos(pos);
1962                 redoDrawingOfParagraph(bview, tmpcursor); 
1963         }
1964
1965         // set the cursor again. Otherwise dangling pointers are possible
1966         // also set the selection
1967    
1968         if (selection.set()) {
1969                 tmpcursor = cursor;
1970                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1971                                 false, selection.cursor.boundary());
1972                 selection.cursor = cursor; 
1973                 setCursorIntern(bview, selection.start.par(),
1974                                 selection.start.pos(),
1975                                 false, selection.start.boundary());
1976                 selection.start = cursor; 
1977                 setCursorIntern(bview, selection.end.par(),
1978                                 selection.end.pos(),
1979                                 false, selection.end.boundary());
1980                 selection.end = cursor; 
1981                 setCursorIntern(bview, last_sel_cursor.par(),
1982                                 last_sel_cursor.pos(),
1983                                 false, last_sel_cursor.boundary());
1984                 last_sel_cursor = cursor; 
1985                 cursor = tmpcursor;
1986         }
1987         setCursorIntern(bview, cursor.par(), cursor.pos(),
1988                         false, cursor.boundary());
1989 }
1990
1991
1992 // returns false if inset wasn't found
1993 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1994 {
1995         // first check the current paragraph
1996         int pos = cursor.par()->getPositionOfInset(inset);
1997         if (pos != -1){
1998                 checkParagraph(bview, cursor.par(), pos);
1999                 return true;
2000         }
2001   
2002         // check every paragraph
2003   
2004         Paragraph * par = firstParagraph();
2005         do {
2006                         pos = par->getPositionOfInset(inset);
2007                         if (pos != -1){
2008                                 checkParagraph(bview, par, pos);
2009                                 return true;
2010                         }
2011                 par = par->next();
2012         } while (par);
2013   
2014         return false;
2015 }
2016
2017
2018 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2019                         Paragraph::size_type pos, 
2020                         bool setfont, bool boundary) const
2021 {
2022         LyXCursor old_cursor = cursor;
2023         setCursorIntern(bview, par, pos, setfont, boundary);
2024         deleteEmptyParagraphMechanism(bview, old_cursor);
2025 }
2026
2027
2028 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2029                         Paragraph::size_type pos, bool boundary) const
2030 {
2031         cur.par(par);
2032         cur.pos(pos);
2033         cur.boundary(boundary);
2034
2035         /* get the cursor y position in text  */
2036         int y = 0;
2037         Row * row = getRow(par, pos, y);
2038         /* y is now the beginning of the cursor row */ 
2039         y += row->baseline();
2040         /* y is now the cursor baseline */ 
2041         cur.y(y);
2042    
2043         /* now get the cursors x position */
2044         float x;
2045         float fill_separator;
2046         float fill_hfill;
2047         float fill_label_hfill;
2048         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2049                        fill_label_hfill);
2050         Paragraph::size_type cursor_vpos = 0;
2051         Paragraph::size_type last = rowLastPrintable(row);
2052
2053         if (pos > last + 1)   // This shouldn't happen.
2054                 pos = last + 1;
2055         else if (pos < row->pos())
2056                 pos = row->pos();
2057
2058         if (last < row->pos())
2059                 cursor_vpos = row->pos();
2060         else if (pos > last && !boundary)
2061                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2062                         ? row->pos() : last + 1; 
2063         else if (pos > row->pos() &&
2064                  (pos > last || boundary))
2065                 /// Place cursor after char at (logical) position pos - 1
2066                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2067                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2068         else
2069                 /// Place cursor before char at (logical) position pos
2070                 cursor_vpos = (bidi_level(pos) % 2 == 0)
2071                         ? log2vis(pos) : log2vis(pos) + 1;
2072         
2073         Paragraph::size_type main_body =
2074                 beginningOfMainBody(bview->buffer(), row->par());
2075         if ((main_body > 0) &&
2076             ((main_body-1 > last) || 
2077              !row->par()->isLineSeparator(main_body-1)))
2078                 main_body = 0;
2079         
2080         for (Paragraph::size_type vpos = row->pos();
2081              vpos < cursor_vpos; ++vpos) {
2082                 pos = vis2log(vpos);
2083                 if (main_body > 0 && pos == main_body - 1) {
2084                         x += fill_label_hfill +
2085                                 lyxfont::width(textclasslist.Style(
2086                                         bview->buffer()->params.textclass,
2087                                         row->par()->getLayout())
2088                                                .labelsep,
2089                                                getFont(bview->buffer(), row->par(), -2));
2090                         if (row->par()->isLineSeparator(main_body-1))
2091                                 x -= singleWidth(bview, row->par(),main_body-1);
2092                 }
2093                 if (hfillExpansion(bview->buffer(), row, pos)) {
2094                         x += singleWidth(bview, row->par(), pos);
2095                         if (pos >= main_body)
2096                                 x += fill_hfill;
2097                         else 
2098                                 x += fill_label_hfill;
2099                 } else if (row->par()->isSeparator(pos)) {
2100                         x += singleWidth(bview, row->par(), pos);
2101                         if (pos >= main_body)
2102                                 x += fill_separator;
2103                 } else
2104                         x += singleWidth(bview, row->par(), pos);
2105         }
2106         
2107         cur.x(int(x));
2108         cur.x_fix(cur.x());
2109         cur.row(row);
2110 }
2111
2112
2113 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2114                               Paragraph::size_type pos,
2115                               bool setfont, bool boundary) const
2116 {
2117         InsetText * it = static_cast<InsetText *>(par->InInset());
2118         if (it && (it != inset_owner)) {
2119                 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2120                                                                                            boundary);
2121         } else {
2122                 setCursor(bview, cursor, par, pos, boundary);
2123                 if (setfont)
2124                         setCurrentFont(bview);
2125         }
2126 }
2127
2128
2129 void LyXText::setCurrentFont(BufferView * bview) const
2130 {
2131         Paragraph::size_type pos = cursor.pos();
2132         if (cursor.boundary() && pos > 0)
2133                 --pos;
2134
2135         if (pos > 0) {
2136                 if (pos == cursor.par()->size())
2137                         --pos;
2138                 else // potentional bug... BUG (Lgb)
2139                 if (cursor.par()->isSeparator(pos)) {
2140                         if (pos > cursor.row()->pos() &&
2141                             bidi_level(pos) % 2 == 
2142                             bidi_level(pos - 1) % 2)
2143                                 --pos;
2144                         else if (pos + 1 < cursor.par()->size())
2145                                 ++pos;
2146                 }
2147         }
2148
2149         current_font =
2150                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2151         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2152
2153         if (cursor.pos() == cursor.par()->size() &&
2154             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2155             !cursor.boundary()) {
2156                 Language const * lang =
2157                         cursor.par()->getParLanguage(bview->buffer()->params);
2158                 current_font.setLanguage(lang);
2159                 current_font.setNumber(LyXFont::OFF);
2160                 real_current_font.setLanguage(lang);
2161                 real_current_font.setNumber(LyXFont::OFF);
2162         }
2163 }
2164
2165
2166 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2167 {
2168         LyXCursor old_cursor = cursor;
2169    
2170         /* get the row first */ 
2171    
2172         Row * row = getRowNearY(y);
2173         cursor.par(row->par());
2174
2175         bool bound = false;
2176         int column = getColumnNearX(bview, row, x, bound);
2177         cursor.pos(row->pos() + column);
2178         cursor.x(x);
2179         cursor.y(y + row->baseline());
2180         cursor.row(row);
2181         cursor.boundary(bound);
2182         setCurrentFont(bview);
2183         deleteEmptyParagraphMechanism(bview, old_cursor);
2184 }
2185
2186
2187 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2188                                        int x, int y) const
2189 {
2190         /* get the row first */ 
2191    
2192         Row * row = getRowNearY(y);
2193         bool bound = false;
2194         int column = getColumnNearX(bview, row, x, bound);
2195    
2196         cur.par(row->par());
2197         cur.pos(row->pos() + column);
2198         cur.x(x);
2199         cur.y(y + row->baseline());
2200         cur.row(row);
2201         cur.boundary(bound);
2202 }
2203
2204
2205 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2206 {
2207         if (cursor.pos() > 0) {
2208                 bool boundary = cursor.boundary();
2209                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2210                 if (!internal && !boundary &&
2211                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2212                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2213         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2214                 Paragraph * par = cursor.par()->previous();
2215                 setCursor(bview, par, par->size());
2216         }
2217 }
2218
2219
2220 void LyXText::cursorRight(BufferView * bview, bool internal) const
2221 {
2222         if (!internal && cursor.boundary() &&
2223             !cursor.par()->isNewline(cursor.pos()))
2224                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2225         else if (cursor.pos() < cursor.par()->size()) {
2226                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2227                 if (!internal &&
2228                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2229                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2230         } else if (cursor.par()->next())
2231                 setCursor(bview, cursor.par()->next(), 0);
2232 }
2233
2234
2235 void LyXText::cursorUp(BufferView * bview) const
2236 {
2237         setCursorFromCoordinates(bview, cursor.x_fix(), 
2238                                  cursor.y() - cursor.row()->baseline() - 1);
2239 }
2240
2241
2242 void LyXText::cursorDown(BufferView * bview) const
2243 {
2244         setCursorFromCoordinates(bview, cursor.x_fix(), 
2245                                  cursor.y() - cursor.row()->baseline()
2246                                  + cursor.row()->height() + 1);
2247 }
2248
2249
2250 void LyXText::cursorUpParagraph(BufferView * bview) const
2251 {
2252         if (cursor.pos() > 0) {
2253                 setCursor(bview, cursor.par(), 0);
2254         }
2255         else if (cursor.par()->previous()) {
2256                 setCursor(bview, cursor.par()->previous(), 0);
2257         }
2258 }
2259
2260
2261 void LyXText::cursorDownParagraph(BufferView * bview) const
2262 {
2263         if (cursor.par()->next()) {
2264                 setCursor(bview, cursor.par()->next(), 0);
2265         } else {
2266                 setCursor(bview, cursor.par(), cursor.par()->size());
2267         }
2268 }
2269
2270
2271 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2272                                             LyXCursor const & old_cursor) const
2273 {
2274         // Would be wrong to delete anything if we have a selection.
2275         if (selection.set()) return;
2276
2277         // We allow all kinds of "mumbo-jumbo" when freespacing.
2278         if (textclasslist.Style(bview->buffer()->params.textclass,
2279                                 old_cursor.par()->getLayout()).free_spacing)
2280                 return;
2281
2282         bool deleted = false;
2283         
2284         /* Ok I'll put some comments here about what is missing.
2285            I have fixed BackSpace (and thus Delete) to not delete
2286            double-spaces automagically. I have also changed Cut,
2287            Copy and Paste to hopefully do some sensible things.
2288            There are still some small problems that can lead to
2289            double spaces stored in the document file or space at
2290            the beginning of paragraphs. This happens if you have
2291            the cursor betwenn to spaces and then save. Or if you
2292            cut and paste and the selection have a space at the
2293            beginning and then save right after the paste. I am
2294            sure none of these are very hard to fix, but I will
2295            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2296            that I can get some feedback. (Lgb)
2297         */
2298
2299         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2300         // delete the LineSeparator.
2301         // MISSING
2302
2303         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2304         // delete the LineSeparator.
2305         // MISSING
2306
2307         // If the pos around the old_cursor were spaces, delete one of them.
2308         if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) { 
2309                 // Only if the cursor has really moved
2310                 
2311                 if (old_cursor.pos() > 0
2312                     && old_cursor.pos() < old_cursor.par()->size()
2313                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2314                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2315                         old_cursor.par()->erase(old_cursor.pos() - 1);
2316                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2317                         // correct cursor
2318                         if (old_cursor.par() == cursor.par() &&
2319                             cursor.pos() > old_cursor.pos()) {
2320                                 setCursorIntern(bview, cursor.par(),
2321                                                 cursor.pos() - 1);
2322                         } else
2323                                 setCursorIntern(bview, cursor.par(),
2324                                                 cursor.pos());
2325                         return;
2326                 }
2327         }
2328
2329         // Do not delete empty paragraphs with keepempty set.
2330         if ((textclasslist.Style(bview->buffer()->params.textclass,
2331                                  old_cursor.par()->getLayout())).keepempty)
2332                 return;
2333
2334         LyXCursor tmpcursor;
2335
2336         if (old_cursor.par() != cursor.par()) {
2337                 if ((old_cursor.par()->size() == 0
2338                       || (old_cursor.par()->size() == 1
2339                           && old_cursor.par()->isLineSeparator(0)))) {
2340                         // ok, we will delete anything
2341                         
2342                         // make sure that you do not delete any environments
2343                                 status(bview, LyXText::NEED_MORE_REFRESH);
2344                                 deleted = true;
2345                                 
2346                                 if (old_cursor.row()->previous()) {
2347                                         refresh_row = old_cursor.row()->previous();
2348                                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2349                                         tmpcursor = cursor;
2350                                         cursor = old_cursor; // that undo can restore the right cursor position
2351                                         Paragraph * endpar = old_cursor.par()->next();
2352                                         if (endpar && endpar->getDepth()) {
2353                                                 while (endpar && endpar->getDepth()) {
2354                                                         endpar = endpar->next();
2355                                                 }
2356                                         }
2357                                         setUndo(bview, Undo::DELETE,
2358                                                 old_cursor.par(),
2359                                                 endpar);
2360                                         cursor = tmpcursor;
2361
2362                                         // delete old row
2363                                         removeRow(old_cursor.row());
2364                                         if (ownerParagraph() == old_cursor.par()) {
2365                                                 ownerParagraph(ownerParagraph()->next());
2366                                         }
2367                                         // delete old par
2368                                         delete old_cursor.par();
2369                                         
2370                                         /* Breakagain the next par. Needed
2371                                          * because of the parindent that
2372                                          * can occur or dissappear. The
2373                                          * next row can change its height,
2374                                          * if there is another layout before */
2375                                         if (refresh_row->next()) {
2376                                                 breakAgain(bview, refresh_row->next());
2377                                                 updateCounters(bview, refresh_row);
2378                                         }
2379                                         setHeightOfRow(bview, refresh_row);
2380                                 } else {
2381                                         refresh_row = old_cursor.row()->next();
2382                                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2383                                         
2384                                         tmpcursor = cursor;
2385                                         cursor = old_cursor; // that undo can restore the right cursor position
2386                                         Paragraph * endpar = old_cursor.par()->next();
2387                                         if (endpar && endpar->getDepth()) {
2388                                                 while (endpar && endpar->getDepth()) {
2389                                                         endpar = endpar->next();
2390                                                 }
2391                                         }
2392                                         setUndo(bview, Undo::DELETE,
2393                                                 old_cursor.par(),
2394                                                 endpar);
2395                                         cursor = tmpcursor;
2396
2397                                         // delete old row
2398                                         removeRow(old_cursor.row());
2399                                         // delete old par
2400                                         if (ownerParagraph() == old_cursor.par()) {
2401                                                 ownerParagraph(ownerParagraph()->next());
2402                                         }
2403
2404                                         delete old_cursor.par();
2405                                         
2406                                         /* Breakagain the next par. Needed
2407                                            because of the parindent that can
2408                                            occur or dissappear.
2409                                            The next row can change its height,
2410                                            if there is another layout before
2411                                         */ 
2412                                         if (refresh_row) {
2413                                                 breakAgain(bview, refresh_row);
2414                                                 updateCounters(bview, refresh_row->previous());
2415                                         }
2416                                 }
2417                                 
2418                                 // correct cursor y
2419
2420                                 setCursorIntern(bview, cursor.par(), cursor.pos());
2421
2422                                 if (selection.cursor.par()  == old_cursor.par()
2423                                     && selection.cursor.pos() == selection.cursor.pos()) {
2424                                         // correct selection
2425                                         selection.cursor = cursor;
2426                                 }
2427                 }
2428                 if (!deleted) {
2429                         if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2430                                 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2431                                 // correct cursor y
2432                                 setCursorIntern(bview, cursor.par(), cursor.pos());
2433                                 selection.cursor = cursor;
2434                         }
2435                 }
2436         }
2437 }
2438
2439
2440 Paragraph * LyXText::getParFromID(int id) const
2441 {
2442         if (id < 0)
2443                 return 0;
2444         Paragraph * result = firstParagraph();
2445         Paragraph * ires = 0;
2446         while (result && result->id() != id) {
2447                 if ((ires = result->getParFromID(id)))
2448                         return ires;
2449                 result = result->next();
2450         }
2451         return result;
2452 }
2453
2454 void LyXText::toggleAppendix(BufferView * bview)
2455 {
2456         Paragraph * par = cursor.par();
2457         bool start = !par->params().startOfAppendix();
2458
2459         // ensure that we have only one start_of_appendix in this document
2460         Paragraph * tmp = firstParagraph();
2461         for (; tmp; tmp = tmp->next())
2462                 tmp->params().startOfAppendix(false);
2463
2464         par->params().startOfAppendix(start);
2465
2466         // we can set the refreshing parameters now
2467         status(bview, LyXText::NEED_MORE_REFRESH);
2468         refresh_y = 0;
2469         refresh_row = 0; // not needed for full update
2470         updateCounters(bview, 0);
2471         setCursor(bview, cursor.par(), cursor.pos());
2472 }
2473
2474
2475 Paragraph * LyXText::ownerParagraph() const
2476 {
2477         if (inset_owner)
2478                 return inset_owner->paragraph();
2479
2480         return bv_owner->buffer()->paragraph;
2481 }
2482
2483
2484 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2485 {
2486         if (inset_owner)
2487                 inset_owner->paragraph(p);
2488         else
2489                 bv_owner->buffer()->paragraph = p;
2490         return 0;
2491 }
2492
2493 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2494 {
2495         Paragraph * op = getParFromID(id);
2496         if (op && op->InInset()) {
2497                 static_cast<InsetText *>(op->InInset())->paragraph(p);
2498         } else {
2499                 if (inset_owner)
2500                         inset_owner->paragraph(p);
2501                 else
2502                         bv_owner->buffer()->paragraph = p;
2503         }
2504         return 0;
2505 }
2506
2507
2508 LyXText::text_status LyXText::status() const
2509 {
2510         return status_;
2511 }
2512
2513
2514 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2515 {
2516         if ((status_ != NEED_MORE_REFRESH) ||
2517                 (status_ == NEED_MORE_REFRESH) && (st != NEED_VERY_LITTLE_REFRESH))
2518         {
2519                 status_ = st;
2520                 if (inset_owner && st != UNCHANGED)
2521                         bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2522         }
2523 }