]> git.lyx.org Git - lyx.git/blob - src/text2.C
bug 183
[lyx.git] / src / text2.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *       
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation "lyxtext.h"
15 #endif
16
17 #include "lyxtext.h"
18 #include "LString.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
21 #include "LyXView.h"
22 #include "undo_funcs.h"
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "lyx_gui_misc.h"
26 #include "gettext.h"
27 #include "BufferView.h"
28 #include "LyXView.h"
29 #include "CutAndPaste.h"
30 #include "Painter.h"
31 #include "font.h"
32 #include "debug.h"
33 #include "lyxrc.h"
34 #include "lyxrow.h"
35 #include "FloatList.h"
36 #include "language.h"
37 #include "ParagraphParameters.h"
38
39 #include "insets/inseterror.h"
40 #include "insets/insetbib.h"
41 #include "insets/insetspecialchar.h"
42 #include "insets/insettext.h"
43 #include "insets/insetfloat.h"
44
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
48
49 using std::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 ownerParagrah() so the paragraph inside the row is NOT
867                 // my really first par anymore. Got it Lars ;) (Jug 20011206)
868                 first_phys_par = ownerParagraph();
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         // I don't know if this is necessary here (Jug 20020102)
1688         setUndo(bview, Undo::INSERT, 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 the selection onto the X clipboard, from an explicit copy request
1800         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1801
1802         // this doesnt make sense, if there is no selection
1803         if (!selection.set())
1804                 return;
1805
1806         // ok we have a selection. This is always between selection.start
1807         // and sel_end cursor
1808
1809         // copy behind a space if there is one
1810         while (selection.start.par()->size() > selection.start.pos()
1811                && selection.start.par()->isLineSeparator(selection.start.pos())
1812                && (selection.start.par() != selection.end.par()
1813                    || selection.start.pos() < selection.end.pos()))
1814                 selection.start.pos(selection.start.pos() + 1); 
1815
1816         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1817                                    selection.start.pos(), selection.end.pos(),
1818                                    bview->buffer()->params.textclass);
1819 }
1820
1821
1822 void LyXText::pasteSelection(BufferView * bview)
1823 {
1824         // this does not make sense, if there is nothing to paste
1825         if (!CutAndPaste::checkPastePossible(cursor.par()))
1826                 return;
1827
1828         setUndo(bview, Undo::INSERT,
1829                 cursor.par(), cursor.par()->next()); 
1830
1831         Paragraph * endpar;
1832         Paragraph * actpar = cursor.par();
1833         int pos = cursor.pos();
1834
1835         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1836                                     bview->buffer()->params.textclass);
1837     
1838         redoParagraphs(bview, cursor, endpar);
1839         
1840         setCursor(bview, cursor.par(), cursor.pos());
1841         clearSelection();
1842    
1843         setCursor(bview, actpar, pos);
1844         updateCounters(bview, cursor.row());
1845 }
1846
1847
1848 // sets the selection over the number of characters of string, no check!!
1849 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1850 {
1851         if (str.empty())
1852                 return;
1853         
1854         selection.cursor = cursor;
1855         for (string::size_type i = 0; i < str.length(); ++i)
1856                 cursorRight(bview);
1857         setSelection(bview);
1858 }
1859
1860
1861 // simple replacing. The font of the first selected character is used
1862 void LyXText::replaceSelectionWithString(BufferView * bview,
1863                                          string const & str)
1864 {
1865         setCursorParUndo(bview);
1866         freezeUndo();
1867
1868         if (!selection.set()) { // create a dummy selection
1869                 selection.end = cursor;
1870                 selection.start = cursor;
1871         }
1872
1873         // Get font setting before we cut
1874         pos_type pos = selection.end.pos();
1875         LyXFont const font = selection.start.par()
1876                 ->getFontSettings(bview->buffer()->params,
1877                                   selection.start.pos());
1878
1879         // Insert the new string
1880         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1881                 selection.end.par()->insertChar(pos, (*cit), font);
1882                 ++pos;
1883         }
1884         
1885         // Cut the selection
1886         cutSelection(bview, true, false);
1887
1888         unFreezeUndo();
1889 }
1890
1891
1892 // needed to insert the selection
1893 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1894 {
1895         Paragraph * par = cursor.par();
1896         pos_type pos = cursor.pos();
1897         Paragraph * endpar = cursor.par()->next();
1898         
1899         setCursorParUndo(bview);
1900         
1901         // only to be sure, should not be neccessary
1902         clearSelection();
1903         
1904         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1905
1906         redoParagraphs(bview, cursor, endpar);
1907         setCursor(bview, cursor.par(), cursor.pos());
1908         selection.cursor = cursor;
1909         setCursor(bview, par, pos);
1910         setSelection(bview);
1911 }
1912
1913
1914 // turns double-CR to single CR, others where converted into one
1915 // blank. Then InsertStringAsLines is called
1916 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1917 {
1918         string linestr(str);
1919         bool newline_inserted = false;
1920         for (string::size_type i = 0; i < linestr.length(); ++i) {
1921                 if (linestr[i] == '\n') {
1922                         if (newline_inserted) {
1923                                 // we know that \r will be ignored by
1924                                 // InsertStringA. Of course, it is a dirty
1925                                 // trick, but it works...
1926                                 linestr[i - 1] = '\r';
1927                                 linestr[i] = '\n';
1928                         } else {
1929                                 linestr[i] = ' ';
1930                                 newline_inserted = true;
1931                         } 
1932                 } else if (IsPrintable(linestr[i])) {
1933                         newline_inserted = false;
1934                 }
1935         }
1936         insertStringAsLines(bview, linestr);
1937 }
1938
1939
1940 bool LyXText::gotoNextInset(BufferView * bview,
1941                             std::vector<Inset::Code> const & codes,
1942                             string const & contents) const
1943 {
1944         LyXCursor res = cursor;
1945         Inset * inset;
1946         do {
1947                 if (res.pos() < res.par()->size() - 1) {
1948                         res.pos(res.pos() + 1);
1949                 } else  {
1950                         res.par(res.par()->next());
1951                         res.pos(0);
1952                 }
1953       
1954         } while (res.par() && 
1955                  !(res.par()->isInset(res.pos())
1956                    && (inset = res.par()->getInset(res.pos())) != 0
1957                    && find(codes.begin(), codes.end(), inset->lyxCode())
1958                    != codes.end()
1959                    && (contents.empty() ||
1960                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1961                        == contents)));
1962
1963         if (res.par()) {
1964                 setCursor(bview, res.par(), res.pos(), false);
1965                 return true;
1966         }
1967         return false;
1968 }
1969
1970
1971 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1972                              pos_type pos)
1973 {
1974         LyXCursor tmpcursor;                    
1975
1976         int y = 0;
1977         pos_type z;
1978         Row * row = getRow(par, pos, y);
1979         
1980         // is there a break one row above
1981         if (row->previous() && row->previous()->par() == row->par()) {
1982                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1983                 if (z >= row->pos()) {
1984                         // set the dimensions of the row above
1985                         y -= row->previous()->height();
1986                         refresh_y = y;
1987                         refresh_row = row->previous();
1988                         status(bview, LyXText::NEED_MORE_REFRESH);
1989                         
1990                         breakAgain(bview, row->previous());
1991                         
1992                         // set the cursor again. Otherwise
1993                         // dangling pointers are possible
1994                         setCursor(bview, cursor.par(), cursor.pos(),
1995                                   false, cursor.boundary());
1996                         selection.cursor = cursor;
1997                         return;
1998                 }
1999         }
2000
2001         int const tmpheight = row->height();
2002         pos_type const tmplast = rowLast(row);
2003         refresh_y = y;
2004         refresh_row = row;
2005         
2006         breakAgain(bview, row);
2007         if (row->height() == tmpheight && rowLast(row) == tmplast)
2008                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2009         else
2010                 status(bview, LyXText::NEED_MORE_REFRESH); 
2011         
2012         // check the special right address boxes
2013         if (textclasslist.Style(bview->buffer()->params.textclass,
2014                                 par->getLayout()).margintype
2015             == MARGIN_RIGHT_ADDRESS_BOX)
2016         {
2017                 tmpcursor.par(par);
2018                 tmpcursor.row(row);
2019                 tmpcursor.y(y);
2020                 tmpcursor.x(0);
2021                 tmpcursor.x_fix(0);
2022                 tmpcursor.pos(pos);
2023                 redoDrawingOfParagraph(bview, tmpcursor); 
2024         }
2025
2026         // set the cursor again. Otherwise dangling pointers are possible
2027         // also set the selection
2028    
2029         if (selection.set()) {
2030                 tmpcursor = cursor;
2031                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2032                                 false, selection.cursor.boundary());
2033                 selection.cursor = cursor; 
2034                 setCursorIntern(bview, selection.start.par(),
2035                                 selection.start.pos(),
2036                                 false, selection.start.boundary());
2037                 selection.start = cursor; 
2038                 setCursorIntern(bview, selection.end.par(),
2039                                 selection.end.pos(),
2040                                 false, selection.end.boundary());
2041                 selection.end = cursor; 
2042                 setCursorIntern(bview, last_sel_cursor.par(),
2043                                 last_sel_cursor.pos(),
2044                                 false, last_sel_cursor.boundary());
2045                 last_sel_cursor = cursor; 
2046                 cursor = tmpcursor;
2047         }
2048         setCursorIntern(bview, cursor.par(), cursor.pos(),
2049                         false, cursor.boundary());
2050 }
2051
2052
2053 // returns false if inset wasn't found
2054 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2055 {
2056         // first check the current paragraph
2057         int pos = cursor.par()->getPositionOfInset(inset);
2058         if (pos != -1) {
2059                 checkParagraph(bview, cursor.par(), pos);
2060                 return true;
2061         }
2062   
2063         // check every paragraph
2064   
2065         Paragraph * par = ownerParagraph();
2066         do {
2067                 pos = par->getPositionOfInset(inset);
2068                 if (pos != -1) {
2069                         checkParagraph(bview, par, pos);
2070                         return true;
2071                 }
2072                 par = par->next();
2073         } while (par);
2074   
2075         return false;
2076 }
2077
2078
2079 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2080                         pos_type pos, 
2081                         bool setfont, bool boundary) const
2082 {
2083         LyXCursor old_cursor = cursor;
2084         setCursorIntern(bview, par, pos, setfont, boundary);
2085         return deleteEmptyParagraphMechanism(bview, old_cursor);
2086 }
2087
2088
2089 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2090                         pos_type pos, bool boundary) const
2091 {
2092         lyx::Assert(par);
2093         lyx::Assert(bview);
2094         
2095         cur.par(par);
2096         cur.pos(pos);
2097         cur.boundary(boundary);
2098
2099         // get the cursor y position in text
2100         int y = 0;
2101         Row * row = getRow(par, pos, y);
2102         // y is now the beginning of the cursor row
2103         y += row->baseline();
2104         // y is now the cursor baseline 
2105         cur.y(y);
2106
2107         // now get the cursors x position
2108         float x;
2109         float fill_separator;
2110         float fill_hfill;
2111         float fill_label_hfill;
2112         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2113                        fill_label_hfill);
2114         pos_type cursor_vpos = 0;
2115         pos_type last = rowLastPrintable(row);
2116
2117         if (pos > last + 1) {
2118                 // This shouldn't happen.
2119                 pos = last + 1;
2120                 cur.pos(pos);
2121         } else if (pos < row->pos()) {
2122                 pos = row->pos();
2123                 cur.pos(pos);
2124         }
2125         
2126         if (last < row->pos())
2127                 cursor_vpos = row->pos();
2128         else if (pos > last && !boundary)
2129                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2130                         ? row->pos() : last + 1; 
2131         else if (pos > row->pos() &&
2132                  (pos > last || boundary))
2133                 /// Place cursor after char at (logical) position pos - 1
2134                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2135                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2136         else
2137                 /// Place cursor before char at (logical) position pos
2138                 cursor_vpos = (bidi_level(pos) % 2 == 0)
2139                         ? log2vis(pos) : log2vis(pos) + 1;
2140         
2141         pos_type main_body =
2142                 beginningOfMainBody(bview->buffer(), row->par());
2143         if ((main_body > 0) &&
2144             ((main_body-1 > last) || 
2145              !row->par()->isLineSeparator(main_body-1)))
2146                 main_body = 0;
2147         
2148         for (pos_type vpos = row->pos();
2149              vpos < cursor_vpos; ++vpos) {
2150                 pos = vis2log(vpos);
2151                 if (main_body > 0 && pos == main_body - 1) {
2152                         x += fill_label_hfill +
2153                                 lyxfont::width(textclasslist.Style(
2154                                         bview->buffer()->params.textclass,
2155                                         row->par()->getLayout())
2156                                                .labelsep,
2157                                                getLabelFont(bview->buffer(), row->par()));
2158                         if (row->par()->isLineSeparator(main_body-1))
2159                                 x -= singleWidth(bview, row->par(),main_body-1);
2160                 }
2161                 if (hfillExpansion(bview->buffer(), row, pos)) {
2162                         x += singleWidth(bview, row->par(), pos);
2163                         if (pos >= main_body)
2164                                 x += fill_hfill;
2165                         else 
2166                                 x += fill_label_hfill;
2167                 } else if (row->par()->isSeparator(pos)) {
2168                         x += singleWidth(bview, row->par(), pos);
2169                         if (pos >= main_body)
2170                                 x += fill_separator;
2171                 } else
2172                         x += singleWidth(bview, row->par(), pos);
2173         }
2174         
2175         cur.x(int(x));
2176         cur.x_fix(cur.x());
2177         cur.row(row);
2178 }
2179
2180
2181 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2182                               pos_type pos, bool setfont, bool boundary) const
2183 {
2184         InsetText * it = static_cast<InsetText *>(par->inInset());
2185         if (it) {
2186                 if (it != inset_owner) {
2187                         lyxerr << "InsetText   is " << it << endl;
2188                         lyxerr << "inset_owner is " << inset_owner << endl;
2189 #warning I believe this code is wrong. (Lgb)
2190 #warning Jürgen, have a look at this. (Lgb)
2191 #warning Hmmm, I guess you are right but we
2192 #warning should verify when this is needed
2193                         // Jürgen, would you like to have a look?
2194                         // I guess we need to move the outer cursor
2195                         // and open and lock the inset (bla bla bla)
2196                         // stuff I don't know... so can you have a look?
2197                         // (Lgb)
2198                         // I moved the lyxerr stuff in here so we can see if
2199                         // this is actually really needed and where!
2200                         // (Jug)
2201                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2202                         return;
2203                 }
2204         }
2205         
2206         setCursor(bview, cursor, par, pos, boundary);
2207         if (setfont)
2208                 setCurrentFont(bview);
2209 }
2210
2211
2212 void LyXText::setCurrentFont(BufferView * bview) const
2213 {
2214         pos_type pos = cursor.pos();
2215         if (cursor.boundary() && pos > 0)
2216                 --pos;
2217
2218         if (pos > 0) {
2219                 if (pos == cursor.par()->size())
2220                         --pos;
2221                 else // potentional bug... BUG (Lgb)
2222                         if (cursor.par()->isSeparator(pos)) {
2223                                 if (pos > cursor.row()->pos() &&
2224                                     bidi_level(pos) % 2 == 
2225                                     bidi_level(pos - 1) % 2)
2226                                         --pos;
2227                                 else if (pos + 1 < cursor.par()->size())
2228                                         ++pos;
2229                         }
2230         }
2231
2232         current_font =
2233                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2234         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2235
2236         if (cursor.pos() == cursor.par()->size() &&
2237             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2238             !cursor.boundary()) {
2239                 Language const * lang =
2240                         cursor.par()->getParLanguage(bview->buffer()->params);
2241                 current_font.setLanguage(lang);
2242                 current_font.setNumber(LyXFont::OFF);
2243                 real_current_font.setLanguage(lang);
2244                 real_current_font.setNumber(LyXFont::OFF);
2245         }
2246 }
2247
2248
2249 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2250 {
2251         LyXCursor old_cursor = cursor;
2252
2253         setCursorFromCoordinates(bview, cursor, x, y);
2254         setCurrentFont(bview);
2255         deleteEmptyParagraphMechanism(bview, old_cursor);
2256 }
2257
2258
2259 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2260                                        int x, int y) const
2261 {
2262         // Get the row first.
2263    
2264         Row * row = getRowNearY(y);
2265         bool bound = false;
2266         pos_type const column = getColumnNearX(bview, row, x, bound);
2267         cur.par(row->par());
2268         cur.pos(row->pos() + column);
2269         cur.x(x);
2270         cur.y(y + row->baseline());
2271         cur.row(row);
2272         cur.boundary(bound);
2273 }
2274
2275
2276 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2277 {
2278         if (cursor.pos() > 0) {
2279                 bool boundary = cursor.boundary();
2280                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2281                 if (!internal && !boundary &&
2282                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2283                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2284         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2285                 Paragraph * par = cursor.par()->previous();
2286                 setCursor(bview, par, par->size());
2287         }
2288 }
2289
2290
2291 void LyXText::cursorRight(BufferView * bview, bool internal) const
2292 {
2293         if (!internal && cursor.boundary() &&
2294             !cursor.par()->isNewline(cursor.pos()))
2295                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2296         else if (cursor.pos() < cursor.par()->size()) {
2297                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2298                 if (!internal &&
2299                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2300                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2301         } else if (cursor.par()->next())
2302                 setCursor(bview, cursor.par()->next(), 0);
2303 }
2304
2305
2306 void LyXText::cursorUp(BufferView * bview) const
2307 {
2308         setCursorFromCoordinates(bview, cursor.x_fix(), 
2309                                  cursor.y() - cursor.row()->baseline() - 1);
2310 }
2311
2312
2313 void LyXText::cursorDown(BufferView * bview) const
2314 {
2315         setCursorFromCoordinates(bview, cursor.x_fix(), 
2316                                  cursor.y() - cursor.row()->baseline()
2317                                  + cursor.row()->height() + 1);
2318 }
2319
2320
2321 void LyXText::cursorUpParagraph(BufferView * bview) const
2322 {
2323         if (cursor.pos() > 0) {
2324                 setCursor(bview, cursor.par(), 0);
2325         }
2326         else if (cursor.par()->previous()) {
2327                 setCursor(bview, cursor.par()->previous(), 0);
2328         }
2329 }
2330
2331
2332 void LyXText::cursorDownParagraph(BufferView * bview) const
2333 {
2334         if (cursor.par()->next()) {
2335                 setCursor(bview, cursor.par()->next(), 0);
2336         } else {
2337                 setCursor(bview, cursor.par(), cursor.par()->size());
2338         }
2339 }
2340
2341 // fix the cursor `cur' after a characters has been deleted at `where'
2342 // position. Called by deleteEmptyParagraphMechanism
2343 void LyXText::fixCursorAfterDelete(BufferView * bview,
2344                                    LyXCursor & cur,
2345                                    LyXCursor const & where) const
2346 {
2347         // if cursor is not in the paragraph where the delete occured,
2348         // do nothing
2349         if (cur.par() != where.par())
2350                 return;
2351
2352         // if cursor position is after the place where the delete occured,
2353         // update it
2354         if (cur.pos() > where.pos())
2355                 cur.pos(cur.pos()-1);
2356
2357         // recompute row et al. for this cursor
2358         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2359 }
2360
2361
2362 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2363                                             LyXCursor const & old_cursor) const
2364 {
2365         // Would be wrong to delete anything if we have a selection.
2366         if (selection.set()) return false;
2367
2368         // We allow all kinds of "mumbo-jumbo" when freespacing.
2369         if (textclasslist.Style(bview->buffer()->params.textclass,
2370                                 old_cursor.par()->getLayout()).free_spacing
2371             || old_cursor.par()->isFreeSpacing())
2372         {
2373                 return false;
2374         }
2375         
2376         /* Ok I'll put some comments here about what is missing.
2377            I have fixed BackSpace (and thus Delete) to not delete
2378            double-spaces automagically. I have also changed Cut,
2379            Copy and Paste to hopefully do some sensible things.
2380            There are still some small problems that can lead to
2381            double spaces stored in the document file or space at
2382            the beginning of paragraphs. This happens if you have
2383            the cursor betwenn to spaces and then save. Or if you
2384            cut and paste and the selection have a space at the
2385            beginning and then save right after the paste. I am
2386            sure none of these are very hard to fix, but I will
2387            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2388            that I can get some feedback. (Lgb)
2389         */
2390
2391         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2392         // delete the LineSeparator.
2393         // MISSING
2394
2395         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2396         // delete the LineSeparator.
2397         // MISSING
2398
2399         // If the pos around the old_cursor were spaces, delete one of them.
2400         if (old_cursor.par() != cursor.par()
2401             || old_cursor.pos() != cursor.pos()) { 
2402                 // Only if the cursor has really moved
2403                 
2404                 if (old_cursor.pos() > 0
2405                     && old_cursor.pos() < old_cursor.par()->size()
2406                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2407                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2408                         old_cursor.par()->erase(old_cursor.pos() - 1);
2409                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2410
2411 #ifdef WITH_WARNINGS
2412 #warning This will not work anymore when we have multiple views of the same buffer
2413 // In this case, we will have to correct also the cursors held by
2414 // other bufferviews. It will probably be easier to do that in a more
2415 // automated way in LyXCursor code. (JMarc 26/09/2001)
2416 #endif
2417                         // correct all cursors held by the LyXText
2418                         fixCursorAfterDelete(bview, cursor, old_cursor);
2419                         fixCursorAfterDelete(bview, selection.cursor,
2420                                              old_cursor);
2421                         fixCursorAfterDelete(bview, selection.start,
2422                                              old_cursor);
2423                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2424                         fixCursorAfterDelete(bview, last_sel_cursor,
2425                                              old_cursor);
2426                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2427                         fixCursorAfterDelete(bview, toggle_end_cursor,
2428                                              old_cursor);
2429                         return false;
2430                 }
2431         }
2432
2433         // don't delete anything if this is the ONLY paragraph!
2434         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2435                 return false;
2436         
2437         // Do not delete empty paragraphs with keepempty set.
2438         if ((textclasslist.Style(bview->buffer()->params.textclass,
2439                                  old_cursor.par()->getLayout())).keepempty)
2440                 return false;
2441
2442         // only do our magic if we changed paragraph
2443         if (old_cursor.par() == cursor.par()) 
2444                 return false;
2445         
2446         // record if we have deleted a paragraph
2447         // we can't possibly have deleted a paragraph before this point
2448         bool deleted = false;
2449
2450         if ((old_cursor.par()->size() == 0
2451              || (old_cursor.par()->size() == 1
2452                  && old_cursor.par()->isLineSeparator(0)))) {
2453                 // ok, we will delete anything
2454                 LyXCursor tmpcursor;
2455                 
2456                 // make sure that you do not delete any environments
2457                 status(bview, LyXText::NEED_MORE_REFRESH);
2458                 deleted = true;
2459                                 
2460                 if (old_cursor.row()->previous()) {
2461                         refresh_row = old_cursor.row()->previous();
2462                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2463                         tmpcursor = cursor;
2464                         cursor = old_cursor; // that undo can restore the right cursor position
2465                         Paragraph * endpar = old_cursor.par()->next();
2466                         if (endpar && endpar->getDepth()) {
2467                                 while (endpar && endpar->getDepth()) {
2468                                         endpar = endpar->next();
2469                                 }
2470                         }
2471                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2472                         cursor = tmpcursor;
2473
2474                         // delete old row
2475                         removeRow(old_cursor.row());
2476                         if (ownerParagraph() == old_cursor.par()) {
2477                                 ownerParagraph(ownerParagraph()->next());
2478                         }
2479                         // delete old par
2480                         delete old_cursor.par();
2481                                         
2482                         /* Breakagain the next par. Needed because of
2483                          * the parindent that can occur or dissappear.
2484                          * The next row can change its height, if
2485                          * there is another layout before */
2486                         if (refresh_row->next()) {
2487                                 breakAgain(bview, refresh_row->next());
2488                                 updateCounters(bview, refresh_row);
2489                         }
2490                         setHeightOfRow(bview, refresh_row);
2491                 } else {
2492                         refresh_row = old_cursor.row()->next();
2493                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2494                                         
2495                         tmpcursor = cursor;
2496                         cursor = old_cursor; // that undo can restore the right cursor position
2497                         Paragraph * endpar = old_cursor.par()->next();
2498                         if (endpar && endpar->getDepth()) {
2499                                 while (endpar && endpar->getDepth()) {
2500                                         endpar = endpar->next();
2501                                 }
2502                         }
2503                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2504                         cursor = tmpcursor;
2505
2506                         // delete old row
2507                         removeRow(old_cursor.row());
2508                         // delete old par
2509                         if (ownerParagraph() == old_cursor.par()) {
2510                                 ownerParagraph(ownerParagraph()->next());
2511                         }
2512
2513                         delete old_cursor.par();
2514                                         
2515                         /* Breakagain the next par. Needed because of
2516                            the parindent that can occur or dissappear.
2517                            The next row can change its height, if
2518                            there is another layout before */
2519                         if (refresh_row) {
2520                                 breakAgain(bview, refresh_row);
2521                                 updateCounters(bview, refresh_row->previous());
2522                         }
2523                 }
2524                                 
2525                 // correct cursor y
2526                 setCursorIntern(bview, cursor.par(), cursor.pos());
2527
2528                 if (selection.cursor.par()  == old_cursor.par()
2529                     && selection.cursor.pos() == old_cursor.pos()) {
2530                         // correct selection
2531                         selection.cursor = cursor;
2532                 }
2533         }
2534         if (!deleted) {
2535                 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2536                         redoParagraphs(bview, old_cursor,
2537                                        old_cursor.par()->next());
2538                         // correct cursor y
2539                         setCursorIntern(bview, cursor.par(), cursor.pos());
2540                         selection.cursor = cursor;
2541                 }
2542         }
2543         return deleted;
2544 }
2545
2546
2547 void LyXText::toggleAppendix(BufferView * bview)
2548 {
2549         Paragraph * par = cursor.par();
2550         bool start = !par->params().startOfAppendix();
2551
2552         // ensure that we have only one start_of_appendix in this document
2553         Paragraph * tmp = ownerParagraph();
2554         for (; tmp; tmp = tmp->next()) {
2555                 tmp->params().startOfAppendix(false);
2556         }
2557         
2558         par->params().startOfAppendix(start);
2559
2560         // we can set the refreshing parameters now
2561         status(bview, LyXText::NEED_MORE_REFRESH);
2562         refresh_y = 0;
2563         refresh_row = 0; // not needed for full update
2564         updateCounters(bview, 0);
2565         setCursor(bview, cursor.par(), cursor.pos());
2566 }
2567
2568
2569 Paragraph * LyXText::ownerParagraph() const
2570 {
2571         if (inset_owner) {
2572                 return inset_owner->paragraph();
2573         }
2574         return bv_owner->buffer()->paragraph;
2575 }
2576
2577
2578 void LyXText::ownerParagraph(Paragraph * p) const
2579 {
2580         if (inset_owner) {
2581                 inset_owner->paragraph(p);
2582         } else {
2583                 bv_owner->buffer()->paragraph = p;
2584         }
2585 }
2586
2587
2588 void LyXText::ownerParagraph(int id, Paragraph * p) const
2589 {
2590         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2591         if (op && op->inInset()) {
2592                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2593         } else {
2594                 ownerParagraph(p);
2595         }
2596 }
2597
2598
2599 LyXText::text_status LyXText::status() const
2600 {
2601         return status_;
2602 }
2603
2604
2605 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2606 {
2607         // well as much as I know && binds more then || so the above and the
2608         // below are identical (this for your known use of parentesis!)
2609         // Now some explanation:
2610         // We should only go up with refreshing code so this means that if
2611         // we have a MORE refresh we should never set it to LITTLE if we still
2612         // didn't handle it (and then it will be UNCHANGED. Now as long as
2613         // we stay inside one LyXText this may work but we need to tell the
2614         // outermost LyXText that it should REALLY draw us if there is some
2615         // change in a Inset::LyXText. So you see that when we are inside a
2616         // inset's LyXText we give the LITTLE to the outermost LyXText to
2617         // tell'em that it should redraw the actual row (where the inset
2618         // resides! Capito?!
2619
2620         if ((status_ != NEED_MORE_REFRESH)
2621             || (status_ == NEED_MORE_REFRESH
2622                 && st != NEED_VERY_LITTLE_REFRESH))
2623         {
2624                 status_ = st;
2625                 if (inset_owner && st != UNCHANGED) {
2626                         bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2627                         if (!bview->text->refresh_row) {
2628                                 bview->text->refresh_row = bview->text->cursor.row();
2629                                 bview->text->refresh_y = bview->text->cursor.y() -
2630                                         bview->text->cursor.row()->baseline();
2631                         }
2632                 }
2633         }
2634 }