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