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