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