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