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