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