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