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