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