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