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