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