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