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