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