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