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