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