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