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