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