]> git.lyx.org Git - lyx.git/blob - src/text2.C
introduce namespace lyx::support
[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(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 = selectWordWhenUnderCursor(PREVIOUS_WORD);
862
863         string idxstring;
864         if (!selection.set())
865                 bv()->owner()->message(_("Nothing to index!"));
866         else if (selection.start.par() != selection.end.par())
867                 bv()->owner()->message(_("Cannot index more than one paragraph!"));
868         else
869                 idxstring = selectionAsString(bv()->buffer(), false);
870
871         // Reset cursors to their original position.
872         cursor = reset_cursor;
873         setCursor(cursor.par(), cursor.pos());
874         selection.cursor = cursor;
875
876         // Clear the implicit selection.
877         if (implicitSelection)
878                 clearSelection();
879
880         return idxstring;
881 }
882
883
884 // the DTP switches for paragraphs. LyX will store them in the first
885 // physicla paragraph. When a paragraph is broken, the top settings rest,
886 // the bottom settings are given to the new one. So I can make shure,
887 // they do not duplicate themself and you cannnot make dirty things with
888 // them!
889
890 void LyXText::setParagraph(bool line_top, bool line_bottom,
891                            bool pagebreak_top, bool pagebreak_bottom,
892                            VSpace const & space_top,
893                            VSpace const & space_bottom,
894                            Spacing const & spacing,
895                            LyXAlignment align,
896                            string const & labelwidthstring,
897                            bool noindent)
898 {
899         LyXCursor tmpcursor = cursor;
900         if (!selection.set()) {
901                 selection.start = cursor;
902                 selection.end = cursor;
903         }
904
905         // make sure that the depth behind the selection are restored, too
906         ParagraphList::iterator endpit = boost::next(selection.end.par());
907         ParagraphList::iterator undoendpit = endpit;
908         ParagraphList::iterator pars_end = ownerParagraphs().end();
909
910         if (endpit != pars_end && endpit->getDepth()) {
911                 while (endpit != pars_end && endpit->getDepth()) {
912                         ++endpit;
913                         undoendpit = endpit;
914                 }
915         } else if (endpit != pars_end) {
916                 // because of parindents etc.
917                 ++endpit;
918         }
919
920         setUndo(bv(), Undo::EDIT, selection.start.par(),
921                 boost::prior(undoendpit));
922
923
924         ParagraphList::iterator tmppit = selection.end.par();
925
926         while (tmppit != boost::prior(selection.start.par())) {
927                 setCursor(tmppit, 0);
928                 postPaint(cursor.y() - cursorRow()->baseline());
929
930                 ParagraphList::iterator pit = cursor.par();
931                 ParagraphParameters & params = pit->params();
932
933                 params.lineTop(line_top);
934                 params.lineBottom(line_bottom);
935                 params.pagebreakTop(pagebreak_top);
936                 params.pagebreakBottom(pagebreak_bottom);
937                 params.spaceTop(space_top);
938                 params.spaceBottom(space_bottom);
939                 params.spacing(spacing);
940                 // does the layout allow the new alignment?
941                 LyXLayout_ptr const & layout = pit->layout();
942
943                 if (align == LYX_ALIGN_LAYOUT)
944                         align = layout->align;
945                 if (align & layout->alignpossible) {
946                         if (align == layout->align)
947                                 params.align(LYX_ALIGN_LAYOUT);
948                         else
949                                 params.align(align);
950                 }
951                 pit->setLabelWidthString(labelwidthstring);
952                 params.noindent(noindent);
953                 tmppit = boost::prior(pit);
954         }
955
956         redoParagraphs(selection.start, endpit);
957
958         clearSelection();
959         setCursor(selection.start.par(), selection.start.pos());
960         selection.cursor = cursor;
961         setCursor(selection.end.par(), selection.end.pos());
962         setSelection();
963         setCursor(tmpcursor.par(), tmpcursor.pos());
964         if (inset_owner)
965                 bv()->updateInset(inset_owner);
966 }
967
968
969 // set the counter of a paragraph. This includes the labels
970 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
971 {
972         LyXTextClass const & textclass = buf->params.getLyXTextClass();
973         LyXLayout_ptr const & layout = pit->layout();
974
975         if (pit != ownerParagraphs().begin()) {
976
977                 pit->params().appendix(boost::prior(pit)->params().appendix());
978                 if (!pit->params().appendix() &&
979                     pit->params().startOfAppendix()) {
980                         pit->params().appendix(true);
981                         textclass.counters().reset();
982                 }
983                 pit->enumdepth = boost::prior(pit)->enumdepth;
984                 pit->itemdepth = boost::prior(pit)->itemdepth;
985         } else {
986                 pit->params().appendix(pit->params().startOfAppendix());
987                 pit->enumdepth = 0;
988                 pit->itemdepth = 0;
989         }
990
991         /* Maybe we have to increment the enumeration depth.
992          * BUT, enumeration in a footnote is considered in isolation from its
993          *      surrounding paragraph so don't increment if this is the
994          *      first line of the footnote
995          * AND, bibliographies can't have their depth changed ie. they
996          *      are always of depth 0
997          */
998         if (pit != ownerParagraphs().begin()
999             && boost::prior(pit)->getDepth() < pit->getDepth()
1000             && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
1001             && pit->enumdepth < 3
1002             && layout->labeltype != LABEL_BIBLIO) {
1003                 pit->enumdepth++;
1004         }
1005
1006         // Maybe we have to decrement the enumeration depth, see note above
1007         if (pit != ownerParagraphs().begin()
1008             && boost::prior(pit)->getDepth() > pit->getDepth()
1009             && layout->labeltype != LABEL_BIBLIO) {
1010                 pit->enumdepth = depthHook(pit, ownerParagraphs(),
1011                                            pit->getDepth())->enumdepth;
1012         }
1013
1014         if (!pit->params().labelString().empty()) {
1015                 pit->params().labelString(string());
1016         }
1017
1018         if (layout->margintype == MARGIN_MANUAL) {
1019                 if (pit->params().labelWidthString().empty()) {
1020                         pit->setLabelWidthString(layout->labelstring());
1021                 }
1022         } else {
1023                 pit->setLabelWidthString(string());
1024         }
1025
1026         // is it a layout that has an automatic label?
1027         if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1028                 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1029
1030                 ostringstream s;
1031
1032                 if (i >= 0 && i <= buf->params.secnumdepth) {
1033                         string numbertype;
1034                         string langtype;
1035
1036                         textclass.counters().step(layout->latexname());
1037
1038                         // Is there a label? Useful for Chapter layout
1039                         if (!pit->params().appendix()) {
1040                                 s << buf->B_(layout->labelstring());
1041                         } else {
1042                                 s << buf->B_(layout->labelstring_appendix());
1043                         }
1044
1045                         // Use of an integer is here less than elegant. For now.
1046                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1047                         if (!pit->params().appendix()) {
1048                                 numbertype = "sectioning";
1049                         } else {
1050                                 numbertype = "appendix";
1051                                 if (pit->isRightToLeftPar(buf->params))
1052                                         langtype = "hebrew";
1053                                 else
1054                                         langtype = "latin";
1055                         }
1056
1057                         s << " "
1058                           << textclass.counters()
1059                                 .numberLabel(layout->latexname(),
1060                                              numbertype, langtype, head);
1061
1062                         pit->params().labelString(STRCONV(s.str()));
1063
1064                         // reset enum counters
1065                         textclass.counters().reset("enum");
1066                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1067                         textclass.counters().reset("enum");
1068                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1069                         // FIXME
1070                         // Yes I know this is a really, really! bad solution
1071                         // (Lgb)
1072                         string enumcounter("enum");
1073
1074                         switch (pit->enumdepth) {
1075                         case 2:
1076                                 enumcounter += 'i';
1077                         case 1:
1078                                 enumcounter += 'i';
1079                         case 0:
1080                                 enumcounter += 'i';
1081                                 break;
1082                         case 3:
1083                                 enumcounter += "iv";
1084                                 break;
1085                         default:
1086                                 // not a valid enumdepth...
1087                                 break;
1088                         }
1089
1090                         textclass.counters().step(enumcounter);
1091
1092                         s << textclass.counters()
1093                                 .numberLabel(enumcounter, "enumeration");
1094                         pit->params().labelString(STRCONV(s.str()));
1095                 }
1096         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1097                 textclass.counters().step("bibitem");
1098                 int number = textclass.counters().value("bibitem");
1099                 if (pit->bibitem()) {
1100                         pit->bibitem()->setCounter(number);
1101                         pit->params().labelString(layout->labelstring());
1102                 }
1103                 // In biblio should't be following counters but...
1104         } else {
1105                 string s = buf->B_(layout->labelstring());
1106
1107                 // the caption hack:
1108                 if (layout->labeltype == LABEL_SENSITIVE) {
1109                         ParagraphList::iterator tmppit = pit;
1110                         Inset * in = 0;
1111                         bool isOK = false;
1112                         while (tmppit != ownerParagraphs().end() &&
1113                                tmppit->inInset()
1114                                // the single '=' is intended below
1115                                && (in = tmppit->inInset()->owner())) {
1116                                 if (in->lyxCode() == Inset::FLOAT_CODE ||
1117                                     in->lyxCode() == Inset::WRAP_CODE) {
1118                                         isOK = true;
1119                                         break;
1120                                 } else {
1121                                         tmppit = std::find(ownerParagraphs().begin(), ownerParagraphs().end(), *in->parOwner());
1122                                 }
1123                         }
1124
1125                         if (isOK) {
1126                                 string type;
1127
1128                                 if (in->lyxCode() == Inset::FLOAT_CODE)
1129                                         type = static_cast<InsetFloat*>(in)->params().type;
1130                                 else if (in->lyxCode() == Inset::WRAP_CODE)
1131                                         type = static_cast<InsetWrap*>(in)->params().type;
1132                                 else
1133                                         Assert(0);
1134
1135                                 Floating const & fl = textclass.floats().getType(type);
1136
1137                                 textclass.counters().step(fl.type());
1138
1139                                 // Doesn't work... yet.
1140                                 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1141                         } else {
1142                                 // par->SetLayout(0);
1143                                 // s = layout->labelstring;
1144                                 s = _("Senseless: ");
1145                         }
1146                 }
1147                 pit->params().labelString(s);
1148
1149                 // reset the enumeration counter. They are always reset
1150                 // when there is any other layout between
1151                 // Just fall-through between the cases so that all
1152                 // enum counters deeper than enumdepth is also reset.
1153                 switch (pit->enumdepth) {
1154                 case 0:
1155                         textclass.counters().reset("enumi");
1156                 case 1:
1157                         textclass.counters().reset("enumii");
1158                 case 2:
1159                         textclass.counters().reset("enumiii");
1160                 case 3:
1161                         textclass.counters().reset("enumiv");
1162                 }
1163         }
1164 }
1165
1166
1167 // Updates all counters. Paragraphs with changed label string will be rebroken
1168 void LyXText::updateCounters()
1169 {
1170         RowList::iterator rowit = rows().begin();
1171         ParagraphList::iterator pit = rowit->par();
1172
1173         // CHECK if this is really needed. (Lgb)
1174         bv()->buffer()->params.getLyXTextClass().counters().reset();
1175
1176         ParagraphList::iterator beg = ownerParagraphs().begin();
1177         ParagraphList::iterator end = ownerParagraphs().end();
1178         for (; pit != end; ++pit) {
1179                 while (rowit->par() != pit)
1180                         ++rowit;
1181
1182                 string const oldLabel = pit->params().labelString();
1183
1184                 size_t maxdepth = 0;
1185                 if (pit != beg)
1186                         maxdepth = boost::prior(pit)->getMaxDepthAfter();
1187
1188                 if (pit->params().depth() > maxdepth)
1189                         pit->params().depth(maxdepth);
1190
1191                 // setCounter can potentially change the labelString.
1192                 setCounter(bv()->buffer(), pit);
1193
1194                 string const & newLabel = pit->params().labelString();
1195
1196                 if (oldLabel.empty() && !newLabel.empty()) {
1197                         removeParagraph(rowit);
1198                         appendParagraph(rowit);
1199                 }
1200         }
1201 }
1202
1203
1204 void LyXText::insertInset(Inset * inset)
1205 {
1206         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1207                 return;
1208         setUndo(bv(), Undo::FINISH, cursor.par());
1209         freezeUndo();
1210         cursor.par()->insertInset(cursor.pos(), inset);
1211         // Just to rebreak and refresh correctly.
1212         // The character will not be inserted a second time
1213         insertChar(Paragraph::META_INSET);
1214         // If we enter a highly editable inset the cursor should be to before
1215         // the inset. This couldn't happen before as Undo was not handled inside
1216         // inset now after the Undo LyX tries to call inset->Edit(...) again
1217         // and cannot do this as the cursor is behind the inset and GetInset
1218         // does not return the inset!
1219         if (isHighlyEditableInset(inset)) {
1220                 cursorLeft(true);
1221         }
1222         unFreezeUndo();
1223 }
1224
1225
1226 void LyXText::cutSelection(bool doclear, bool realcut)
1227 {
1228         // Stuff what we got on the clipboard. Even if there is no selection.
1229
1230         // There is a problem with having the stuffing here in that the
1231         // larger the selection the slower LyX will get. This can be
1232         // solved by running the line below only when the selection has
1233         // finished. The solution used currently just works, to make it
1234         // faster we need to be more clever and probably also have more
1235         // calls to stuffClipboard. (Lgb)
1236         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1237
1238         // This doesn't make sense, if there is no selection
1239         if (!selection.set())
1240                 return;
1241
1242         // OK, we have a selection. This is always between selection.start
1243         // and selection.end
1244
1245         // make sure that the depth behind the selection are restored, too
1246         ParagraphList::iterator endpit = boost::next(selection.end.par());
1247         ParagraphList::iterator undoendpit = endpit;
1248         ParagraphList::iterator pars_end = ownerParagraphs().end();
1249
1250         if (endpit != pars_end && endpit->getDepth()) {
1251                 while (endpit != pars_end && endpit->getDepth()) {
1252                         ++endpit;
1253                         undoendpit = endpit;
1254                 }
1255         } else if (endpit != pars_end) {
1256                 // because of parindents etc.
1257                 ++endpit;
1258         }
1259
1260         setUndo(bv(), Undo::DELETE, selection.start.par(),
1261                 boost::prior(undoendpit));
1262
1263
1264         endpit = selection.end.par();
1265         int endpos = selection.end.pos();
1266
1267         boost::tie(endpit, endpos) = realcut ?
1268                 CutAndPaste::cutSelection(bv()->buffer()->params,
1269                                           ownerParagraphs(),
1270                                           selection.start.par(), endpit,
1271                                           selection.start.pos(), endpos,
1272                                           bv()->buffer()->params.textclass,
1273                                           doclear)
1274                 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1275                                               ownerParagraphs(),
1276                                               selection.start.par(), endpit,
1277                                               selection.start.pos(), endpos,
1278                                               doclear);
1279         // sometimes necessary
1280         if (doclear)
1281                 selection.start.par()->stripLeadingSpaces();
1282
1283         redoParagraphs(selection.start, boost::next(endpit));
1284 #warning FIXME latent bug
1285         // endpit will be invalidated on redoParagraphs once ParagraphList
1286         // becomes a std::list? There are maybe other places on which this
1287         // can happend? (Ab)
1288         // cutSelection can invalidate the cursor so we need to set
1289         // it anew. (Lgb)
1290         // we prefer the end for when tracking changes
1291         cursor.pos(endpos);
1292         cursor.par(endpit);
1293
1294         // need a valid cursor. (Lgb)
1295         clearSelection();
1296
1297         setCursor(cursor.par(), cursor.pos());
1298         selection.cursor = cursor;
1299         updateCounters();
1300 }
1301
1302
1303 void LyXText::copySelection()
1304 {
1305         // stuff the selection onto the X clipboard, from an explicit copy request
1306         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1307
1308         // this doesnt make sense, if there is no selection
1309         if (!selection.set())
1310                 return;
1311
1312         // ok we have a selection. This is always between selection.start
1313         // and sel_end cursor
1314
1315         // copy behind a space if there is one
1316         while (selection.start.par()->size() > selection.start.pos()
1317                && selection.start.par()->isLineSeparator(selection.start.pos())
1318                && (selection.start.par() != selection.end.par()
1319                    || selection.start.pos() < selection.end.pos()))
1320                 selection.start.pos(selection.start.pos() + 1);
1321
1322         CutAndPaste::copySelection(selection.start.par(),
1323                                    selection.end.par(),
1324                                    selection.start.pos(), selection.end.pos(),
1325                                    bv()->buffer()->params.textclass);
1326 }
1327
1328
1329 void LyXText::pasteSelection(size_t sel_index)
1330 {
1331         // this does not make sense, if there is nothing to paste
1332         if (!CutAndPaste::checkPastePossible())
1333                 return;
1334
1335         setUndo(bv(), Undo::INSERT, cursor.par());
1336
1337         ParagraphList::iterator endpit;
1338         PitPosPair ppp;
1339
1340         ErrorList el;
1341
1342         boost::tie(ppp, endpit) =
1343                 CutAndPaste::pasteSelection(*bv()->buffer(),
1344                                             ownerParagraphs(),
1345                                             cursor.par(), cursor.pos(),
1346                                             bv()->buffer()->params.textclass,
1347                                             sel_index, el);
1348         parseErrors(*bv()->buffer(), el);
1349         bv()->showErrorList(_("Paste"));
1350
1351         redoParagraphs(cursor, endpit);
1352
1353         setCursor(cursor.par(), cursor.pos());
1354         clearSelection();
1355
1356         selection.cursor = cursor;
1357         setCursor(ppp.first, ppp.second);
1358         setSelection();
1359         updateCounters();
1360 }
1361
1362
1363 void LyXText::setSelectionRange(lyx::pos_type length)
1364 {
1365         if (!length)
1366                 return;
1367
1368         selection.cursor = cursor;
1369         while (length--)
1370                 cursorRight(bv());
1371         setSelection();
1372 }
1373
1374
1375 // simple replacing. The font of the first selected character is used
1376 void LyXText::replaceSelectionWithString(string const & str)
1377 {
1378         setCursorParUndo(bv());
1379         freezeUndo();
1380
1381         if (!selection.set()) { // create a dummy selection
1382                 selection.end = cursor;
1383                 selection.start = cursor;
1384         }
1385
1386         // Get font setting before we cut
1387         pos_type pos = selection.end.pos();
1388         LyXFont const font = selection.start.par()
1389                 ->getFontSettings(bv()->buffer()->params,
1390                                   selection.start.pos());
1391
1392         // Insert the new string
1393         string::const_iterator cit = str.begin();
1394         string::const_iterator end = str.end();
1395         for (; cit != end; ++cit) {
1396                 selection.end.par()->insertChar(pos, (*cit), font);
1397                 ++pos;
1398         }
1399
1400         // Cut the selection
1401         cutSelection(true, false);
1402
1403         unFreezeUndo();
1404 }
1405
1406
1407 // needed to insert the selection
1408 void LyXText::insertStringAsLines(string const & str)
1409 {
1410         ParagraphList::iterator pit = cursor.par();
1411         pos_type pos = cursor.pos();
1412         ParagraphList::iterator endpit = boost::next(cursor.par());
1413
1414         setCursorParUndo(bv());
1415
1416         // only to be sure, should not be neccessary
1417         clearSelection();
1418
1419         bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1420
1421         redoParagraphs(cursor, endpit);
1422         setCursor(cursor.par(), cursor.pos());
1423         selection.cursor = cursor;
1424         setCursor(pit, pos);
1425         setSelection();
1426 }
1427
1428
1429 // turns double-CR to single CR, others where converted into one
1430 // blank. Then InsertStringAsLines is called
1431 void LyXText::insertStringAsParagraphs(string const & str)
1432 {
1433         string linestr(str);
1434         bool newline_inserted = false;
1435         string::size_type const siz = linestr.length();
1436
1437         for (string::size_type i = 0; i < siz; ++i) {
1438                 if (linestr[i] == '\n') {
1439                         if (newline_inserted) {
1440                                 // we know that \r will be ignored by
1441                                 // InsertStringA. Of course, it is a dirty
1442                                 // trick, but it works...
1443                                 linestr[i - 1] = '\r';
1444                                 linestr[i] = '\n';
1445                         } else {
1446                                 linestr[i] = ' ';
1447                                 newline_inserted = true;
1448                         }
1449                 } else if (IsPrintable(linestr[i])) {
1450                         newline_inserted = false;
1451                 }
1452         }
1453         insertStringAsLines(linestr);
1454 }
1455
1456
1457 void LyXText::checkParagraph(ParagraphList::iterator pit, pos_type pos)
1458 {
1459         LyXCursor tmpcursor;
1460
1461         int y = 0;
1462         pos_type z;
1463         RowList::iterator row = getRow(pit, pos, y);
1464         RowList::iterator beg = rows().begin();
1465
1466         // is there a break one row above
1467         if (row != beg
1468             && boost::prior(row)->par() == row->par()) {
1469                 z = rowBreakPoint(*boost::prior(row));
1470                 if (z >= row->pos()) {
1471                         // set the dimensions of the row above
1472                         y -= boost::prior(row)->height();
1473                         postPaint(y);
1474
1475                         breakAgain(boost::prior(row));
1476
1477                         // set the cursor again. Otherwise
1478                         // dangling pointers are possible
1479                         setCursor(cursor.par(), cursor.pos(),
1480                                   false, cursor.boundary());
1481                         selection.cursor = cursor;
1482                         return;
1483                 }
1484         }
1485
1486         int const tmpheight = row->height();
1487         pos_type const tmplast = lastPos(*this, row);
1488
1489         breakAgain(row);
1490         if (row->height() == tmpheight && lastPos(*this, row) == tmplast) {
1491                 postRowPaint(row, y);
1492         } else {
1493                 postPaint(y);
1494         }
1495
1496         // check the special right address boxes
1497         if (pit->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1498                 tmpcursor.par(pit);
1499                 tmpcursor.y(y);
1500                 tmpcursor.x(0);
1501                 tmpcursor.x_fix(0);
1502                 tmpcursor.pos(pos);
1503                 redoDrawingOfParagraph(tmpcursor);
1504         }
1505
1506         // set the cursor again. Otherwise dangling pointers are possible
1507         // also set the selection
1508
1509         if (selection.set()) {
1510                 tmpcursor = cursor;
1511                 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1512                                 false, selection.cursor.boundary());
1513                 selection.cursor = cursor;
1514                 setCursorIntern(selection.start.par(),
1515                                 selection.start.pos(),
1516                                 false, selection.start.boundary());
1517                 selection.start = cursor;
1518                 setCursorIntern(selection.end.par(),
1519                                 selection.end.pos(),
1520                                 false, selection.end.boundary());
1521                 selection.end = cursor;
1522                 setCursorIntern(last_sel_cursor.par(),
1523                                 last_sel_cursor.pos(),
1524                                 false, last_sel_cursor.boundary());
1525                 last_sel_cursor = cursor;
1526                 cursor = tmpcursor;
1527         }
1528         setCursorIntern(cursor.par(), cursor.pos(),
1529                         false, cursor.boundary());
1530 }
1531
1532
1533 // returns false if inset wasn't found
1534 bool LyXText::updateInset(Inset * inset)
1535 {
1536         // first check the current paragraph
1537         int pos = cursor.par()->getPositionOfInset(inset);
1538         if (pos != -1) {
1539                 checkParagraph(cursor.par(), pos);
1540                 return true;
1541         }
1542
1543         // check every paragraph
1544
1545         ParagraphList::iterator par = ownerParagraphs().begin();
1546         ParagraphList::iterator end = ownerParagraphs().end();
1547         for (; par != end; ++par) {
1548                 pos = par->getPositionOfInset(inset);
1549                 if (pos != -1) {
1550                         checkParagraph(par, pos);
1551                         return true;
1552                 }
1553         };
1554
1555         return false;
1556 }
1557
1558
1559 bool LyXText::setCursor(ParagraphList::iterator pit,
1560                         pos_type pos,
1561                         bool setfont, bool boundary)
1562 {
1563         LyXCursor old_cursor = cursor;
1564         setCursorIntern(pit, pos, setfont, boundary);
1565         return deleteEmptyParagraphMechanism(old_cursor);
1566 }
1567
1568
1569 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1570                         pos_type pos, bool boundary)
1571 {
1572         Assert(pit != ownerParagraphs().end());
1573
1574         cur.par(pit);
1575         cur.pos(pos);
1576         cur.boundary(boundary);
1577
1578         // get the cursor y position in text
1579         int y = 0;
1580         RowList::iterator row = getRow(pit, pos, y);
1581         RowList::iterator beg = rows().begin();
1582
1583         RowList::iterator old_row = row;
1584         cur.irow(row);
1585         // if we are before the first char of this row and are still in the
1586         // same paragraph and there is a previous row then put the cursor on
1587         // the end of the previous row
1588         cur.iy(y + row->baseline());
1589         if (row != beg &&
1590             pos &&
1591             boost::prior(row)->par() == row->par() &&
1592             pos < pit->size() &&
1593             pit->getChar(pos) == Paragraph::META_INSET) {
1594                 Inset * ins = pit->getInset(pos);
1595                 if (ins && (ins->needFullRow() || ins->display())) {
1596                         --row;
1597                         y -= row->height();
1598                 }
1599         }
1600
1601         // y is now the beginning of the cursor row
1602         y += row->baseline();
1603         // y is now the cursor baseline
1604         cur.y(y);
1605
1606         pos_type last = lastPrintablePos(*this, old_row);
1607
1608         // None of these should happen, but we're scaredy-cats
1609         if (pos > pit->size()) {
1610                 lyxerr << "dont like 1 please report" << endl;
1611                 pos = 0;
1612                 cur.pos(0);
1613         } else if (pos > last + 1) {
1614                 lyxerr << "dont like 2 please report" << endl;
1615                 // This shouldn't happen.
1616                 pos = last + 1;
1617                 cur.pos(pos);
1618         } else if (pos < row->pos()) {
1619                 lyxerr << "dont like 3 please report" << endl;
1620                 pos = row->pos();
1621                 cur.pos(pos);
1622         }
1623
1624         // now get the cursors x position
1625         float x = getCursorX(row, pos, last, boundary);
1626         cur.x(int(x));
1627         cur.x_fix(cur.x());
1628         if (old_row != row) {
1629                 x = getCursorX(old_row, pos, last, boundary);
1630                 cur.ix(int(x));
1631         } else
1632                 cur.ix(cur.x());
1633 /* We take out this for the time being because 1) the redraw code is not
1634    prepared to this yet and 2) because some good policy has yet to be decided
1635    while editting: for instance how to act on rows being created/deleted
1636    because of DEPM.
1637 */
1638 #if 0
1639         //if the cursor is in a visible row, anchor to it
1640         int topy = top_y();
1641         if (topy < y && y < topy + bv()->workHeight())
1642                 anchor_row(row);
1643 #endif
1644 }
1645
1646
1647 float LyXText::getCursorX(RowList::iterator rit,
1648                           pos_type pos, pos_type last, bool boundary) const
1649 {
1650         pos_type cursor_vpos = 0;
1651         float x;
1652         float fill_separator;
1653         float fill_hfill;
1654         float fill_label_hfill;
1655         // This call HAS to be here because of the BidiTables!!!
1656         prepareToPrint(rit, x, fill_separator, fill_hfill,
1657                        fill_label_hfill);
1658
1659         ParagraphList::iterator rit_par = rit->par();
1660         pos_type const rit_pos = rit->pos();
1661
1662         if (last < rit_pos)
1663                 cursor_vpos = rit_pos;
1664         else if (pos > last && !boundary)
1665                 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1666                         ? rit_pos : last + 1;
1667         else if (pos > rit_pos && (pos > last || boundary))
1668                 /// Place cursor after char at (logical) position pos - 1
1669                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1670                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1671         else
1672                 /// Place cursor before char at (logical) position pos
1673                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1674                         ? log2vis(pos) : log2vis(pos) + 1;
1675
1676         pos_type body_pos = rit_par->beginningOfBody();
1677         if ((body_pos > 0) &&
1678             ((body_pos - 1 > last) || !rit_par->isLineSeparator(body_pos - 1)))
1679                 body_pos = 0;
1680
1681         for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1682                 pos_type pos = vis2log(vpos);
1683                 if (body_pos > 0 && pos == body_pos - 1) {
1684                         x += fill_label_hfill +
1685                                 font_metrics::width(
1686                                         rit_par->layout()->labelsep,
1687                                         getLabelFont(bv()->buffer(), rit_par));
1688                         if (rit_par->isLineSeparator(body_pos - 1))
1689                                 x -= singleWidth(rit_par, body_pos - 1);
1690                 }
1691
1692                 if (hfillExpansion(*this, rit, pos)) {
1693                         x += singleWidth(rit_par, pos);
1694                         if (pos >= body_pos)
1695                                 x += fill_hfill;
1696                         else
1697                                 x += fill_label_hfill;
1698                 } else if (rit_par->isSeparator(pos)) {
1699                         x += singleWidth(rit_par, pos);
1700                         if (pos >= body_pos)
1701                                 x += fill_separator;
1702                 } else
1703                         x += singleWidth(rit_par, pos);
1704         }
1705         return x;
1706 }
1707
1708
1709 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1710                               pos_type pos, bool setfont, bool boundary)
1711 {
1712         UpdatableInset * it = pit->inInset();
1713         if (it) {
1714                 if (it != inset_owner) {
1715                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1716                                               << endl
1717                                               << "inset_owner is "
1718                                               << inset_owner << endl;
1719 #ifdef WITH_WARNINGS
1720 #warning I believe this code is wrong. (Lgb)
1721 #warning Jürgen, have a look at this. (Lgb)
1722 #warning Hmmm, I guess you are right but we
1723 #warning should verify when this is needed
1724 #endif
1725                         // Jürgen, would you like to have a look?
1726                         // I guess we need to move the outer cursor
1727                         // and open and lock the inset (bla bla bla)
1728                         // stuff I don't know... so can you have a look?
1729                         // (Lgb)
1730                         // I moved the lyxerr stuff in here so we can see if
1731                         // this is actually really needed and where!
1732                         // (Jug)
1733                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1734                         return;
1735                 }
1736         }
1737
1738         setCursor(cursor, pit, pos, boundary);
1739         if (setfont)
1740                 setCurrentFont();
1741 }
1742
1743
1744 void LyXText::setCurrentFont()
1745 {
1746         pos_type pos = cursor.pos();
1747         ParagraphList::iterator pit = cursor.par();
1748
1749         if (cursor.boundary() && pos > 0)
1750                 --pos;
1751
1752         if (pos > 0) {
1753                 if (pos == pit->size())
1754                         --pos;
1755                 else // potentional bug... BUG (Lgb)
1756                         if (pit->isSeparator(pos)) {
1757                                 if (pos > cursorRow()->pos() &&
1758                                     bidi_level(pos) % 2 ==
1759                                     bidi_level(pos - 1) % 2)
1760                                         --pos;
1761                                 else if (pos + 1 < pit->size())
1762                                         ++pos;
1763                         }
1764         }
1765
1766         current_font =
1767                 pit->getFontSettings(bv()->buffer()->params, pos);
1768         real_current_font = getFont(bv()->buffer(), pit, pos);
1769
1770         if (cursor.pos() == pit->size() &&
1771             isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1772             !cursor.boundary()) {
1773                 Language const * lang =
1774                         pit->getParLanguage(bv()->buffer()->params);
1775                 current_font.setLanguage(lang);
1776                 current_font.setNumber(LyXFont::OFF);
1777                 real_current_font.setLanguage(lang);
1778                 real_current_font.setNumber(LyXFont::OFF);
1779         }
1780 }
1781
1782
1783 // returns the column near the specified x-coordinate of the row
1784 // x is set to the real beginning of this column
1785 pos_type
1786 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1787 {
1788         float tmpx = 0.0;
1789         float fill_separator;
1790         float fill_hfill;
1791         float fill_label_hfill;
1792
1793         prepareToPrint(rit, tmpx, fill_separator,
1794                        fill_hfill, fill_label_hfill);
1795
1796         pos_type vc = rit->pos();
1797         pos_type last = lastPrintablePos(*this, rit);
1798         pos_type c = 0;
1799
1800         ParagraphList::iterator rit_par = rit->par();
1801         LyXLayout_ptr const & layout = rit->par()->layout();
1802
1803         bool left_side = false;
1804
1805         pos_type body_pos = rit_par->beginningOfBody();
1806         float last_tmpx = tmpx;
1807
1808         if (body_pos > 0 &&
1809             (body_pos - 1 > last ||
1810              !rit_par->isLineSeparator(body_pos - 1)))
1811                 body_pos = 0;
1812
1813         // check for empty row
1814         if (!rit_par->size()) {
1815                 x = int(tmpx);
1816                 return 0;
1817         }
1818
1819         while (vc <= last && tmpx <= x) {
1820                 c = vis2log(vc);
1821                 last_tmpx = tmpx;
1822                 if (body_pos > 0 && c == body_pos - 1) {
1823                         tmpx += fill_label_hfill +
1824                                 font_metrics::width(layout->labelsep,
1825                                                getLabelFont(bv()->buffer(), rit_par));
1826                         if (rit_par->isLineSeparator(body_pos - 1))
1827                                 tmpx -= singleWidth(rit_par, body_pos - 1);
1828                 }
1829
1830                 if (hfillExpansion(*this, rit, c)) {
1831                         tmpx += singleWidth(rit_par, c);
1832                         if (c >= body_pos)
1833                                 tmpx += fill_hfill;
1834                         else
1835                                 tmpx += fill_label_hfill;
1836                 } else if (rit_par->isSeparator(c)) {
1837                         tmpx += singleWidth(rit_par, c);
1838                         if (c >= body_pos)
1839                                 tmpx+= fill_separator;
1840                 } else {
1841                         tmpx += singleWidth(rit_par, c);
1842                 }
1843                 ++vc;
1844         }
1845
1846         if ((tmpx + last_tmpx) / 2 > x) {
1847                 tmpx = last_tmpx;
1848                 left_side = true;
1849         }
1850
1851         if (vc > last + 1)  // This shouldn't happen.
1852                 vc = last + 1;
1853
1854         boundary = false;
1855         // This (rtl_support test) is not needed, but gives
1856         // some speedup if rtl_support=false
1857         RowList::iterator next_rit = boost::next(rit);
1858
1859         bool const lastrow = lyxrc.rtl_support &&
1860                 (next_rit == rowlist_.end() ||
1861                  next_rit->par() != rit_par);
1862
1863         // If lastrow is false, we don't need to compute
1864         // the value of rtl.
1865         bool const rtl = (lastrow)
1866                 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1867                 : false;
1868         if (lastrow &&
1869                  ((rtl &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
1870                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
1871                 c = last + 1;
1872         else if (vc == rit->pos()) {
1873                 c = vis2log(vc);
1874                 if (bidi_level(c) % 2 == 1)
1875                         ++c;
1876         } else {
1877                 c = vis2log(vc - 1);
1878                 bool const rtl = (bidi_level(c) % 2 == 1);
1879                 if (left_side == rtl) {
1880                         ++c;
1881                         boundary = isBoundary(bv()->buffer(), *rit_par, c);
1882                 }
1883         }
1884
1885         if (rit->pos() <= last && c > last
1886             && rit_par->isNewline(last)) {
1887                 if (bidi_level(last) % 2 == 0)
1888                         tmpx -= singleWidth(rit_par, last);
1889                 else
1890                         tmpx += singleWidth(rit_par, last);
1891                 c = last;
1892         }
1893
1894         c -= rit->pos();
1895         x = int(tmpx);
1896         return c;
1897 }
1898
1899
1900 void LyXText::setCursorFromCoordinates(int x, int y)
1901 {
1902         LyXCursor old_cursor = cursor;
1903
1904         setCursorFromCoordinates(cursor, x, y);
1905         setCurrentFont();
1906         deleteEmptyParagraphMechanism(old_cursor);
1907 }
1908
1909
1910 namespace {
1911
1912         /**
1913          * return true if the cursor given is at the end of a row,
1914          * and the next row is filled by an inset that spans an entire
1915          * row.
1916          */
1917         bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1918         {
1919                 RowList::iterator row = lt.getRow(cur);
1920                 if (boost::next(row) == lt.rows().end())
1921                         return false;
1922
1923                 Row const & next = *boost::next(row);
1924
1925                 if (next.pos() != cur.pos() || next.par() != cur.par())
1926                         return false;
1927
1928                 if (cur.pos() == cur.par()->size()
1929                     || !cur.par()->isInset(cur.pos()))
1930                         return false;
1931
1932                 Inset const * inset = cur.par()->getInset(cur.pos());
1933                 if (inset->needFullRow() || inset->display())
1934                         return true;
1935
1936                 return false;
1937         }
1938 }
1939
1940
1941 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1942 {
1943         // Get the row first.
1944
1945         RowList::iterator row = getRowNearY(y);
1946         bool bound = false;
1947         pos_type const column = getColumnNearX(row, x, bound);
1948         cur.par(row->par());
1949         cur.pos(row->pos() + column);
1950         cur.x(x);
1951         cur.y(y + row->baseline());
1952
1953         if (beforeFullRowInset(*this, cur)) {
1954                 pos_type const last = lastPrintablePos(*this, row);
1955                 RowList::iterator next_row = boost::next(row);
1956
1957                 float x = getCursorX(next_row, cur.pos(), last, bound);
1958                 cur.ix(int(x));
1959                 cur.iy(y + row->height() + next_row->baseline());
1960                 cur.irow(next_row);
1961         } else {
1962                 cur.iy(cur.y());
1963                 cur.ix(cur.x());
1964                 cur.irow(row);
1965         }
1966         cur.boundary(bound);
1967 }
1968
1969
1970 void LyXText::cursorLeft(bool internal)
1971 {
1972         if (cursor.pos() > 0) {
1973                 bool boundary = cursor.boundary();
1974                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1975                 if (!internal && !boundary &&
1976                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1977                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
1978         } else if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph.
1979                 ParagraphList::iterator pit = boost::prior(cursor.par());
1980                 setCursor(pit, pit->size());
1981         }
1982 }
1983
1984
1985 void LyXText::cursorRight(bool internal)
1986 {
1987         bool const at_end = (cursor.pos() == cursor.par()->size());
1988         bool const at_newline = !at_end &&
1989                 cursor.par()->isNewline(cursor.pos());
1990
1991         if (!internal && cursor.boundary() && !at_newline)
1992                 setCursor(cursor.par(), cursor.pos(), true, false);
1993         else if (!at_end) {
1994                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1995                 if (!internal &&
1996                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1997                         setCursor(cursor.par(), cursor.pos(), true, true);
1998         } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1999                 setCursor(boost::next(cursor.par()), 0);
2000 }
2001
2002
2003 void LyXText::cursorUp(bool selecting)
2004 {
2005 #if 1
2006         int x = cursor.x_fix();
2007         int y = cursor.y() - cursorRow()->baseline() - 1;
2008         setCursorFromCoordinates(x, y);
2009         if (!selecting) {
2010                 int topy = top_y();
2011                 int y1 = cursor.iy() - topy;
2012                 int y2 = y1;
2013                 y -= topy;
2014                 Inset * inset_hit = checkInsetHit(x, y1);
2015                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2016                         inset_hit->localDispatch(
2017                                 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
2018                 }
2019         }
2020 #else
2021         setCursorFromCoordinates(bv(), cursor.x_fix(),
2022                                  cursor.y() - cursorRow()->baseline() - 1);
2023 #endif
2024 }
2025
2026
2027 void LyXText::cursorDown(bool selecting)
2028 {
2029 #if 1
2030         int x = cursor.x_fix();
2031         int y = cursor.y() - cursorRow()->baseline() +
2032                 cursorRow()->height() + 1;
2033         setCursorFromCoordinates(x, y);
2034         if (!selecting && cursorRow() == cursor.irow()) {
2035                 int topy = top_y();
2036                 int y1 = cursor.iy() - topy;
2037                 int y2 = y1;
2038                 y -= topy;
2039                 Inset * inset_hit = checkInsetHit(x, y1);
2040                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2041                         FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
2042                         inset_hit->localDispatch(cmd);
2043                 }
2044         }
2045 #else
2046         setCursorFromCoordinates(bv(), cursor.x_fix(),
2047                                  cursor.y() - cursorRow()->baseline()
2048                                  + cursorRow()->height() + 1);
2049 #endif
2050 }
2051
2052
2053 void LyXText::cursorUpParagraph()
2054 {
2055         if (cursor.pos() > 0) {
2056                 setCursor(cursor.par(), 0);
2057         }
2058         else if (cursor.par() != ownerParagraphs().begin()) {
2059                 setCursor(boost::prior(cursor.par()), 0);
2060         }
2061 }
2062
2063
2064 void LyXText::cursorDownParagraph()
2065 {
2066         ParagraphList::iterator par = cursor.par();
2067         ParagraphList::iterator next_par = boost::next(par);
2068
2069         if (next_par != ownerParagraphs().end()) {
2070                 setCursor(next_par, 0);
2071         } else {
2072                 setCursor(par, par->size());
2073         }
2074 }
2075
2076 // fix the cursor `cur' after a characters has been deleted at `where'
2077 // position. Called by deleteEmptyParagraphMechanism
2078 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2079                                    LyXCursor const & where)
2080 {
2081         // if cursor is not in the paragraph where the delete occured,
2082         // do nothing
2083         if (cur.par() != where.par())
2084                 return;
2085
2086         // if cursor position is after the place where the delete occured,
2087         // update it
2088         if (cur.pos() > where.pos())
2089                 cur.pos(cur.pos()-1);
2090
2091         // check also if we don't want to set the cursor on a spot behind the
2092         // pagragraph because we erased the last character.
2093         if (cur.pos() > cur.par()->size())
2094                 cur.pos(cur.par()->size());
2095
2096         // recompute row et al. for this cursor
2097         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2098 }
2099
2100
2101 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2102 {
2103         // Would be wrong to delete anything if we have a selection.
2104         if (selection.set())
2105                 return false;
2106
2107         // We allow all kinds of "mumbo-jumbo" when freespacing.
2108         if (old_cursor.par()->layout()->free_spacing
2109             || old_cursor.par()->isFreeSpacing()) {
2110                 return false;
2111         }
2112
2113         /* Ok I'll put some comments here about what is missing.
2114            I have fixed BackSpace (and thus Delete) to not delete
2115            double-spaces automagically. I have also changed Cut,
2116            Copy and Paste to hopefully do some sensible things.
2117            There are still some small problems that can lead to
2118            double spaces stored in the document file or space at
2119            the beginning of paragraphs. This happens if you have
2120            the cursor betwenn to spaces and then save. Or if you
2121            cut and paste and the selection have a space at the
2122            beginning and then save right after the paste. I am
2123            sure none of these are very hard to fix, but I will
2124            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2125            that I can get some feedback. (Lgb)
2126         */
2127
2128         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2129         // delete the LineSeparator.
2130         // MISSING
2131
2132         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2133         // delete the LineSeparator.
2134         // MISSING
2135
2136         // If the pos around the old_cursor were spaces, delete one of them.
2137         if (old_cursor.par() != cursor.par()
2138             || old_cursor.pos() != cursor.pos()) {
2139                 // Only if the cursor has really moved
2140
2141                 if (old_cursor.pos() > 0
2142                     && old_cursor.pos() < old_cursor.par()->size()
2143                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2144                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2145                         old_cursor.par()->erase(old_cursor.pos() - 1);
2146                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2147
2148 #ifdef WITH_WARNINGS
2149 #warning This will not work anymore when we have multiple views of the same buffer
2150 // In this case, we will have to correct also the cursors held by
2151 // other bufferviews. It will probably be easier to do that in a more
2152 // automated way in LyXCursor code. (JMarc 26/09/2001)
2153 #endif
2154                         // correct all cursors held by the LyXText
2155                         fixCursorAfterDelete(cursor, old_cursor);
2156                         fixCursorAfterDelete(selection.cursor,
2157                                              old_cursor);
2158                         fixCursorAfterDelete(selection.start,
2159                                              old_cursor);
2160                         fixCursorAfterDelete(selection.end, old_cursor);
2161                         fixCursorAfterDelete(last_sel_cursor,
2162                                              old_cursor);
2163                         fixCursorAfterDelete(toggle_cursor, old_cursor);
2164                         fixCursorAfterDelete(toggle_end_cursor,
2165                                              old_cursor);
2166                         return false;
2167                 }
2168         }
2169
2170         // don't delete anything if this is the ONLY paragraph!
2171         if (ownerParagraphs().size() == 1)
2172                 return false;
2173
2174         // Do not delete empty paragraphs with keepempty set.
2175         if (old_cursor.par()->allowEmpty())
2176                 return false;
2177
2178         // only do our magic if we changed paragraph
2179         if (old_cursor.par() == cursor.par())
2180                 return false;
2181
2182         // record if we have deleted a paragraph
2183         // we can't possibly have deleted a paragraph before this point
2184         bool deleted = false;
2185
2186         if (old_cursor.par()->empty() ||
2187             (old_cursor.par()->size() == 1 &&
2188              old_cursor.par()->isLineSeparator(0))) {
2189                 // ok, we will delete anything
2190                 LyXCursor tmpcursor;
2191
2192                 deleted = true;
2193
2194                 bool selection_position_was_oldcursor_position = (
2195                         selection.cursor.par()  == old_cursor.par()
2196                         && selection.cursor.pos() == old_cursor.pos());
2197
2198                 if (getRow(old_cursor) != rows().begin()) {
2199                         RowList::iterator
2200                                 prevrow = boost::prior(getRow(old_cursor));
2201                         postPaint(old_cursor.y() - getRow(old_cursor)->baseline() - prevrow->height());
2202                         tmpcursor = cursor;
2203                         cursor = old_cursor; // that undo can restore the right cursor position
2204                         #warning FIXME. --end() iterator is usable here
2205                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2206                         while (endpit != ownerParagraphs().end() &&
2207                                endpit->getDepth()) {
2208                                 ++endpit;
2209                         }
2210
2211                         setUndo(bv(), Undo::DELETE, old_cursor.par(),
2212                                 boost::prior(endpit));
2213                         cursor = tmpcursor;
2214
2215                         // delete old row
2216                         removeRow(getRow(old_cursor));
2217                         // delete old par
2218                         ownerParagraphs().erase(old_cursor.par());
2219
2220                         /* Breakagain the next par. Needed because of
2221                          * the parindent that can occur or dissappear.
2222                          * The next row can change its height, if
2223                          * there is another layout before */
2224                         RowList::iterator tmprit = boost::next(prevrow);
2225                         if (tmprit != rows().end()) {
2226                                 breakAgain(tmprit);
2227                                 updateCounters();
2228                         }
2229                         setHeightOfRow(prevrow);
2230                 } else {
2231                         RowList::iterator nextrow = boost::next(getRow(old_cursor));
2232                         postPaint(old_cursor.y() - getRow(old_cursor)->baseline());
2233
2234                         tmpcursor = cursor;
2235                         cursor = old_cursor; // that undo can restore the right cursor position
2236 #warning FIXME. --end() iterator is usable here
2237                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2238                         while (endpit != ownerParagraphs().end() &&
2239                                endpit->getDepth()) {
2240                                 ++endpit;
2241                         }
2242
2243                         setUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2244                         cursor = tmpcursor;
2245
2246                         // delete old row
2247                         removeRow(getRow(old_cursor));
2248                         // delete old par
2249                         ownerParagraphs().erase(old_cursor.par());
2250
2251                         /* Breakagain the next par. Needed because of
2252                            the parindent that can occur or dissappear.
2253                            The next row can change its height, if
2254                            there is another layout before */
2255                         if (nextrow != rows().end()) {
2256                                 breakAgain(nextrow);
2257                                 updateCounters();
2258                         }
2259                 }
2260
2261                 // correct cursor y
2262                 setCursorIntern(cursor.par(), cursor.pos());
2263
2264                 if (selection_position_was_oldcursor_position) {
2265                         // correct selection
2266                         selection.cursor = cursor;
2267                 }
2268         }
2269         if (!deleted) {
2270                 if (old_cursor.par()->stripLeadingSpaces()) {
2271                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2272                         // correct cursor y
2273                         setCursorIntern(cursor.par(), cursor.pos());
2274                         selection.cursor = cursor;
2275                 }
2276         }
2277         return deleted;
2278 }
2279
2280
2281 ParagraphList & LyXText::ownerParagraphs() const
2282 {
2283         if (inset_owner) {
2284                 return inset_owner->paragraphs;
2285         }
2286         return bv_owner->buffer()->paragraphs;
2287 }
2288
2289
2290 LyXText::refresh_status LyXText::refreshStatus() const
2291 {
2292         return refresh_status_;
2293 }
2294
2295
2296 void LyXText::clearPaint()
2297 {
2298         refresh_status_ = REFRESH_NONE;
2299         refresh_row = rows().end();
2300         refresh_y = 0;
2301 }
2302
2303
2304 void LyXText::postPaint(int start_y)
2305 {
2306         refresh_status old = refresh_status_;
2307
2308         refresh_status_ = REFRESH_AREA;
2309         refresh_row = rows().end();
2310
2311         if (old != REFRESH_NONE && refresh_y < start_y)
2312                 return;
2313
2314         refresh_y = start_y;
2315
2316         if (!inset_owner)
2317                 return;
2318
2319         // We are an inset's lyxtext. Tell the top-level lyxtext
2320         // it needs to update the row we're in.
2321         LyXText * t = bv()->text;
2322         t->postRowPaint(t->cursorRow(), t->cursor.y() - t->cursorRow()->baseline());
2323 }
2324
2325
2326 // FIXME: we should probably remove this y parameter,
2327 // make refresh_y be 0, and use row->y etc.
2328 void LyXText::postRowPaint(RowList::iterator rit, int start_y)
2329 {
2330         if (refresh_status_ != REFRESH_NONE && refresh_y < start_y) {
2331                 refresh_status_ = REFRESH_AREA;
2332                 return;
2333         } else {
2334                 refresh_y = start_y;
2335         }
2336
2337         if (refresh_status_ == REFRESH_AREA)
2338                 return;
2339
2340         refresh_status_ = REFRESH_ROW;
2341         refresh_row = rit;
2342
2343         if (!inset_owner)
2344                 return;
2345
2346         // We are an inset's lyxtext. Tell the top-level lyxtext
2347         // it needs to update the row we're in.
2348         LyXText * t = bv()->text;
2349         t->postRowPaint(t->cursorRow(), t->cursor.y() - t->cursorRow()->baseline());
2350 }
2351
2352
2353 bool LyXText::isInInset() const
2354 {
2355         // Sub-level has non-null bv owner and
2356         // non-null inset owner.
2357         return inset_owner != 0 && bv_owner != 0;
2358 }
2359
2360
2361 int defaultRowHeight()
2362 {
2363         LyXFont const font(LyXFont::ALL_SANE);
2364         return int(font_metrics::maxAscent(font)
2365                  + font_metrics::maxDescent(font) * 1.5);
2366 }