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