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