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