]> git.lyx.org Git - lyx.git/blob - src/text2.C
Remove the XOpenIM test as lyxlookup.C has been buried.
[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         setLayout(bview, copylayouttype);
1427 }
1428
1429
1430 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1431 {
1432         // Stuff what we got on the clipboard. Even if there is no selection.
1433
1434         // There is a problem with having the stuffing here in that the
1435         // larger the selection the slower LyX will get. This can be
1436         // solved by running the line below only when the selection has
1437         // finished. The solution used currently just works, to make it
1438         // faster we need to be more clever and probably also have more
1439         // calls to stuffClipboard. (Lgb)
1440         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1441
1442         // This doesn't make sense, if there is no selection
1443         if (!selection.set())
1444                 return;
1445
1446         // OK, we have a selection. This is always between selection.start
1447         // and selection.end
1448
1449         // make sure that the depth behind the selection are restored, too
1450         Paragraph * endpar = selection.end.par()->next();
1451         Paragraph * undoendpar = endpar;
1452
1453         if (endpar && endpar->getDepth()) {
1454                 while (endpar && endpar->getDepth()) {
1455                         endpar = endpar->next();
1456                         undoendpar = endpar;
1457                 }
1458         } else if (endpar) {
1459                 endpar = endpar->next(); // because of parindents etc.
1460         }
1461
1462         setUndo(bview, Undo::DELETE,
1463                 selection.start.par(), undoendpar);
1464
1465         // there are two cases: cut only within one paragraph or
1466         // more than one paragraph
1467         if (selection.start.par() == selection.end.par()) {
1468                 // only within one paragraph
1469                 endpar = selection.end.par();
1470                 int pos = selection.end.pos();
1471                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1472                                           selection.start.pos(), pos,
1473                                           bview->buffer()->params.textclass,
1474                                           doclear, realcut);
1475                 selection.end.pos(pos);
1476         } else {
1477                 endpar = selection.end.par();
1478                 int pos = selection.end.pos();
1479                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1480                                           selection.start.pos(), pos,
1481                                           bview->buffer()->params.textclass,
1482                                           doclear, realcut);
1483                 cursor.par(endpar);
1484                 selection.end.par(endpar);
1485                 selection.end.pos(pos);
1486                 cursor.pos(selection.end.pos());
1487         }
1488         endpar = endpar->next();
1489
1490         // sometimes necessary
1491         if (doclear)
1492                 selection.start.par()->stripLeadingSpaces();
1493
1494         redoParagraphs(bview, selection.start, endpar);
1495
1496         // cutSelection can invalidate the cursor so we need to set
1497         // it anew. (Lgb)
1498         cursor = selection.start;
1499
1500         // need a valid cursor. (Lgb)
1501         clearSelection();
1502
1503         setCursor(bview, cursor.par(), cursor.pos());
1504         selection.cursor = cursor;
1505         updateCounters(bview);
1506 }
1507
1508
1509 void LyXText::copySelection(BufferView * bview)
1510 {
1511         // stuff the selection onto the X clipboard, from an explicit copy request
1512         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1513
1514         // this doesnt make sense, if there is no selection
1515         if (!selection.set())
1516                 return;
1517
1518         // ok we have a selection. This is always between selection.start
1519         // and sel_end cursor
1520
1521         // copy behind a space if there is one
1522         while (selection.start.par()->size() > selection.start.pos()
1523                && selection.start.par()->isLineSeparator(selection.start.pos())
1524                && (selection.start.par() != selection.end.par()
1525                    || selection.start.pos() < selection.end.pos()))
1526                 selection.start.pos(selection.start.pos() + 1);
1527
1528         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1529                                    selection.start.pos(), selection.end.pos(),
1530                                    bview->buffer()->params.textclass);
1531 }
1532
1533
1534 void LyXText::pasteSelection(BufferView * bview)
1535 {
1536         // this does not make sense, if there is nothing to paste
1537         if (!CutAndPaste::checkPastePossible(cursor.par()))
1538                 return;
1539
1540         setUndo(bview, Undo::INSERT,
1541                 cursor.par(), cursor.par()->next());
1542
1543         Paragraph * endpar;
1544         Paragraph * actpar = cursor.par();
1545         int pos = cursor.pos();
1546
1547         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1548                                     bview->buffer()->params.textclass);
1549
1550         redoParagraphs(bview, cursor, endpar);
1551
1552         setCursor(bview, cursor.par(), cursor.pos());
1553         clearSelection();
1554
1555         selection.cursor = cursor;
1556         setCursor(bview, actpar, pos);
1557         setSelection(bview);
1558         updateCounters(bview);
1559 }
1560
1561
1562 // sets the selection over the number of characters of string, no check!!
1563 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1564 {
1565         if (str.empty())
1566                 return;
1567
1568         selection.cursor = cursor;
1569         for (string::size_type i = 0; i < str.length(); ++i)
1570                 cursorRight(bview);
1571         setSelection(bview);
1572 }
1573
1574
1575 // simple replacing. The font of the first selected character is used
1576 void LyXText::replaceSelectionWithString(BufferView * bview,
1577                                          string const & str)
1578 {
1579         setCursorParUndo(bview);
1580         freezeUndo();
1581
1582         if (!selection.set()) { // create a dummy selection
1583                 selection.end = cursor;
1584                 selection.start = cursor;
1585         }
1586
1587         // Get font setting before we cut
1588         pos_type pos = selection.end.pos();
1589         LyXFont const font = selection.start.par()
1590                 ->getFontSettings(bview->buffer()->params,
1591                                   selection.start.pos());
1592
1593         // Insert the new string
1594         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1595                 selection.end.par()->insertChar(pos, (*cit), font);
1596                 ++pos;
1597         }
1598
1599         // Cut the selection
1600         cutSelection(bview, true, false);
1601
1602         unFreezeUndo();
1603 }
1604
1605
1606 // needed to insert the selection
1607 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1608 {
1609         Paragraph * par = cursor.par();
1610         pos_type pos = cursor.pos();
1611         Paragraph * endpar = cursor.par()->next();
1612
1613         setCursorParUndo(bview);
1614
1615         // only to be sure, should not be neccessary
1616         clearSelection();
1617
1618         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1619
1620         redoParagraphs(bview, cursor, endpar);
1621         setCursor(bview, cursor.par(), cursor.pos());
1622         selection.cursor = cursor;
1623         setCursor(bview, par, pos);
1624         setSelection(bview);
1625 }
1626
1627
1628 // turns double-CR to single CR, others where converted into one
1629 // blank. Then InsertStringAsLines is called
1630 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1631 {
1632         string linestr(str);
1633         bool newline_inserted = false;
1634         for (string::size_type i = 0; i < linestr.length(); ++i) {
1635                 if (linestr[i] == '\n') {
1636                         if (newline_inserted) {
1637                                 // we know that \r will be ignored by
1638                                 // InsertStringA. Of course, it is a dirty
1639                                 // trick, but it works...
1640                                 linestr[i - 1] = '\r';
1641                                 linestr[i] = '\n';
1642                         } else {
1643                                 linestr[i] = ' ';
1644                                 newline_inserted = true;
1645                         }
1646                 } else if (IsPrintable(linestr[i])) {
1647                         newline_inserted = false;
1648                 }
1649         }
1650         insertStringAsLines(bview, linestr);
1651 }
1652
1653
1654 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1655                              pos_type pos)
1656 {
1657         LyXCursor tmpcursor;
1658
1659         int y = 0;
1660         pos_type z;
1661         Row * row = getRow(par, pos, y);
1662
1663         // is there a break one row above
1664         if (row->previous() && row->previous()->par() == row->par()) {
1665                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1666                 if (z >= row->pos()) {
1667                         // set the dimensions of the row above
1668                         y -= row->previous()->height();
1669                         refresh_y = y;
1670                         refresh_row = row->previous();
1671                         status(bview, LyXText::NEED_MORE_REFRESH);
1672
1673                         breakAgain(bview, row->previous());
1674
1675                         // set the cursor again. Otherwise
1676                         // dangling pointers are possible
1677                         setCursor(bview, cursor.par(), cursor.pos(),
1678                                   false, cursor.boundary());
1679                         selection.cursor = cursor;
1680                         return;
1681                 }
1682         }
1683
1684         int const tmpheight = row->height();
1685         pos_type const tmplast = rowLast(row);
1686         refresh_y = y;
1687         refresh_row = row;
1688
1689         breakAgain(bview, row);
1690         if (row->height() == tmpheight && rowLast(row) == tmplast)
1691                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1692         else
1693                 status(bview, LyXText::NEED_MORE_REFRESH);
1694
1695         // check the special right address boxes
1696         if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1697                 tmpcursor.par(par);
1698                 tmpcursor.row(row);
1699                 tmpcursor.y(y);
1700                 tmpcursor.x(0);
1701                 tmpcursor.x_fix(0);
1702                 tmpcursor.pos(pos);
1703                 redoDrawingOfParagraph(bview, tmpcursor);
1704         }
1705
1706         // set the cursor again. Otherwise dangling pointers are possible
1707         // also set the selection
1708
1709         if (selection.set()) {
1710                 tmpcursor = cursor;
1711                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1712                                 false, selection.cursor.boundary());
1713                 selection.cursor = cursor;
1714                 setCursorIntern(bview, selection.start.par(),
1715                                 selection.start.pos(),
1716                                 false, selection.start.boundary());
1717                 selection.start = cursor;
1718                 setCursorIntern(bview, selection.end.par(),
1719                                 selection.end.pos(),
1720                                 false, selection.end.boundary());
1721                 selection.end = cursor;
1722                 setCursorIntern(bview, last_sel_cursor.par(),
1723                                 last_sel_cursor.pos(),
1724                                 false, last_sel_cursor.boundary());
1725                 last_sel_cursor = cursor;
1726                 cursor = tmpcursor;
1727         }
1728         setCursorIntern(bview, cursor.par(), cursor.pos(),
1729                         false, cursor.boundary());
1730 }
1731
1732
1733 // returns false if inset wasn't found
1734 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1735 {
1736         // first check the current paragraph
1737         int pos = cursor.par()->getPositionOfInset(inset);
1738         if (pos != -1) {
1739                 checkParagraph(bview, cursor.par(), pos);
1740                 return true;
1741         }
1742
1743         // check every paragraph
1744
1745         Paragraph * par = ownerParagraph();
1746         do {
1747                 pos = par->getPositionOfInset(inset);
1748                 if (pos != -1) {
1749                         checkParagraph(bview, par, pos);
1750                         return true;
1751                 }
1752                 par = par->next();
1753         } while (par);
1754
1755         return false;
1756 }
1757
1758
1759 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1760                         pos_type pos,
1761                         bool setfont, bool boundary) const
1762 {
1763         LyXCursor old_cursor = cursor;
1764         setCursorIntern(bview, par, pos, setfont, boundary);
1765         return deleteEmptyParagraphMechanism(bview, old_cursor);
1766 }
1767
1768
1769 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1770                         pos_type pos, bool boundary) const
1771 {
1772         lyx::Assert(par);
1773         lyx::Assert(bview);
1774
1775         cur.par(par);
1776         cur.pos(pos);
1777         cur.boundary(boundary);
1778
1779         // get the cursor y position in text
1780         int y = 0;
1781         Row * row = getRow(par, pos, y);
1782         Row * old_row = row;
1783         cur.irow(row);
1784         // if we are before the first char of this row and are still in the
1785         // same paragraph and there is a previous row then put the cursor on
1786         // the end of the previous row
1787         cur.iy(y + row->baseline());
1788         Inset * ins;
1789         if (row->previous() && pos &&
1790                 row->previous()->par() == row->par() &&
1791                 par->getChar(pos) == Paragraph::META_INSET &&
1792                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1793         {
1794                 row = row->previous();
1795                 y -= row->height();
1796         }
1797
1798         cur.row(row);
1799         // y is now the beginning of the cursor row
1800         y += row->baseline();
1801         // y is now the cursor baseline
1802         cur.y(y);
1803
1804         pos_type last = rowLastPrintable(old_row);
1805
1806         if (pos > last + 1) {
1807                 // This shouldn't happen.
1808                 pos = last + 1;
1809                 cur.pos(pos);
1810         } else if (pos < row->pos()) {
1811                 pos = row->pos();
1812                 cur.pos(pos);
1813         }
1814
1815         // now get the cursors x position
1816         float x = getCursorX(bview, row, pos, last, boundary);
1817         cur.x(int(x));
1818         cur.x_fix(cur.x());
1819         if (old_row != row) {
1820                 x = getCursorX(bview, old_row, pos, last, boundary);
1821                 cur.ix(int(x));
1822         } else
1823                 cur.ix(cur.x());
1824 }
1825
1826
1827 float LyXText::getCursorX(BufferView * bview, Row * row,
1828                                                   pos_type pos, pos_type last, bool boundary) const
1829 {
1830         pos_type cursor_vpos = 0;
1831         float x;
1832         float fill_separator;
1833         float fill_hfill;
1834         float fill_label_hfill;
1835         // This call HAS to be here because of the BidiTables!!!
1836         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1837                        fill_label_hfill);
1838
1839         if (last < row->pos())
1840                 cursor_vpos = row->pos();
1841         else if (pos > last && !boundary)
1842                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1843                         ? row->pos() : last + 1;
1844         else if (pos > row->pos() &&
1845                  (pos > last || boundary))
1846                 /// Place cursor after char at (logical) position pos - 1
1847                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1848                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1849         else
1850                 /// Place cursor before char at (logical) position pos
1851                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1852                         ? log2vis(pos) : log2vis(pos) + 1;
1853
1854         pos_type main_body =
1855                 beginningOfMainBody(bview->buffer(), row->par());
1856         if ((main_body > 0) &&
1857             ((main_body-1 > last) ||
1858              !row->par()->isLineSeparator(main_body-1)))
1859                 main_body = 0;
1860
1861         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1862                 pos_type pos = vis2log(vpos);
1863                 if (main_body > 0 && pos == main_body - 1) {
1864                         x += fill_label_hfill +
1865                                 font_metrics::width(
1866                                         row->par()->layout()->labelsep,
1867                                         getLabelFont(bview->buffer(),
1868                                                      row->par()));
1869                         if (row->par()->isLineSeparator(main_body - 1))
1870                                 x -= singleWidth(bview,
1871                                                  row->par(), main_body - 1);
1872                 }
1873                 if (hfillExpansion(bview->buffer(), row, pos)) {
1874                         x += singleWidth(bview, row->par(), pos);
1875                         if (pos >= main_body)
1876                                 x += fill_hfill;
1877                         else
1878                                 x += fill_label_hfill;
1879                 } else if (row->par()->isSeparator(pos)) {
1880                         x += singleWidth(bview, row->par(), pos);
1881                         if (pos >= main_body)
1882                                 x += fill_separator;
1883                 } else
1884                         x += singleWidth(bview, row->par(), pos);
1885         }
1886         return x;
1887 }
1888
1889
1890 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1891                               pos_type pos, bool setfont, bool boundary) const
1892 {
1893         InsetText * it = static_cast<InsetText *>(par->inInset());
1894         if (it) {
1895                 if (it != inset_owner) {
1896                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1897                                               << endl
1898                                               << "inset_owner is "
1899                                               << inset_owner << endl;
1900 #ifdef WITH_WARNINGS
1901 #warning I believe this code is wrong. (Lgb)
1902 #warning Jürgen, have a look at this. (Lgb)
1903 #warning Hmmm, I guess you are right but we
1904 #warning should verify when this is needed
1905 #endif
1906                         // Jürgen, would you like to have a look?
1907                         // I guess we need to move the outer cursor
1908                         // and open and lock the inset (bla bla bla)
1909                         // stuff I don't know... so can you have a look?
1910                         // (Lgb)
1911                         // I moved the lyxerr stuff in here so we can see if
1912                         // this is actually really needed and where!
1913                         // (Jug)
1914                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1915                         return;
1916                 }
1917         }
1918
1919         setCursor(bview, cursor, par, pos, boundary);
1920         if (setfont)
1921                 setCurrentFont(bview);
1922 }
1923
1924
1925 void LyXText::setCurrentFont(BufferView * bview) const
1926 {
1927         pos_type pos = cursor.pos();
1928         if (cursor.boundary() && pos > 0)
1929                 --pos;
1930
1931         if (pos > 0) {
1932                 if (pos == cursor.par()->size())
1933                         --pos;
1934                 else // potentional bug... BUG (Lgb)
1935                         if (cursor.par()->isSeparator(pos)) {
1936                                 if (pos > cursor.row()->pos() &&
1937                                     bidi_level(pos) % 2 ==
1938                                     bidi_level(pos - 1) % 2)
1939                                         --pos;
1940                                 else if (pos + 1 < cursor.par()->size())
1941                                         ++pos;
1942                         }
1943         }
1944
1945         current_font =
1946                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1947         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1948
1949         if (cursor.pos() == cursor.par()->size() &&
1950             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1951             !cursor.boundary()) {
1952                 Language const * lang =
1953                         cursor.par()->getParLanguage(bview->buffer()->params);
1954                 current_font.setLanguage(lang);
1955                 current_font.setNumber(LyXFont::OFF);
1956                 real_current_font.setLanguage(lang);
1957                 real_current_font.setNumber(LyXFont::OFF);
1958         }
1959 }
1960
1961
1962 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1963 {
1964         LyXCursor old_cursor = cursor;
1965
1966         setCursorFromCoordinates(bview, cursor, x, y);
1967         setCurrentFont(bview);
1968         deleteEmptyParagraphMechanism(bview, old_cursor);
1969 }
1970
1971
1972 namespace {
1973
1974         /**
1975          * return true if the cursor given is at the end of a row,
1976          * and the next row is filled by an inset that spans an entire
1977          * row.
1978          */
1979         bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1980                 if (!row.next())
1981                         return false;
1982                 Row const & next = *row.next();
1983
1984                 if (next.pos() != cur.pos() || next.par() != cur.par())
1985                         return false;
1986                 if (!cur.par()->isInset(cur.pos()))
1987                         return false;
1988                 Inset const * inset = cur.par()->getInset(cur.pos());
1989                 if (inset->needFullRow() || inset->display())
1990                         return true;
1991                 return false;
1992         }
1993 }
1994
1995
1996 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1997                                        int x, int y) const
1998 {
1999         // Get the row first.
2000
2001         Row * row = getRowNearY(y);
2002         bool bound = false;
2003         pos_type const column = getColumnNearX(bview, row, x, bound);
2004         cur.par(row->par());
2005         cur.pos(row->pos() + column);
2006         cur.x(x);
2007         cur.y(y + row->baseline());
2008         cur.row(row);
2009
2010         if (beforeFullRowInset(*row, cur)) {
2011                 pos_type last = rowLastPrintable(row);
2012                 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2013                 cur.ix(int(x));
2014                 cur.iy(y + row->height() + row->next()->baseline());
2015                 cur.irow(row->next());
2016         } else {
2017                 cur.iy(cur.y());
2018                 cur.ix(cur.x());
2019                 cur.irow(row);
2020         }
2021         cur.boundary(bound);
2022 }
2023
2024
2025 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2026 {
2027         if (cursor.pos() > 0) {
2028                 bool boundary = cursor.boundary();
2029                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2030                 if (!internal && !boundary &&
2031                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2032                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2033         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2034                 Paragraph * par = cursor.par()->previous();
2035                 setCursor(bview, par, par->size());
2036         }
2037 }
2038
2039
2040 void LyXText::cursorRight(BufferView * bview, bool internal) const
2041 {
2042         if (!internal && cursor.boundary() &&
2043             !cursor.par()->isNewline(cursor.pos()))
2044                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2045         else if (cursor.pos() < cursor.par()->size()) {
2046                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2047                 if (!internal &&
2048                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2049                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2050         } else if (cursor.par()->next())
2051                 setCursor(bview, cursor.par()->next(), 0);
2052 }
2053
2054
2055 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2056 {
2057 #if 1
2058         int x = cursor.x_fix();
2059         int y = cursor.y() - cursor.row()->baseline() - 1;
2060         setCursorFromCoordinates(bview, x, y);
2061         if (!selecting) {
2062                 int y1 = cursor.iy() - first_y;
2063                 int y2 = y1;
2064                 y -= first_y;
2065                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2066                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2067                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2068                 }
2069         }
2070 #else
2071         setCursorFromCoordinates(bview, cursor.x_fix(),
2072                                  cursor.y() - cursor.row()->baseline() - 1);
2073 #endif
2074 }
2075
2076
2077 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2078 {
2079 #if 1
2080         int x = cursor.x_fix();
2081         int y = cursor.y() - cursor.row()->baseline() +
2082                 cursor.row()->height() + 1;
2083         setCursorFromCoordinates(bview, x, y);
2084         if (!selecting && cursor.row() == cursor.irow()) {
2085                 int y1 = cursor.iy() - first_y;
2086                 int y2 = y1;
2087                 y -= first_y;
2088                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2089                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2090                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2091                 }
2092         }
2093 #else
2094         setCursorFromCoordinates(bview, cursor.x_fix(),
2095                                  cursor.y() - cursor.row()->baseline()
2096                                  + cursor.row()->height() + 1);
2097 #endif
2098 }
2099
2100
2101 void LyXText::cursorUpParagraph(BufferView * bview) const
2102 {
2103         if (cursor.pos() > 0) {
2104                 setCursor(bview, cursor.par(), 0);
2105         }
2106         else if (cursor.par()->previous()) {
2107                 setCursor(bview, cursor.par()->previous(), 0);
2108         }
2109 }
2110
2111
2112 void LyXText::cursorDownParagraph(BufferView * bview) const
2113 {
2114         if (cursor.par()->next()) {
2115                 setCursor(bview, cursor.par()->next(), 0);
2116         } else {
2117                 setCursor(bview, cursor.par(), cursor.par()->size());
2118         }
2119 }
2120
2121 // fix the cursor `cur' after a characters has been deleted at `where'
2122 // position. Called by deleteEmptyParagraphMechanism
2123 void LyXText::fixCursorAfterDelete(BufferView * bview,
2124                                    LyXCursor & cur,
2125                                    LyXCursor const & where) const
2126 {
2127         // if cursor is not in the paragraph where the delete occured,
2128         // do nothing
2129         if (cur.par() != where.par())
2130                 return;
2131
2132         // if cursor position is after the place where the delete occured,
2133         // update it
2134         if (cur.pos() > where.pos())
2135                 cur.pos(cur.pos()-1);
2136
2137         // check also if we don't want to set the cursor on a spot behind the
2138         // pagragraph because we erased the last character.
2139         if (cur.pos() > cur.par()->size())
2140                 cur.pos(cur.par()->size());
2141
2142         // recompute row et al. for this cursor
2143         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2144 }
2145
2146
2147 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2148                                             LyXCursor const & old_cursor) const
2149 {
2150         // Would be wrong to delete anything if we have a selection.
2151         if (selection.set())
2152                 return false;
2153
2154         // We allow all kinds of "mumbo-jumbo" when freespacing.
2155         if (old_cursor.par()->layout()->free_spacing
2156             || old_cursor.par()->isFreeSpacing()) {
2157                 return false;
2158         }
2159
2160         /* Ok I'll put some comments here about what is missing.
2161            I have fixed BackSpace (and thus Delete) to not delete
2162            double-spaces automagically. I have also changed Cut,
2163            Copy and Paste to hopefully do some sensible things.
2164            There are still some small problems that can lead to
2165            double spaces stored in the document file or space at
2166            the beginning of paragraphs. This happens if you have
2167            the cursor betwenn to spaces and then save. Or if you
2168            cut and paste and the selection have a space at the
2169            beginning and then save right after the paste. I am
2170            sure none of these are very hard to fix, but I will
2171            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2172            that I can get some feedback. (Lgb)
2173         */
2174
2175         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2176         // delete the LineSeparator.
2177         // MISSING
2178
2179         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2180         // delete the LineSeparator.
2181         // MISSING
2182
2183         // If the pos around the old_cursor were spaces, delete one of them.
2184         if (old_cursor.par() != cursor.par()
2185             || old_cursor.pos() != cursor.pos()) {
2186                 // Only if the cursor has really moved
2187
2188                 if (old_cursor.pos() > 0
2189                     && old_cursor.pos() < old_cursor.par()->size()
2190                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2191                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2192                         old_cursor.par()->erase(old_cursor.pos() - 1);
2193                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2194
2195 #ifdef WITH_WARNINGS
2196 #warning This will not work anymore when we have multiple views of the same buffer
2197 // In this case, we will have to correct also the cursors held by
2198 // other bufferviews. It will probably be easier to do that in a more
2199 // automated way in LyXCursor code. (JMarc 26/09/2001)
2200 #endif
2201                         // correct all cursors held by the LyXText
2202                         fixCursorAfterDelete(bview, cursor, old_cursor);
2203                         fixCursorAfterDelete(bview, selection.cursor,
2204                                              old_cursor);
2205                         fixCursorAfterDelete(bview, selection.start,
2206                                              old_cursor);
2207                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2208                         fixCursorAfterDelete(bview, last_sel_cursor,
2209                                              old_cursor);
2210                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2211                         fixCursorAfterDelete(bview, toggle_end_cursor,
2212                                              old_cursor);
2213                         return false;
2214                 }
2215         }
2216
2217         // don't delete anything if this is the ONLY paragraph!
2218         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2219                 return false;
2220
2221         // Do not delete empty paragraphs with keepempty set.
2222         if (old_cursor.par()->layout()->keepempty)
2223                 return false;
2224
2225         // only do our magic if we changed paragraph
2226         if (old_cursor.par() == cursor.par())
2227                 return false;
2228
2229         // record if we have deleted a paragraph
2230         // we can't possibly have deleted a paragraph before this point
2231         bool deleted = false;
2232
2233         if ((old_cursor.par()->empty()
2234              || (old_cursor.par()->size() == 1
2235                  && old_cursor.par()->isLineSeparator(0)))) {
2236                 // ok, we will delete anything
2237                 LyXCursor tmpcursor;
2238
2239                 // make sure that you do not delete any environments
2240                 status(bview, LyXText::NEED_MORE_REFRESH);
2241                 deleted = true;
2242
2243                 if (old_cursor.row()->previous()) {
2244                         refresh_row = old_cursor.row()->previous();
2245                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2246                         tmpcursor = cursor;
2247                         cursor = old_cursor; // that undo can restore the right cursor position
2248                         Paragraph * endpar = old_cursor.par()->next();
2249                         if (endpar && endpar->getDepth()) {
2250                                 while (endpar && endpar->getDepth()) {
2251                                         endpar = endpar->next();
2252                                 }
2253                         }
2254                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2255                         cursor = tmpcursor;
2256
2257                         // delete old row
2258                         removeRow(old_cursor.row());
2259                         if (ownerParagraph() == old_cursor.par()) {
2260                                 ownerParagraph(ownerParagraph()->next());
2261                         }
2262                         // delete old par
2263                         delete old_cursor.par();
2264
2265                         /* Breakagain the next par. Needed because of
2266                          * the parindent that can occur or dissappear.
2267                          * The next row can change its height, if
2268                          * there is another layout before */
2269                         if (refresh_row->next()) {
2270                                 breakAgain(bview, refresh_row->next());
2271                                 updateCounters(bview);
2272                         }
2273                         setHeightOfRow(bview, refresh_row);
2274                 } else {
2275                         refresh_row = old_cursor.row()->next();
2276                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2277
2278                         tmpcursor = cursor;
2279                         cursor = old_cursor; // that undo can restore the right cursor position
2280                         Paragraph * endpar = old_cursor.par()->next();
2281                         if (endpar && endpar->getDepth()) {
2282                                 while (endpar && endpar->getDepth()) {
2283                                         endpar = endpar->next();
2284                                 }
2285                         }
2286                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2287                         cursor = tmpcursor;
2288
2289                         // delete old row
2290                         removeRow(old_cursor.row());
2291                         // delete old par
2292                         if (ownerParagraph() == old_cursor.par()) {
2293                                 ownerParagraph(ownerParagraph()->next());
2294                         }
2295
2296                         delete old_cursor.par();
2297
2298                         /* Breakagain the next par. Needed because of
2299                            the parindent that can occur or dissappear.
2300                            The next row can change its height, if
2301                            there is another layout before */
2302                         if (refresh_row) {
2303                                 breakAgain(bview, refresh_row);
2304                                 updateCounters(bview);
2305                         }
2306                 }
2307
2308                 // correct cursor y
2309                 setCursorIntern(bview, cursor.par(), cursor.pos());
2310
2311                 if (selection.cursor.par()  == old_cursor.par()
2312                     && selection.cursor.pos() == old_cursor.pos()) {
2313                         // correct selection
2314                         selection.cursor = cursor;
2315                 }
2316         }
2317         if (!deleted) {
2318                 if (old_cursor.par()->stripLeadingSpaces()) {
2319                         redoParagraphs(bview, old_cursor,
2320                                        old_cursor.par()->next());
2321                         // correct cursor y
2322                         setCursorIntern(bview, cursor.par(), cursor.pos());
2323                         selection.cursor = cursor;
2324                 }
2325         }
2326         return deleted;
2327 }
2328
2329
2330 Paragraph * LyXText::ownerParagraph() const
2331 {
2332         if (inset_owner) {
2333                 return inset_owner->paragraph();
2334         }
2335         return &*(bv_owner->buffer()->paragraphs.begin());
2336 }
2337
2338
2339 void LyXText::ownerParagraph(Paragraph * p) const
2340 {
2341         if (inset_owner) {
2342                 inset_owner->paragraph(p);
2343         } else {
2344                 bv_owner->buffer()->paragraphs.set(p);
2345         }
2346 }
2347
2348
2349 void LyXText::ownerParagraph(int id, Paragraph * p) const
2350 {
2351         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2352         if (op && op->inInset()) {
2353                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2354         } else {
2355                 ownerParagraph(p);
2356         }
2357 }
2358
2359
2360 LyXText::text_status LyXText::status() const
2361 {
2362         return status_;
2363 }
2364
2365
2366 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2367 {
2368         LyXText * t = bview->text;
2369
2370         // We should only go up with refreshing code so this means that if
2371         // we have a MORE refresh we should never set it to LITTLE if we still
2372         // didn't handle it (and then it will be UNCHANGED. Now as long as
2373         // we stay inside one LyXText this may work but we need to tell the
2374         // outermost LyXText that it should REALLY draw us if there is some
2375         // change in a Inset::LyXText. So you see that when we are inside a
2376         // inset's LyXText we give the LITTLE to the outermost LyXText to
2377         // tell'em that it should redraw the actual row (where the inset
2378         // resides! Capito?!
2379
2380         if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2381                 status_ = st;
2382                 if (inset_owner && st != UNCHANGED) {
2383                         t->status(bview, NEED_VERY_LITTLE_REFRESH);
2384                         if (!t->refresh_row) {
2385                                 t->refresh_row = t->cursor.row();
2386                                 t->refresh_y = t->cursor.y() -
2387                                         t->cursor.row()->baseline();
2388                         }
2389                 }
2390         }
2391 }