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