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