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