]> git.lyx.org Git - lyx.git/blob - src/text2.C
rename commandtags.h to lfuns.h and renumber/cleanup. Rebuild the tree !
[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), anchor_row_(0), anchor_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), anchor_row_(0), anchor_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)
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)
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 (anchor_row_ == row) {
335                 if (row_prev) {
336                         anchor_row_ = row_prev;
337                         anchor_row_offset_ = 0;
338                 } else {
339                         anchor_row_ = row->next();
340                         anchor_row_offset_ -= row->height();
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)
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)
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)
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()
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()
955 {
956         setCursor(cursor.par(), cursor.row()->pos());
957 }
958
959
960 void LyXText::cursorEnd()
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()
979 {
980         while (cursor.par()->previous())
981                 cursor.par(cursor.par()->previous());
982         setCursor(cursor.par(), 0);
983 }
984
985
986 void LyXText::cursorBottom()
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);
1133 }
1134
1135
1136 // set the counter of a paragraph. This includes the labels
1137 void LyXText::setCounter(Buffer const * buf, Paragraph * par)
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()
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)
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)
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         //if the cursor is in a visible row, anchor to it
1797         int topy = top_y();
1798         if (topy < y && y < topy + bv()->workHeight()) 
1799                 anchor_row(row);
1800 }
1801
1802
1803 float LyXText::getCursorX(Row * row,
1804                           pos_type pos, pos_type last, bool boundary) const
1805 {
1806         pos_type cursor_vpos = 0;
1807         float x;
1808         float fill_separator;
1809         float fill_hfill;
1810         float fill_label_hfill;
1811         // This call HAS to be here because of the BidiTables!!!
1812         prepareToPrint(row, x, fill_separator, fill_hfill,
1813                        fill_label_hfill);
1814
1815         if (last < row->pos())
1816                 cursor_vpos = row->pos();
1817         else if (pos > last && !boundary)
1818                 cursor_vpos = (row->par()->isRightToLeftPar(bv()->buffer()->params))
1819                         ? row->pos() : last + 1;
1820         else if (pos > row->pos() &&
1821                  (pos > last || boundary))
1822                 /// Place cursor after char at (logical) position pos - 1
1823                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1824                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1825         else
1826                 /// Place cursor before char at (logical) position pos
1827                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1828                         ? log2vis(pos) : log2vis(pos) + 1;
1829
1830         pos_type body_pos = row->par()->beginningOfBody();
1831         if ((body_pos > 0) &&
1832             ((body_pos-1 > last) ||
1833              !row->par()->isLineSeparator(body_pos - 1)))
1834                 body_pos = 0;
1835
1836         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1837                 pos_type pos = vis2log(vpos);
1838                 if (body_pos > 0 && pos == body_pos - 1) {
1839                         x += fill_label_hfill +
1840                                 font_metrics::width(
1841                                         row->par()->layout()->labelsep,
1842                                         getLabelFont(bv()->buffer(),
1843                                                      row->par()));
1844                         if (row->par()->isLineSeparator(body_pos - 1))
1845                                 x -= singleWidth(
1846                                                  row->par(), body_pos - 1);
1847                 }
1848                 if (row->hfillExpansion(pos)) {
1849                         x += singleWidth(row->par(), pos);
1850                         if (pos >= body_pos)
1851                                 x += fill_hfill;
1852                         else
1853                                 x += fill_label_hfill;
1854                 } else if (row->par()->isSeparator(pos)) {
1855                         x += singleWidth(row->par(), pos);
1856                         if (pos >= body_pos)
1857                                 x += fill_separator;
1858                 } else
1859                         x += singleWidth(row->par(), pos);
1860         }
1861         return x;
1862 }
1863
1864
1865 void LyXText::setCursorIntern(Paragraph * par,
1866                               pos_type pos, bool setfont, bool boundary)
1867 {
1868         InsetText * it = static_cast<InsetText *>(par->inInset());
1869         if (it) {
1870                 if (it != inset_owner) {
1871                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1872                                               << endl
1873                                               << "inset_owner is "
1874                                               << inset_owner << endl;
1875 #ifdef WITH_WARNINGS
1876 #warning I believe this code is wrong. (Lgb)
1877 #warning Jürgen, have a look at this. (Lgb)
1878 #warning Hmmm, I guess you are right but we
1879 #warning should verify when this is needed
1880 #endif
1881                         // Jürgen, would you like to have a look?
1882                         // I guess we need to move the outer cursor
1883                         // and open and lock the inset (bla bla bla)
1884                         // stuff I don't know... so can you have a look?
1885                         // (Lgb)
1886                         // I moved the lyxerr stuff in here so we can see if
1887                         // this is actually really needed and where!
1888                         // (Jug)
1889                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1890                         return;
1891                 }
1892         }
1893
1894         setCursor(cursor, par, pos, boundary);
1895         if (setfont)
1896                 setCurrentFont();
1897 }
1898
1899
1900 void LyXText::setCurrentFont()
1901 {
1902         pos_type pos = cursor.pos();
1903         if (cursor.boundary() && pos > 0)
1904                 --pos;
1905
1906         if (pos > 0) {
1907                 if (pos == cursor.par()->size())
1908                         --pos;
1909                 else // potentional bug... BUG (Lgb)
1910                         if (cursor.par()->isSeparator(pos)) {
1911                                 if (pos > cursor.row()->pos() &&
1912                                     bidi_level(pos) % 2 ==
1913                                     bidi_level(pos - 1) % 2)
1914                                         --pos;
1915                                 else if (pos + 1 < cursor.par()->size())
1916                                         ++pos;
1917                         }
1918         }
1919
1920         current_font =
1921                 cursor.par()->getFontSettings(bv()->buffer()->params, pos);
1922         real_current_font = getFont(bv()->buffer(), cursor.par(), pos);
1923
1924         if (cursor.pos() == cursor.par()->size() &&
1925             isBoundary(bv()->buffer(), cursor.par(), cursor.pos()) &&
1926             !cursor.boundary()) {
1927                 Language const * lang =
1928                         cursor.par()->getParLanguage(bv()->buffer()->params);
1929                 current_font.setLanguage(lang);
1930                 current_font.setNumber(LyXFont::OFF);
1931                 real_current_font.setLanguage(lang);
1932                 real_current_font.setNumber(LyXFont::OFF);
1933         }
1934 }
1935
1936
1937 // returns the column near the specified x-coordinate of the row
1938 // x is set to the real beginning of this column
1939 pos_type
1940 LyXText::getColumnNearX(Row * row, int & x,
1941                         bool & boundary) const
1942 {
1943         float tmpx = 0.0;
1944         float fill_separator;
1945         float fill_hfill;
1946         float fill_label_hfill;
1947
1948         prepareToPrint(row, tmpx, fill_separator,
1949                        fill_hfill, fill_label_hfill);
1950
1951         pos_type vc = row->pos();
1952         pos_type last = row->lastPrintablePos();
1953         pos_type c = 0;
1954
1955         LyXLayout_ptr const & layout = row->par()->layout();
1956
1957         bool left_side = false;
1958
1959         pos_type body_pos = row->par()->beginningOfBody();
1960         float last_tmpx = tmpx;
1961
1962         if (body_pos > 0 &&
1963             (body_pos - 1 > last ||
1964              !row->par()->isLineSeparator(body_pos - 1)))
1965                 body_pos = 0;
1966
1967         // check for empty row
1968         if (!row->par()->size()) {
1969                 x = int(tmpx);
1970                 return 0;
1971         }
1972
1973         while (vc <= last && tmpx <= x) {
1974                 c = vis2log(vc);
1975                 last_tmpx = tmpx;
1976                 if (body_pos > 0 && c == body_pos-1) {
1977                         tmpx += fill_label_hfill +
1978                                 font_metrics::width(layout->labelsep,
1979                                                getLabelFont(bv()->buffer(), row->par()));
1980                         if (row->par()->isLineSeparator(body_pos - 1))
1981                                 tmpx -= singleWidth(row->par(), body_pos-1);
1982                 }
1983
1984                 if (row->hfillExpansion(c)) {
1985                         tmpx += singleWidth(row->par(), c);
1986                         if (c >= body_pos)
1987                                 tmpx += fill_hfill;
1988                         else
1989                                 tmpx += fill_label_hfill;
1990                 } else if (row->par()->isSeparator(c)) {
1991                         tmpx += singleWidth(row->par(), c);
1992                         if (c >= body_pos)
1993                                 tmpx+= fill_separator;
1994                 } else {
1995                         tmpx += singleWidth(row->par(), c);
1996                 }
1997                 ++vc;
1998         }
1999
2000         if ((tmpx + last_tmpx) / 2 > x) {
2001                 tmpx = last_tmpx;
2002                 left_side = true;
2003         }
2004
2005         if (vc > last + 1)  // This shouldn't happen.
2006                 vc = last + 1;
2007
2008         boundary = false;
2009         bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
2010                                          // some speedup if rtl_support=false
2011                 && (!row->next() || row->next()->par() != row->par());
2012         bool const rtl = (lastrow)
2013                 ? row->par()->isRightToLeftPar(bv()->buffer()->params)
2014                 : false; // If lastrow is false, we don't need to compute
2015                          // the value of rtl.
2016
2017         if (lastrow &&
2018                  ((rtl &&  left_side && vc == row->pos() && x < tmpx - 5) ||
2019                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
2020                 c = last + 1;
2021         else if (vc == row->pos()) {
2022                 c = vis2log(vc);
2023                 if (bidi_level(c) % 2 == 1)
2024                         ++c;
2025         } else {
2026                 c = vis2log(vc - 1);
2027                 bool const rtl = (bidi_level(c) % 2 == 1);
2028                 if (left_side == rtl) {
2029                         ++c;
2030                         boundary = isBoundary(bv()->buffer(), row->par(), c);
2031                 }
2032         }
2033
2034         if (row->pos() <= last && c > last
2035             && row->par()->isNewline(last)) {
2036                 if (bidi_level(last) % 2 == 0)
2037                         tmpx -= singleWidth(row->par(), last);
2038                 else
2039                         tmpx += singleWidth(row->par(), last);
2040                 c = last;
2041         }
2042
2043         c -= row->pos();
2044         x = int(tmpx);
2045         return c;
2046 }
2047
2048
2049 void LyXText::setCursorFromCoordinates(int x, int y)
2050 {
2051         LyXCursor old_cursor = cursor;
2052
2053         setCursorFromCoordinates(cursor, x, y);
2054         setCurrentFont();
2055         deleteEmptyParagraphMechanism(old_cursor);
2056 }
2057
2058
2059 namespace {
2060
2061         /**
2062          * return true if the cursor given is at the end of a row,
2063          * and the next row is filled by an inset that spans an entire
2064          * row.
2065          */
2066         bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2067                 if (!row.next())
2068                         return false;
2069                 Row const & next = *row.next();
2070
2071                 if (next.pos() != cur.pos() || next.par() != cur.par())
2072                         return false;
2073                 if (!cur.par()->isInset(cur.pos()))
2074                         return false;
2075                 Inset const * inset = cur.par()->getInset(cur.pos());
2076                 if (inset->needFullRow() || inset->display())
2077                         return true;
2078                 return false;
2079         }
2080 }
2081
2082
2083 void LyXText::setCursorFromCoordinates(LyXCursor & cur,
2084                                        int x, int y)
2085 {
2086         // Get the row first.
2087
2088         Row * row = getRowNearY(y);
2089         bool bound = false;
2090         pos_type const column = getColumnNearX(row, x, bound);
2091         cur.par(row->par());
2092         cur.pos(row->pos() + column);
2093         cur.x(x);
2094         cur.y(y + row->baseline());
2095         cur.row(row);
2096
2097         if (beforeFullRowInset(*row, cur)) {
2098                 pos_type last = row->lastPrintablePos();
2099                 float x = getCursorX(row->next(), cur.pos(), last, bound);
2100                 cur.ix(int(x));
2101                 cur.iy(y + row->height() + row->next()->baseline());
2102                 cur.irow(row->next());
2103         } else {
2104                 cur.iy(cur.y());
2105                 cur.ix(cur.x());
2106                 cur.irow(row);
2107         }
2108         cur.boundary(bound);
2109 }
2110
2111
2112 void LyXText::cursorLeft(bool internal)
2113 {
2114         if (cursor.pos() > 0) {
2115                 bool boundary = cursor.boundary();
2116                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
2117                 if (!internal && !boundary &&
2118                     isBoundary(bv()->buffer(), cursor.par(), cursor.pos() + 1))
2119                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
2120         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2121                 Paragraph * par = cursor.par()->previous();
2122                 setCursor(par, par->size());
2123         }
2124 }
2125
2126
2127 void LyXText::cursorRight(bool internal)
2128 {
2129         if (!internal && cursor.boundary() &&
2130             !cursor.par()->isNewline(cursor.pos()))
2131                 setCursor(cursor.par(), cursor.pos(), true, false);
2132         else if (cursor.pos() < cursor.par()->size()) {
2133                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
2134                 if (!internal &&
2135                     isBoundary(bv()->buffer(), cursor.par(), cursor.pos()))
2136                         setCursor(cursor.par(), cursor.pos(), true, true);
2137         } else if (cursor.par()->next())
2138                 setCursor(cursor.par()->next(), 0);
2139 }
2140
2141
2142 void LyXText::cursorUp(bool selecting)
2143 {
2144 #if 1
2145         int x = cursor.x_fix();
2146         int y = cursor.y() - cursor.row()->baseline() - 1;
2147         setCursorFromCoordinates(x, y);
2148         if (!selecting) {
2149                 int topy = top_y();
2150                 int y1 = cursor.iy() - topy;
2151                 int y2 = y1;
2152                 y -= topy;
2153                 Inset * inset_hit = checkInsetHit(x, y1);
2154                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2155                         inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2156                 }
2157         }
2158 #else
2159         setCursorFromCoordinates(bv(), cursor.x_fix(),
2160                                  cursor.y() - cursor.row()->baseline() - 1);
2161 #endif
2162 }
2163
2164
2165 void LyXText::cursorDown(bool selecting)
2166 {
2167 #if 1
2168         int x = cursor.x_fix();
2169         int y = cursor.y() - cursor.row()->baseline() +
2170                 cursor.row()->height() + 1;
2171         setCursorFromCoordinates(x, y);
2172         if (!selecting && cursor.row() == cursor.irow()) {
2173                 int topy = top_y();
2174                 int y1 = cursor.iy() - topy;
2175                 int y2 = y1;
2176                 y -= topy;
2177                 Inset * inset_hit = checkInsetHit(x, y1);
2178                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2179                         inset_hit->edit(bv(), x, y - (y2 - y1), mouse_button::none);
2180                 }
2181         }
2182 #else
2183         setCursorFromCoordinates(bv(), cursor.x_fix(),
2184                                  cursor.y() - cursor.row()->baseline()
2185                                  + cursor.row()->height() + 1);
2186 #endif
2187 }
2188
2189
2190 void LyXText::cursorUpParagraph()
2191 {
2192         if (cursor.pos() > 0) {
2193                 setCursor(cursor.par(), 0);
2194         }
2195         else if (cursor.par()->previous()) {
2196                 setCursor(cursor.par()->previous(), 0);
2197         }
2198 }
2199
2200
2201 void LyXText::cursorDownParagraph()
2202 {
2203         if (cursor.par()->next()) {
2204                 setCursor(cursor.par()->next(), 0);
2205         } else {
2206                 setCursor(cursor.par(), cursor.par()->size());
2207         }
2208 }
2209
2210 // fix the cursor `cur' after a characters has been deleted at `where'
2211 // position. Called by deleteEmptyParagraphMechanism
2212 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2213                                    LyXCursor const & where)
2214 {
2215         // if cursor is not in the paragraph where the delete occured,
2216         // do nothing
2217         if (cur.par() != where.par())
2218                 return;
2219
2220         // if cursor position is after the place where the delete occured,
2221         // update it
2222         if (cur.pos() > where.pos())
2223                 cur.pos(cur.pos()-1);
2224
2225         // check also if we don't want to set the cursor on a spot behind the
2226         // pagragraph because we erased the last character.
2227         if (cur.pos() > cur.par()->size())
2228                 cur.pos(cur.par()->size());
2229
2230         // recompute row et al. for this cursor
2231         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2232 }
2233
2234
2235 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2236 {
2237         // Would be wrong to delete anything if we have a selection.
2238         if (selection.set())
2239                 return false;
2240
2241         // We allow all kinds of "mumbo-jumbo" when freespacing.
2242         if (old_cursor.par()->layout()->free_spacing
2243             || old_cursor.par()->isFreeSpacing()) {
2244                 return false;
2245         }
2246
2247         /* Ok I'll put some comments here about what is missing.
2248            I have fixed BackSpace (and thus Delete) to not delete
2249            double-spaces automagically. I have also changed Cut,
2250            Copy and Paste to hopefully do some sensible things.
2251            There are still some small problems that can lead to
2252            double spaces stored in the document file or space at
2253            the beginning of paragraphs. This happens if you have
2254            the cursor betwenn to spaces and then save. Or if you
2255            cut and paste and the selection have a space at the
2256            beginning and then save right after the paste. I am
2257            sure none of these are very hard to fix, but I will
2258            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2259            that I can get some feedback. (Lgb)
2260         */
2261
2262         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2263         // delete the LineSeparator.
2264         // MISSING
2265
2266         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2267         // delete the LineSeparator.
2268         // MISSING
2269
2270         // If the pos around the old_cursor were spaces, delete one of them.
2271         if (old_cursor.par() != cursor.par()
2272             || old_cursor.pos() != cursor.pos()) {
2273                 // Only if the cursor has really moved
2274
2275                 if (old_cursor.pos() > 0
2276                     && old_cursor.pos() < old_cursor.par()->size()
2277                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2278                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2279                         old_cursor.par()->erase(old_cursor.pos() - 1);
2280                         redoParagraphs(old_cursor, old_cursor.par()->next());
2281
2282 #ifdef WITH_WARNINGS
2283 #warning This will not work anymore when we have multiple views of the same buffer
2284 // In this case, we will have to correct also the cursors held by
2285 // other bufferviews. It will probably be easier to do that in a more
2286 // automated way in LyXCursor code. (JMarc 26/09/2001)
2287 #endif
2288                         // correct all cursors held by the LyXText
2289                         fixCursorAfterDelete(cursor, old_cursor);
2290                         fixCursorAfterDelete(selection.cursor,
2291                                              old_cursor);
2292                         fixCursorAfterDelete(selection.start,
2293                                              old_cursor);
2294                         fixCursorAfterDelete(selection.end, old_cursor);
2295                         fixCursorAfterDelete(last_sel_cursor,
2296                                              old_cursor);
2297                         fixCursorAfterDelete(toggle_cursor, old_cursor);
2298                         fixCursorAfterDelete(toggle_end_cursor,
2299                                              old_cursor);
2300                         return false;
2301                 }
2302         }
2303
2304         // don't delete anything if this is the ONLY paragraph!
2305         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2306                 return false;
2307
2308         // Do not delete empty paragraphs with keepempty set.
2309         if (old_cursor.par()->layout()->keepempty)
2310                 return false;
2311
2312         // only do our magic if we changed paragraph
2313         if (old_cursor.par() == cursor.par())
2314                 return false;
2315
2316         // record if we have deleted a paragraph
2317         // we can't possibly have deleted a paragraph before this point
2318         bool deleted = false;
2319
2320         if ((old_cursor.par()->empty()
2321              || (old_cursor.par()->size() == 1
2322                  && old_cursor.par()->isLineSeparator(0)))) {
2323                 // ok, we will delete anything
2324                 LyXCursor tmpcursor;
2325
2326                 deleted = true;
2327
2328                 if (old_cursor.row()->previous()) {
2329                         const_cast<LyXText *>(this)->postPaint(old_cursor.y() - old_cursor.row()->baseline()
2330                                   - old_cursor.row()->previous()->height());
2331                         tmpcursor = cursor;
2332                         cursor = old_cursor; // that undo can restore the right cursor position
2333                         Paragraph * endpar = old_cursor.par()->next();
2334                         if (endpar && endpar->getDepth()) {
2335                                 while (endpar && endpar->getDepth()) {
2336                                         endpar = endpar->next();
2337                                 }
2338                         }
2339                         setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2340                         cursor = tmpcursor;
2341
2342                         // delete old row
2343                         removeRow(old_cursor.row());
2344                         if (ownerParagraph() == old_cursor.par()) {
2345                                 ownerParagraph(ownerParagraph()->next());
2346                         }
2347                         // delete old par
2348                         delete old_cursor.par();
2349
2350                         /* Breakagain the next par. Needed because of
2351                          * the parindent that can occur or dissappear.
2352                          * The next row can change its height, if
2353                          * there is another layout before */
2354                         if (refresh_row->next()) {
2355                                 breakAgain(refresh_row->next());
2356                                 updateCounters();
2357                         }
2358                         setHeightOfRow(refresh_row);
2359                 } else {
2360                         Row * nextrow = old_cursor.row()->next();
2361                         const_cast<LyXText *>(this)->postPaint(
2362                                 old_cursor.y() - old_cursor.row()->baseline());
2363
2364                         tmpcursor = cursor;
2365                         cursor = old_cursor; // that undo can restore the right cursor position
2366                         Paragraph * endpar = old_cursor.par()->next();
2367                         if (endpar && endpar->getDepth()) {
2368                                 while (endpar && endpar->getDepth()) {
2369                                         endpar = endpar->next();
2370                                 }
2371                         }
2372                         setUndo(bv(), Undo::DELETE, old_cursor.par(), endpar);
2373                         cursor = tmpcursor;
2374
2375                         // delete old row
2376                         removeRow(old_cursor.row());
2377                         // delete old par
2378                         if (ownerParagraph() == old_cursor.par()) {
2379                                 ownerParagraph(ownerParagraph()->next());
2380                         }
2381
2382                         delete old_cursor.par();
2383
2384                         /* Breakagain the next par. Needed because of
2385                            the parindent that can occur or dissappear.
2386                            The next row can change its height, if
2387                            there is another layout before */
2388                         if (nextrow) {
2389                                 breakAgain(nextrow);
2390                                 updateCounters();
2391                         }
2392                 }
2393
2394                 // correct cursor y
2395                 setCursorIntern(cursor.par(), cursor.pos());
2396
2397                 if (selection.cursor.par()  == old_cursor.par()
2398                     && selection.cursor.pos() == old_cursor.pos()) {
2399                         // correct selection
2400                         selection.cursor = cursor;
2401                 }
2402         }
2403         if (!deleted) {
2404                 if (old_cursor.par()->stripLeadingSpaces()) {
2405                         redoParagraphs(old_cursor,
2406                                        old_cursor.par()->next());
2407                         // correct cursor y
2408                         setCursorIntern(cursor.par(), cursor.pos());
2409                         selection.cursor = cursor;
2410                 }
2411         }
2412         return deleted;
2413 }
2414
2415
2416 Paragraph * LyXText::ownerParagraph() const
2417 {
2418         if (inset_owner) {
2419                 return inset_owner->paragraph();
2420         }
2421         return &*(bv_owner->buffer()->paragraphs.begin());
2422 }
2423
2424
2425 void LyXText::ownerParagraph(Paragraph * p) const
2426 {
2427         if (inset_owner) {
2428                 inset_owner->paragraph(p);
2429         } else {
2430                 bv_owner->buffer()->paragraphs.set(p);
2431         }
2432 }
2433
2434
2435 void LyXText::ownerParagraph(int id, Paragraph * p) const
2436 {
2437         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2438         if (op && op->inInset()) {
2439                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2440         } else {
2441                 ownerParagraph(p);
2442         }
2443 }
2444
2445
2446 LyXText::text_status LyXText::status() const
2447 {
2448         return status_;
2449 }
2450
2451
2452 void LyXText::clearPaint()
2453 {
2454         status_ = UNCHANGED;
2455         refresh_row = 0;
2456         refresh_y = 0;
2457 }
2458
2459
2460 void LyXText::postChangedInDraw()
2461 {
2462         status_ = CHANGED_IN_DRAW;
2463 }
2464
2465
2466 void LyXText::postPaint(int start_y)
2467 {
2468         text_status old = status_;
2469
2470         status_ = NEED_MORE_REFRESH;
2471         refresh_row = 0;
2472
2473         if (old != UNCHANGED && refresh_y < start_y) {
2474                 lyxerr << "Paint already pending from above" << endl;
2475                 return;
2476         }
2477
2478         refresh_y = start_y;
2479
2480         if (!inset_owner)
2481                 return;
2482
2483         // We are an inset's lyxtext. Tell the top-level lyxtext
2484         // it needs to update the row we're in.
2485
2486         LyXText * t = bv()->text;
2487
2488         // FIXME: but what if this row is below ?
2489         if (!t->refresh_row) {
2490                 t->refresh_row = t->cursor.row();
2491                 t->refresh_y = t->cursor.y() - t->cursor.row()->baseline();
2492         }
2493 }
2494
2495
2496 // FIXME: we should probably remove this y parameter,
2497 // make refresh_y be 0, and use row->y etc.
2498 void LyXText::postRowPaint(Row * row, int start_y)
2499 {
2500         if (status_ != UNCHANGED && refresh_y < start_y) {
2501                 lyxerr << "Paint already pending from above" << endl;
2502                 return;
2503         } else {
2504                 refresh_y = start_y;
2505         }
2506
2507         if (status_ == NEED_MORE_REFRESH)
2508                 return;
2509
2510         status_ = NEED_VERY_LITTLE_REFRESH;
2511         refresh_row = row;
2512
2513         if (!inset_owner)
2514                 return;
2515
2516         // We are an inset's lyxtext. Tell the top-level lyxtext
2517         // it needs to update the row we're in.
2518
2519         LyXText * t = bv()->text;
2520
2521         // FIXME: but what if this new row is above ?
2522         // Why the !t->refresh_row at all ?
2523         if (!t->refresh_row) {
2524                 t->refresh_row = t->cursor.row();
2525                 t->refresh_y = t->cursor.y() - t->cursor.row()->baseline();
2526         }
2527 }
2528
2529
2530 bool LyXText::isInInset() const
2531 {
2532         // Sub-level has non-null bv owner and
2533         // non-null inset owner.
2534         return inset_owner != 0 && bv_owner != 0;
2535 }
2536
2537
2538 int defaultRowHeight()
2539 {
2540         LyXFont const font(LyXFont::ALL_SANE);
2541         return int(font_metrics::maxAscent(font)
2542                  + font_metrics::maxDescent(font) * 1.5);
2543 }