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