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