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