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