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