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