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