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