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