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