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