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