]> git.lyx.org Git - lyx.git/blob - src/text2.C
* disable loading of xpm files by the xforms image loader.
[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         string idxstring;
1043
1044         // Try implicit word selection
1045         // If there is a change in the language the implicit word selection
1046         // is disabled.
1047         LyXCursor const reset_cursor = cursor;
1048         bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1049
1050         if (!selection.set()) {
1051                 bview->owner()->message(_("Nothing to index!"));
1052                 return string();
1053         }
1054         if (selection.start.par() != selection.end.par()) {
1055                 bview->owner()->message(_("Cannot index more than one paragraph!"));
1056                 return string();
1057         }
1058
1059         idxstring = selectionAsString(bview->buffer(), false);
1060
1061         // Implicit selections are cleared afterwards
1062         //and cursor is set to the original position.
1063         if (implicitSelection) {
1064                 clearSelection();
1065                 cursor = reset_cursor;
1066                 setCursor(bview, cursor.par(), cursor.pos());
1067                 selection.cursor = cursor;
1068         }
1069         return idxstring;
1070 }
1071
1072
1073 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1074                              Paragraph const * par) const
1075 {
1076         if (par->layout()->labeltype != LABEL_MANUAL)
1077                 return 0;
1078         else
1079                 return par->beginningOfMainBody();
1080 }
1081
1082
1083 // the DTP switches for paragraphs. LyX will store them in the first
1084 // physicla paragraph. When a paragraph is broken, the top settings rest,
1085 // the bottom settings are given to the new one. So I can make shure,
1086 // they do not duplicate themself and you cannnot make dirty things with
1087 // them!
1088
1089 void LyXText::setParagraph(BufferView * bview,
1090                            bool line_top, bool line_bottom,
1091                            bool pagebreak_top, bool pagebreak_bottom,
1092                            VSpace const & space_top,
1093                            VSpace const & space_bottom,
1094                            Spacing const & spacing,
1095                            LyXAlignment align,
1096                            string labelwidthstring,
1097                            bool noindent)
1098 {
1099         LyXCursor tmpcursor = cursor;
1100         if (!selection.set()) {
1101                 selection.start = cursor;
1102                 selection.end = cursor;
1103         }
1104
1105         // make sure that the depth behind the selection are restored, too
1106         Paragraph * endpar = selection.end.par()->next();
1107         Paragraph * undoendpar = endpar;
1108
1109         if (endpar && endpar->getDepth()) {
1110                 while (endpar && endpar->getDepth()) {
1111                         endpar = endpar->next();
1112                         undoendpar = endpar;
1113                 }
1114         }
1115         else if (endpar) {
1116                 // because of parindents etc.
1117                 endpar = endpar->next();
1118         }
1119
1120         setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1121
1122
1123         Paragraph * tmppar = selection.end.par();
1124
1125         while (tmppar != selection.start.par()->previous()) {
1126                 setCursor(bview, tmppar, 0);
1127                 status(bview, LyXText::NEED_MORE_REFRESH);
1128                 refresh_row = cursor.row();
1129                 refresh_y = cursor.y() - cursor.row()->baseline();
1130                 cursor.par()->params().lineTop(line_top);
1131                 cursor.par()->params().lineBottom(line_bottom);
1132                 cursor.par()->params().pagebreakTop(pagebreak_top);
1133                 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1134                 cursor.par()->params().spaceTop(space_top);
1135                 cursor.par()->params().spaceBottom(space_bottom);
1136                 cursor.par()->params().spacing(spacing);
1137                 // does the layout allow the new alignment?
1138                 LyXLayout_ptr const & layout = cursor.par()->layout();
1139
1140                 if (align == LYX_ALIGN_LAYOUT)
1141                         align = layout->align;
1142                 if (align & layout->alignpossible) {
1143                         if (align == layout->align)
1144                                 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1145                         else
1146                                 cursor.par()->params().align(align);
1147                 }
1148                 cursor.par()->setLabelWidthString(labelwidthstring);
1149                 cursor.par()->params().noindent(noindent);
1150                 tmppar = cursor.par()->previous();
1151         }
1152
1153         redoParagraphs(bview, selection.start, endpar);
1154
1155         clearSelection();
1156         setCursor(bview, selection.start.par(), selection.start.pos());
1157         selection.cursor = cursor;
1158         setCursor(bview, selection.end.par(), selection.end.pos());
1159         setSelection(bview);
1160         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1161         if (inset_owner)
1162                 bview->updateInset(inset_owner, true);
1163 }
1164
1165
1166 // set the counter of a paragraph. This includes the labels
1167 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1168 {
1169         LyXTextClass const & textclass = buf->params.getLyXTextClass();
1170         LyXLayout_ptr const & layout = par->layout();
1171
1172         if (par->previous()) {
1173
1174                 par->params().appendix(par->previous()->params().appendix());
1175                 if (!par->params().appendix() && par->params().startOfAppendix()) {
1176                         par->params().appendix(true);
1177                         textclass.counters().reset();
1178                 }
1179                 par->enumdepth = par->previous()->enumdepth;
1180                 par->itemdepth = par->previous()->itemdepth;
1181         } else {
1182                 par->params().appendix(par->params().startOfAppendix());
1183                 par->enumdepth = 0;
1184                 par->itemdepth = 0;
1185         }
1186
1187         /* Maybe we have to increment the enumeration depth.
1188          * BUT, enumeration in a footnote is considered in isolation from its
1189          *      surrounding paragraph so don't increment if this is the
1190          *      first line of the footnote
1191          * AND, bibliographies can't have their depth changed ie. they
1192          *      are always of depth 0
1193          */
1194         if (par->previous()
1195             && par->previous()->getDepth() < par->getDepth()
1196             && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1197             && par->enumdepth < 3
1198             && layout->labeltype != LABEL_BIBLIO) {
1199                 par->enumdepth++;
1200         }
1201
1202         // Maybe we have to decrement the enumeration depth, see note above
1203         if (par->previous()
1204             && par->previous()->getDepth() > par->getDepth()
1205             && layout->labeltype != LABEL_BIBLIO) {
1206                 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1207         }
1208
1209         if (!par->params().labelString().empty()) {
1210                 par->params().labelString(string());
1211         }
1212
1213         if (layout->margintype == MARGIN_MANUAL) {
1214                 if (par->params().labelWidthString().empty()) {
1215                         par->setLabelWidthString(layout->labelstring());
1216                 }
1217         } else {
1218                 par->setLabelWidthString(string());
1219         }
1220
1221         // is it a layout that has an automatic label?
1222         if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1223                 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1224
1225                 ostringstream s;
1226
1227                 if (i >= 0 && i <= buf->params.secnumdepth) {
1228                         string numbertype;
1229                         string langtype;
1230
1231                         textclass.counters().step(layout->latexname());
1232
1233                         // Is there a label? Useful for Chapter layout
1234                         if (!par->params().appendix()) {
1235                                 s << layout->labelstring();
1236                         } else {
1237                                 s << layout->labelstring_appendix();
1238                         }
1239
1240                         // Use of an integer is here less than elegant. For now.
1241                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1242                         if (!par->params().appendix()) {
1243                                 numbertype = "sectioning";
1244                         } else {
1245                                 numbertype = "appendix";
1246                                 if (par->isRightToLeftPar(buf->params))
1247                                         langtype = "hebrew";
1248                                 else
1249                                         langtype = "latin";
1250                         }
1251
1252                         s << textclass.counters()
1253                                 .numberLabel(layout->latexname(),
1254                                              numbertype, langtype, head);
1255
1256                         par->params().labelString(STRCONV(s.str()));
1257
1258                         // reset enum counters
1259                         textclass.counters().reset("enum");
1260                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1261                         textclass.counters().reset("enum");
1262                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1263                         // FIXME
1264                         // Yes I know this is a really, really! bad solution
1265                         // (Lgb)
1266                         string enumcounter("enum");
1267
1268                         switch (par->enumdepth) {
1269                         case 2:
1270                                 enumcounter += 'i';
1271                         case 1:
1272                                 enumcounter += 'i';
1273                         case 0:
1274                                 enumcounter += 'i';
1275                                 break;
1276                         case 3:
1277                                 enumcounter += "iv";
1278                                 break;
1279                         default:
1280                                 // not a valid enumdepth...
1281                                 break;
1282                         }
1283
1284                         textclass.counters().step(enumcounter);
1285
1286                         s << textclass.counters()
1287                                 .numberLabel(enumcounter, "enumeration");
1288                         par->params().labelString(STRCONV(s.str()));
1289                 }
1290         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1291                 textclass.counters().step("bibitem");
1292                 int number = textclass.counters().value("bibitem");
1293                 if (!par->bibkey) {
1294                         InsetCommandParams p("bibitem");
1295                         par->bibkey = new InsetBibKey(p);
1296                 }
1297                 par->bibkey->setCounter(number);
1298                 par->params().labelString(layout->labelstring());
1299
1300                 // In biblio should't be following counters but...
1301         } else {
1302                 string s = layout->labelstring();
1303
1304                 // the caption hack:
1305                 if (layout->labeltype == LABEL_SENSITIVE) {
1306                         Paragraph * tmppar = par;
1307                         Inset * in = 0;
1308                         bool isOK = false;
1309                         while (tmppar && tmppar->inInset()
1310                                // the single '=' is intended below
1311                                && (in = tmppar->inInset()->owner())) {
1312                                 if (in->lyxCode() == Inset::FLOAT_CODE ||
1313                                     in->lyxCode() == Inset::WRAP_CODE) {
1314                                         isOK = true;
1315                                         break;
1316                                 } else {
1317                                         tmppar = in->parOwner();
1318                                 }
1319                         }
1320
1321                         if (isOK) {
1322                                 Floating const & fl
1323                                         = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1324
1325                                 textclass.counters().step(fl.type());
1326
1327                                 // Doesn't work... yet.
1328 #warning use boost.format
1329 #if USE_BOOST_FORMAT
1330                                 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1331                                 // s << boost::format(_("%1$s %1$d:")
1332                                 //        % fl.name()
1333                                 //        % buf->counters().value(fl.name());
1334 #else
1335                                 ostringstream o;
1336                                 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1337                                 o << fl.name() << " #:";
1338                                 s = STRCONV(o.str());
1339 #endif
1340                         } else {
1341                                 // par->SetLayout(0);
1342                                 // s = layout->labelstring;
1343                                 s = _("Senseless: ");
1344                         }
1345                 }
1346                 par->params().labelString(s);
1347
1348                 // reset the enumeration counter. They are always reset
1349                 // when there is any other layout between
1350                 // Just fall-through between the cases so that all
1351                 // enum counters deeper than enumdepth is also reset.
1352                 switch (par->enumdepth) {
1353                 case 0:
1354                         textclass.counters().reset("enumi");
1355                 case 1:
1356                         textclass.counters().reset("enumii");
1357                 case 2:
1358                         textclass.counters().reset("enumiii");
1359                 case 3:
1360                         textclass.counters().reset("enumiv");
1361                 }
1362         }
1363 }
1364
1365
1366 // Updates all counters. Paragraphs with changed label string will be rebroken
1367 void LyXText::updateCounters(BufferView * bview) const
1368 {
1369         Row * row = firstrow;
1370         Paragraph * par = row->par();
1371
1372         // CHECK if this is really needed. (Lgb)
1373         bview->buffer()->params.getLyXTextClass().counters().reset();
1374
1375         while (par) {
1376                 while (row->par() != par)
1377                         row = row->next();
1378
1379                 string const oldLabel = par->params().labelString();
1380
1381                 // setCounter can potentially change the labelString.
1382                 setCounter(bview->buffer(), par);
1383
1384                 string const & newLabel = par->params().labelString();
1385
1386                 if (oldLabel.empty() && !newLabel.empty()) {
1387                         removeParagraph(row);
1388                         appendParagraph(bview, row);
1389                 }
1390
1391                 par = par->next();
1392         }
1393 }
1394
1395
1396 void LyXText::insertInset(BufferView * bview, Inset * inset)
1397 {
1398         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1399                 return;
1400         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1401         freezeUndo();
1402         cursor.par()->insertInset(cursor.pos(), inset);
1403         // Just to rebreak and refresh correctly.
1404         // The character will not be inserted a second time
1405         insertChar(bview, Paragraph::META_INSET);
1406         // If we enter a highly editable inset the cursor should be to before
1407         // the inset. This couldn't happen before as Undo was not handled inside
1408         // inset now after the Undo LyX tries to call inset->Edit(...) again
1409         // and cannot do this as the cursor is behind the inset and GetInset
1410         // does not return the inset!
1411         if (isHighlyEditableInset(inset)) {
1412                 cursorLeft(bview, true);
1413         }
1414         unFreezeUndo();
1415 }
1416
1417
1418 void LyXText::copyEnvironmentType()
1419 {
1420         copylayouttype = cursor.par()->layout()->name();
1421 }
1422
1423
1424 void LyXText::pasteEnvironmentType(BufferView * bview)
1425 {
1426         // do nothing if there has been no previous copyEnvironmentType()
1427         if (!copylayouttype.empty())
1428                 setLayout(bview, copylayouttype);
1429 }
1430
1431
1432 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1433 {
1434         // Stuff what we got on the clipboard. Even if there is no selection.
1435
1436         // There is a problem with having the stuffing here in that the
1437         // larger the selection the slower LyX will get. This can be
1438         // solved by running the line below only when the selection has
1439         // finished. The solution used currently just works, to make it
1440         // faster we need to be more clever and probably also have more
1441         // calls to stuffClipboard. (Lgb)
1442         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1443
1444         // This doesn't make sense, if there is no selection
1445         if (!selection.set())
1446                 return;
1447
1448         // OK, we have a selection. This is always between selection.start
1449         // and selection.end
1450
1451         // make sure that the depth behind the selection are restored, too
1452         Paragraph * endpar = selection.end.par()->next();
1453         Paragraph * undoendpar = endpar;
1454
1455         if (endpar && endpar->getDepth()) {
1456                 while (endpar && endpar->getDepth()) {
1457                         endpar = endpar->next();
1458                         undoendpar = endpar;
1459                 }
1460         } else if (endpar) {
1461                 endpar = endpar->next(); // because of parindents etc.
1462         }
1463
1464         setUndo(bview, Undo::DELETE,
1465                 selection.start.par(), undoendpar);
1466
1467         // there are two cases: cut only within one paragraph or
1468         // more than one paragraph
1469         if (selection.start.par() == selection.end.par()) {
1470                 // only within one paragraph
1471                 endpar = selection.end.par();
1472                 int pos = selection.end.pos();
1473                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1474                                           selection.start.pos(), pos,
1475                                           bview->buffer()->params.textclass,
1476                                           doclear, realcut);
1477                 selection.end.pos(pos);
1478         } else {
1479                 endpar = selection.end.par();
1480                 int pos = selection.end.pos();
1481                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1482                                           selection.start.pos(), pos,
1483                                           bview->buffer()->params.textclass,
1484                                           doclear, realcut);
1485                 cursor.par(endpar);
1486                 selection.end.par(endpar);
1487                 selection.end.pos(pos);
1488                 cursor.pos(selection.end.pos());
1489         }
1490         endpar = endpar->next();
1491
1492         // sometimes necessary
1493         if (doclear)
1494                 selection.start.par()->stripLeadingSpaces();
1495
1496         redoParagraphs(bview, selection.start, endpar);
1497
1498         // cutSelection can invalidate the cursor so we need to set
1499         // it anew. (Lgb)
1500         cursor = selection.start;
1501
1502         // need a valid cursor. (Lgb)
1503         clearSelection();
1504
1505         setCursor(bview, cursor.par(), cursor.pos());
1506         selection.cursor = cursor;
1507         updateCounters(bview);
1508 }
1509
1510
1511 void LyXText::copySelection(BufferView * bview)
1512 {
1513         // stuff the selection onto the X clipboard, from an explicit copy request
1514         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1515
1516         // this doesnt make sense, if there is no selection
1517         if (!selection.set())
1518                 return;
1519
1520         // ok we have a selection. This is always between selection.start
1521         // and sel_end cursor
1522
1523         // copy behind a space if there is one
1524         while (selection.start.par()->size() > selection.start.pos()
1525                && selection.start.par()->isLineSeparator(selection.start.pos())
1526                && (selection.start.par() != selection.end.par()
1527                    || selection.start.pos() < selection.end.pos()))
1528                 selection.start.pos(selection.start.pos() + 1);
1529
1530         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1531                                    selection.start.pos(), selection.end.pos(),
1532                                    bview->buffer()->params.textclass);
1533 }
1534
1535
1536 void LyXText::pasteSelection(BufferView * bview)
1537 {
1538         // this does not make sense, if there is nothing to paste
1539         if (!CutAndPaste::checkPastePossible(cursor.par()))
1540                 return;
1541
1542         setUndo(bview, Undo::INSERT,
1543                 cursor.par(), cursor.par()->next());
1544
1545         Paragraph * endpar;
1546         Paragraph * actpar = cursor.par();
1547         int pos = cursor.pos();
1548
1549         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1550                                     bview->buffer()->params.textclass);
1551
1552         redoParagraphs(bview, cursor, endpar);
1553
1554         setCursor(bview, cursor.par(), cursor.pos());
1555         clearSelection();
1556
1557         selection.cursor = cursor;
1558         setCursor(bview, actpar, pos);
1559         setSelection(bview);
1560         updateCounters(bview);
1561 }
1562
1563
1564 // sets the selection over the number of characters of string, no check!!
1565 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1566 {
1567         if (str.empty())
1568                 return;
1569
1570         selection.cursor = cursor;
1571         for (string::size_type i = 0; i < str.length(); ++i)
1572                 cursorRight(bview);
1573         setSelection(bview);
1574 }
1575
1576
1577 // simple replacing. The font of the first selected character is used
1578 void LyXText::replaceSelectionWithString(BufferView * bview,
1579                                          string const & str)
1580 {
1581         setCursorParUndo(bview);
1582         freezeUndo();
1583
1584         if (!selection.set()) { // create a dummy selection
1585                 selection.end = cursor;
1586                 selection.start = cursor;
1587         }
1588
1589         // Get font setting before we cut
1590         pos_type pos = selection.end.pos();
1591         LyXFont const font = selection.start.par()
1592                 ->getFontSettings(bview->buffer()->params,
1593                                   selection.start.pos());
1594
1595         // Insert the new string
1596         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1597                 selection.end.par()->insertChar(pos, (*cit), font);
1598                 ++pos;
1599         }
1600
1601         // Cut the selection
1602         cutSelection(bview, true, false);
1603
1604         unFreezeUndo();
1605 }
1606
1607
1608 // needed to insert the selection
1609 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1610 {
1611         Paragraph * par = cursor.par();
1612         pos_type pos = cursor.pos();
1613         Paragraph * endpar = cursor.par()->next();
1614
1615         setCursorParUndo(bview);
1616
1617         // only to be sure, should not be neccessary
1618         clearSelection();
1619
1620         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1621
1622         redoParagraphs(bview, cursor, endpar);
1623         setCursor(bview, cursor.par(), cursor.pos());
1624         selection.cursor = cursor;
1625         setCursor(bview, par, pos);
1626         setSelection(bview);
1627 }
1628
1629
1630 // turns double-CR to single CR, others where converted into one
1631 // blank. Then InsertStringAsLines is called
1632 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1633 {
1634         string linestr(str);
1635         bool newline_inserted = false;
1636         for (string::size_type i = 0; i < linestr.length(); ++i) {
1637                 if (linestr[i] == '\n') {
1638                         if (newline_inserted) {
1639                                 // we know that \r will be ignored by
1640                                 // InsertStringA. Of course, it is a dirty
1641                                 // trick, but it works...
1642                                 linestr[i - 1] = '\r';
1643                                 linestr[i] = '\n';
1644                         } else {
1645                                 linestr[i] = ' ';
1646                                 newline_inserted = true;
1647                         }
1648                 } else if (IsPrintable(linestr[i])) {
1649                         newline_inserted = false;
1650                 }
1651         }
1652         insertStringAsLines(bview, linestr);
1653 }
1654
1655
1656 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1657                              pos_type pos)
1658 {
1659         LyXCursor tmpcursor;
1660
1661         int y = 0;
1662         pos_type z;
1663         Row * row = getRow(par, pos, y);
1664
1665         // is there a break one row above
1666         if (row->previous() && row->previous()->par() == row->par()) {
1667                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1668                 if (z >= row->pos()) {
1669                         // set the dimensions of the row above
1670                         y -= row->previous()->height();
1671                         refresh_y = y;
1672                         refresh_row = row->previous();
1673                         status(bview, LyXText::NEED_MORE_REFRESH);
1674
1675                         breakAgain(bview, row->previous());
1676
1677                         // set the cursor again. Otherwise
1678                         // dangling pointers are possible
1679                         setCursor(bview, cursor.par(), cursor.pos(),
1680                                   false, cursor.boundary());
1681                         selection.cursor = cursor;
1682                         return;
1683                 }
1684         }
1685
1686         int const tmpheight = row->height();
1687         pos_type const tmplast = rowLast(row);
1688         refresh_y = y;
1689         refresh_row = row;
1690
1691         breakAgain(bview, row);
1692         if (row->height() == tmpheight && rowLast(row) == tmplast)
1693                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1694         else
1695                 status(bview, LyXText::NEED_MORE_REFRESH);
1696
1697         // check the special right address boxes
1698         if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1699                 tmpcursor.par(par);
1700                 tmpcursor.row(row);
1701                 tmpcursor.y(y);
1702                 tmpcursor.x(0);
1703                 tmpcursor.x_fix(0);
1704                 tmpcursor.pos(pos);
1705                 redoDrawingOfParagraph(bview, tmpcursor);
1706         }
1707
1708         // set the cursor again. Otherwise dangling pointers are possible
1709         // also set the selection
1710
1711         if (selection.set()) {
1712                 tmpcursor = cursor;
1713                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1714                                 false, selection.cursor.boundary());
1715                 selection.cursor = cursor;
1716                 setCursorIntern(bview, selection.start.par(),
1717                                 selection.start.pos(),
1718                                 false, selection.start.boundary());
1719                 selection.start = cursor;
1720                 setCursorIntern(bview, selection.end.par(),
1721                                 selection.end.pos(),
1722                                 false, selection.end.boundary());
1723                 selection.end = cursor;
1724                 setCursorIntern(bview, last_sel_cursor.par(),
1725                                 last_sel_cursor.pos(),
1726                                 false, last_sel_cursor.boundary());
1727                 last_sel_cursor = cursor;
1728                 cursor = tmpcursor;
1729         }
1730         setCursorIntern(bview, cursor.par(), cursor.pos(),
1731                         false, cursor.boundary());
1732 }
1733
1734
1735 // returns false if inset wasn't found
1736 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1737 {
1738         // first check the current paragraph
1739         int pos = cursor.par()->getPositionOfInset(inset);
1740         if (pos != -1) {
1741                 checkParagraph(bview, cursor.par(), pos);
1742                 return true;
1743         }
1744
1745         // check every paragraph
1746
1747         Paragraph * par = ownerParagraph();
1748         do {
1749                 pos = par->getPositionOfInset(inset);
1750                 if (pos != -1) {
1751                         checkParagraph(bview, par, pos);
1752                         return true;
1753                 }
1754                 par = par->next();
1755         } while (par);
1756
1757         return false;
1758 }
1759
1760
1761 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1762                         pos_type pos,
1763                         bool setfont, bool boundary) const
1764 {
1765         LyXCursor old_cursor = cursor;
1766         setCursorIntern(bview, par, pos, setfont, boundary);
1767         return deleteEmptyParagraphMechanism(bview, old_cursor);
1768 }
1769
1770
1771 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1772                         pos_type pos, bool boundary) const
1773 {
1774         lyx::Assert(par);
1775         lyx::Assert(bview);
1776
1777         cur.par(par);
1778         cur.pos(pos);
1779         cur.boundary(boundary);
1780
1781         // get the cursor y position in text
1782         int y = 0;
1783         Row * row = getRow(par, pos, y);
1784         Row * old_row = row;
1785         cur.irow(row);
1786         // if we are before the first char of this row and are still in the
1787         // same paragraph and there is a previous row then put the cursor on
1788         // the end of the previous row
1789         cur.iy(y + row->baseline());
1790         Inset * ins;
1791         if (row->previous() && pos &&
1792                 row->previous()->par() == row->par() &&
1793                 par->getChar(pos) == Paragraph::META_INSET &&
1794                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1795         {
1796                 row = row->previous();
1797                 y -= row->height();
1798         }
1799
1800         cur.row(row);
1801         // y is now the beginning of the cursor row
1802         y += row->baseline();
1803         // y is now the cursor baseline
1804         cur.y(y);
1805
1806         pos_type last = rowLastPrintable(old_row);
1807
1808         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 }