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