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