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