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