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