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