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