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