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