]> git.lyx.org Git - lyx.git/blob - src/text2.C
parlist-21-a.diff
[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(static_cast<UpdatableInset *>(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 paragaphs between the cursor
662 // and the specified par
663 // This function is needed after SetLayout and SetFont etc.
664 void LyXText::redoParagraphs(LyXCursor const & cur,
665                              ParagraphList::iterator endpit)
666 {
667         RowList::iterator tmprit = cur.row();
668         int y = cur.y() - tmprit->baseline();
669
670         ParagraphList::iterator first_phys_pit;
671         RowList::iterator prevrit;
672         if (tmprit == rows().begin()) {
673                 // A trick/hack for UNDO.
674                 // This is needed because in an UNDO/REDO we could have
675                 // changed the ownerParagrah() so the paragraph inside
676                 // the row is NOT my really first par anymore.
677                 // Got it Lars ;) (Jug 20011206)
678                 first_phys_pit = ownerParagraphs().begin();
679                 prevrit = rows().end();
680         } else {
681                 first_phys_pit = tmprit->par();
682                 while (tmprit != rows().begin()
683                        && boost::prior(tmprit)->par() == first_phys_pit)
684                 {
685                         --tmprit;
686                         y -= tmprit->height();
687                 }
688                 prevrit = boost::prior(tmprit);
689         }
690
691         // remove it
692         while (tmprit != rows().end() && tmprit->par() != endpit) {
693                 RowList::iterator tmprit2 = tmprit++;
694                 removeRow(tmprit2);
695         }
696
697         // Reinsert the paragraphs.
698         ParagraphList::iterator tmppit = first_phys_pit;
699
700         while (tmppit != ownerParagraphs().end()) {
701                 insertParagraph(tmppit, tmprit);
702                 while (tmprit != rows().end()
703                        && tmprit->par() == tmppit) {
704                         ++tmprit;
705                 }
706                 ++tmppit;
707                 if (tmppit == endpit)
708                         break;
709         }
710         if (prevrit != rows().end()) {
711                 setHeightOfRow(prevrit);
712                 const_cast<LyXText *>(this)->postPaint(y - prevrit->height());
713         } else {
714                 setHeightOfRow(rows().begin());
715                 const_cast<LyXText *>(this)->postPaint(0);
716         }
717         if (tmprit != rows().end())
718                 setHeightOfRow(tmprit);
719
720         updateCounters();
721 }
722
723
724 void LyXText::fullRebreak()
725 {
726         if (rows().empty()) {
727                 init(bv());
728                 return;
729         }
730
731         RowList::iterator rows_end = rows().end();
732
733         if (need_break_row != rows_end) {
734                 breakAgain(need_break_row);
735                 need_break_row = rows_end;
736                 return;
737         }
738 }
739
740
741 // important for the screen
742
743
744 // the cursor set functions have a special mechanism. When they
745 // realize, that you left an empty paragraph, they will delete it.
746 // They also delete the corresponding row
747
748 // need the selection cursor:
749 void LyXText::setSelection()
750 {
751         bool const lsel = selection.set();
752
753         if (!selection.set()) {
754                 last_sel_cursor = selection.cursor;
755                 selection.start = selection.cursor;
756                 selection.end = selection.cursor;
757         }
758
759         selection.set(true);
760
761         // first the toggling area
762         if (cursor.y() < last_sel_cursor.y()
763             || (cursor.y() == last_sel_cursor.y()
764                 && cursor.x() < last_sel_cursor.x())) {
765                 toggle_end_cursor = last_sel_cursor;
766                 toggle_cursor = cursor;
767         } else {
768                 toggle_end_cursor = cursor;
769                 toggle_cursor = last_sel_cursor;
770         }
771
772         last_sel_cursor = cursor;
773
774         // and now the whole selection
775
776         if (selection.cursor.par() == cursor.par())
777                 if (selection.cursor.pos() < cursor.pos()) {
778                         selection.end = cursor;
779                         selection.start = selection.cursor;
780                 } else {
781                         selection.end = selection.cursor;
782                         selection.start = cursor;
783                 }
784         else if (selection.cursor.y() < cursor.y() ||
785                  (selection.cursor.y() == cursor.y()
786                   && selection.cursor.x() < cursor.x())) {
787                 selection.end = cursor;
788                 selection.start = selection.cursor;
789         }
790         else {
791                 selection.end = selection.cursor;
792                 selection.start = cursor;
793         }
794
795         // a selection with no contents is not a selection
796         if (selection.start.par() == selection.end.par() &&
797             selection.start.pos() == selection.end.pos())
798                 selection.set(false);
799
800         if (inset_owner && (selection.set() || lsel))
801                 inset_owner->setUpdateStatus(bv(), InsetText::SELECTION);
802 }
803
804
805 string const LyXText::selectionAsString(Buffer const * buffer,
806                                         bool label) const
807 {
808         if (!selection.set()) return string();
809
810         // should be const ...
811         ParagraphList::iterator startpit = selection.start.par();
812         ParagraphList::iterator endpit = selection.end.par();
813         pos_type const startpos(selection.start.pos());
814         pos_type const endpos(selection.end.pos());
815
816         if (startpit == endpit) {
817                 return startpit->asString(buffer, startpos, endpos, label);
818         }
819
820         string result;
821
822         // First paragraph in selection
823         result += startpit->asString(buffer, startpos, startpit->size(), label) + "\n\n";
824
825         // The paragraphs in between (if any)
826         ParagraphList::iterator pit = boost::next(startpit);
827         for (; pit != endpit; ++pit) {
828                 result += pit->asString(buffer, 0, pit->size(), label) + "\n\n";
829         }
830
831         // Last paragraph in selection
832         result += endpit->asString(buffer, 0, endpos, label);
833
834         return result;
835 }
836
837
838 void LyXText::clearSelection()
839 {
840         selection.set(false);
841         selection.mark(false);
842         last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
843         // reset this in the bv_owner!
844         if (bv_owner && bv_owner->text)
845                 bv_owner->text->xsel_cache.set(false);
846 }
847
848
849 void LyXText::cursorHome()
850 {
851         setCursor(cursor.par(), cursor.row()->pos());
852 }
853
854
855 void LyXText::cursorEnd()
856 {
857         if (cursor.par()->empty())
858                 return;
859
860         RowList::iterator rit = cursor.row();
861         RowList::iterator next_rit = boost::next(rit);
862         ParagraphList::iterator pit = rit->par();
863         pos_type last_pos = lastPos(*this, rit);
864
865         if (next_rit == rows().end() || next_rit->par() != pit) {
866                 ++last_pos;
867         } else {
868                 if (pit->empty() ||
869                     (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
870                         ++last_pos;
871                 }
872         }
873
874         setCursor(pit, last_pos);
875 }
876
877
878 void LyXText::cursorTop()
879 {
880         setCursor(ownerParagraphs().begin(), 0);
881 }
882
883
884 void LyXText::cursorBottom()
885 {
886 #warning FIXME
887         // This is how it should be:
888 #ifndef NO_STD_LIST
889         ParagraphList::iterator lastpit = boost::prior(ownerParagraphs().end());
890 #else
891         ParagraphList::iterator lastpit = ownerParagraphs().begin();
892         ParagraphList::iterator end = ownerParagraphs().end();
893         while (boost::next(lastpit) != end)
894                 ++lastpit;
895 #endif
896         int pos = lastpit->size();
897         setCursor(lastpit, pos);
898 }
899
900
901 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
902 {
903         // If the mask is completely neutral, tell user
904         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
905                 // Could only happen with user style
906                 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
907                 return;
908         }
909
910         // Try implicit word selection
911         // If there is a change in the language the implicit word selection
912         // is disabled.
913         LyXCursor resetCursor = cursor;
914         bool implicitSelection = (font.language() == ignore_language
915                                   && font.number() == LyXFont::IGNORE)
916                 ? selectWordWhenUnderCursor(WHOLE_WORD_STRICT) : false;
917
918         // Set font
919         setFont(font, toggleall);
920
921         // Implicit selections are cleared afterwards
922         //and cursor is set to the original position.
923         if (implicitSelection) {
924                 clearSelection();
925                 cursor = resetCursor;
926                 setCursor(cursor.par(), cursor.pos());
927                 selection.cursor = cursor;
928         }
929         if (inset_owner)
930                 inset_owner->setUpdateStatus(bv(), InsetText::CURSOR_PAR);
931 }
932
933
934 string LyXText::getStringToIndex()
935 {
936         // Try implicit word selection
937         // If there is a change in the language the implicit word selection
938         // is disabled.
939         LyXCursor const reset_cursor = cursor;
940         bool const implicitSelection = selectWordWhenUnderCursor(PREVIOUS_WORD);
941
942         string idxstring;
943         if (!selection.set())
944                 bv()->owner()->message(_("Nothing to index!"));
945         else if (selection.start.par() != selection.end.par())
946                 bv()->owner()->message(_("Cannot index more than one paragraph!"));
947         else
948                 idxstring = selectionAsString(bv()->buffer(), false);
949
950         // Reset cursors to their original position.
951         cursor = reset_cursor;
952         setCursor(cursor.par(), cursor.pos());
953         selection.cursor = cursor;
954
955         // Clear the implicit selection.
956         if (implicitSelection)
957                 clearSelection();
958
959         return idxstring;
960 }
961
962
963 // the DTP switches for paragraphs. LyX will store them in the first
964 // physicla paragraph. When a paragraph is broken, the top settings rest,
965 // the bottom settings are given to the new one. So I can make shure,
966 // they do not duplicate themself and you cannnot make dirty things with
967 // them!
968
969 void LyXText::setParagraph(bool line_top, bool line_bottom,
970                            bool pagebreak_top, bool pagebreak_bottom,
971                            VSpace const & space_top,
972                            VSpace const & space_bottom,
973                            Spacing const & spacing,
974                            LyXAlignment align,
975                            string const & labelwidthstring,
976                            bool noindent)
977 {
978         LyXCursor tmpcursor = cursor;
979         if (!selection.set()) {
980                 selection.start = cursor;
981                 selection.end = cursor;
982         }
983
984         // make sure that the depth behind the selection are restored, too
985         ParagraphList::iterator endpit = boost::next(selection.end.par());
986         ParagraphList::iterator undoendpit = endpit;
987         ParagraphList::iterator pars_end = ownerParagraphs().end();
988
989         if (endpit != pars_end && endpit->getDepth()) {
990                 while (endpit != pars_end && endpit->getDepth()) {
991                         ++endpit;
992                         undoendpit = endpit;
993                 }
994         } else if (endpit != pars_end) {
995                 // because of parindents etc.
996                 ++endpit;
997         }
998
999         setUndo(bv(), Undo::EDIT, selection.start.par(),
1000                 boost::prior(undoendpit));
1001
1002
1003         ParagraphList::iterator tmppit = selection.end.par();
1004
1005         while (tmppit != boost::prior(selection.start.par())) {
1006                 setCursor(tmppit, 0);
1007                 postPaint(cursor.y() - cursor.row()->baseline());
1008
1009                 ParagraphList::iterator pit = cursor.par();
1010                 ParagraphParameters & params = pit->params();
1011
1012                 params.lineTop(line_top);
1013                 params.lineBottom(line_bottom);
1014                 params.pagebreakTop(pagebreak_top);
1015                 params.pagebreakBottom(pagebreak_bottom);
1016                 params.spaceTop(space_top);
1017                 params.spaceBottom(space_bottom);
1018                 params.spacing(spacing);
1019                 // does the layout allow the new alignment?
1020                 LyXLayout_ptr const & layout = pit->layout();
1021
1022                 if (align == LYX_ALIGN_LAYOUT)
1023                         align = layout->align;
1024                 if (align & layout->alignpossible) {
1025                         if (align == layout->align)
1026                                 params.align(LYX_ALIGN_LAYOUT);
1027                         else
1028                                 params.align(align);
1029                 }
1030                 pit->setLabelWidthString(labelwidthstring);
1031                 params.noindent(noindent);
1032                 tmppit = boost::prior(pit);
1033         }
1034
1035         redoParagraphs(selection.start, endpit);
1036
1037         clearSelection();
1038         setCursor(selection.start.par(), selection.start.pos());
1039         selection.cursor = cursor;
1040         setCursor(selection.end.par(), selection.end.pos());
1041         setSelection();
1042         setCursor(tmpcursor.par(), tmpcursor.pos());
1043         if (inset_owner)
1044                 bv()->updateInset(inset_owner);
1045 }
1046
1047
1048 // set the counter of a paragraph. This includes the labels
1049 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
1050 {
1051         LyXTextClass const & textclass = buf->params.getLyXTextClass();
1052         LyXLayout_ptr const & layout = pit->layout();
1053
1054         if (pit != ownerParagraphs().begin()) {
1055
1056                 pit->params().appendix(boost::prior(pit)->params().appendix());
1057                 if (!pit->params().appendix() &&
1058                     pit->params().startOfAppendix()) {
1059                         pit->params().appendix(true);
1060                         textclass.counters().reset();
1061                 }
1062                 pit->enumdepth = boost::prior(pit)->enumdepth;
1063                 pit->itemdepth = boost::prior(pit)->itemdepth;
1064         } else {
1065                 pit->params().appendix(pit->params().startOfAppendix());
1066                 pit->enumdepth = 0;
1067                 pit->itemdepth = 0;
1068         }
1069
1070         /* Maybe we have to increment the enumeration depth.
1071          * BUT, enumeration in a footnote is considered in isolation from its
1072          *      surrounding paragraph so don't increment if this is the
1073          *      first line of the footnote
1074          * AND, bibliographies can't have their depth changed ie. they
1075          *      are always of depth 0
1076          */
1077         if (pit != ownerParagraphs().begin()
1078             && boost::prior(pit)->getDepth() < pit->getDepth()
1079             && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
1080             && pit->enumdepth < 3
1081             && layout->labeltype != LABEL_BIBLIO) {
1082                 pit->enumdepth++;
1083         }
1084
1085         // Maybe we have to decrement the enumeration depth, see note above
1086         if (pit != ownerParagraphs().begin()
1087             && boost::prior(pit)->getDepth() > pit->getDepth()
1088             && layout->labeltype != LABEL_BIBLIO) {
1089                 pit->enumdepth = depthHook(pit, ownerParagraphs(),
1090                                            pit->getDepth())->enumdepth;
1091         }
1092
1093         if (!pit->params().labelString().empty()) {
1094                 pit->params().labelString(string());
1095         }
1096
1097         if (layout->margintype == MARGIN_MANUAL) {
1098                 if (pit->params().labelWidthString().empty()) {
1099                         pit->setLabelWidthString(layout->labelstring());
1100                 }
1101         } else {
1102                 pit->setLabelWidthString(string());
1103         }
1104
1105         // is it a layout that has an automatic label?
1106         if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1107                 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1108
1109                 ostringstream s;
1110
1111                 if (i >= 0 && i <= buf->params.secnumdepth) {
1112                         string numbertype;
1113                         string langtype;
1114
1115                         textclass.counters().step(layout->latexname());
1116
1117                         // Is there a label? Useful for Chapter layout
1118                         if (!pit->params().appendix()) {
1119                                 s << buf->B_(layout->labelstring());
1120                         } else {
1121                                 s << buf->B_(layout->labelstring_appendix());
1122                         }
1123
1124                         // Use of an integer is here less than elegant. For now.
1125                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1126                         if (!pit->params().appendix()) {
1127                                 numbertype = "sectioning";
1128                         } else {
1129                                 numbertype = "appendix";
1130                                 if (pit->isRightToLeftPar(buf->params))
1131                                         langtype = "hebrew";
1132                                 else
1133                                         langtype = "latin";
1134                         }
1135
1136                         s << " "
1137                           << textclass.counters()
1138                                 .numberLabel(layout->latexname(),
1139                                              numbertype, langtype, head);
1140
1141                         pit->params().labelString(STRCONV(s.str()));
1142
1143                         // reset enum counters
1144                         textclass.counters().reset("enum");
1145                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1146                         textclass.counters().reset("enum");
1147                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1148                         // FIXME
1149                         // Yes I know this is a really, really! bad solution
1150                         // (Lgb)
1151                         string enumcounter("enum");
1152
1153                         switch (pit->enumdepth) {
1154                         case 2:
1155                                 enumcounter += 'i';
1156                         case 1:
1157                                 enumcounter += 'i';
1158                         case 0:
1159                                 enumcounter += 'i';
1160                                 break;
1161                         case 3:
1162                                 enumcounter += "iv";
1163                                 break;
1164                         default:
1165                                 // not a valid enumdepth...
1166                                 break;
1167                         }
1168
1169                         textclass.counters().step(enumcounter);
1170
1171                         s << textclass.counters()
1172                                 .numberLabel(enumcounter, "enumeration");
1173                         pit->params().labelString(STRCONV(s.str()));
1174                 }
1175         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1176                 textclass.counters().step("bibitem");
1177                 int number = textclass.counters().value("bibitem");
1178                 if (pit->bibitem()) {
1179                         pit->bibitem()->setCounter(number);
1180                         pit->params().labelString(layout->labelstring());
1181                 }
1182                 // In biblio should't be following counters but...
1183         } else {
1184                 string s = buf->B_(layout->labelstring());
1185
1186                 // the caption hack:
1187                 if (layout->labeltype == LABEL_SENSITIVE) {
1188                         ParagraphList::iterator tmppit = pit;
1189                         Inset * in = 0;
1190                         bool isOK = false;
1191                         while (tmppit != ownerParagraphs().end() &&
1192                                tmppit->inInset()
1193                                // the single '=' is intended below
1194                                && (in = tmppit->inInset()->owner())) {
1195                                 if (in->lyxCode() == Inset::FLOAT_CODE ||
1196                                     in->lyxCode() == Inset::WRAP_CODE) {
1197                                         isOK = true;
1198                                         break;
1199                                 } else {
1200                                         tmppit = ownerParagraphs().find(*in->parOwner());
1201                                 }
1202                         }
1203
1204                         if (isOK) {
1205                                 string type;
1206
1207                                 if (in->lyxCode() == Inset::FLOAT_CODE)
1208                                         type = static_cast<InsetFloat*>(in)->params().type;
1209                                 else if (in->lyxCode() == Inset::WRAP_CODE)
1210                                         type = static_cast<InsetWrap*>(in)->params().type;
1211                                 else
1212                                         lyx::Assert(0);
1213
1214                                 Floating const & fl = textclass.floats().getType(type);
1215
1216                                 textclass.counters().step(fl.type());
1217
1218                                 // Doesn't work... yet.
1219                                 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1220                         } else {
1221                                 // par->SetLayout(0);
1222                                 // s = layout->labelstring;
1223                                 s = _("Senseless: ");
1224                         }
1225                 }
1226                 pit->params().labelString(s);
1227
1228                 // reset the enumeration counter. They are always reset
1229                 // when there is any other layout between
1230                 // Just fall-through between the cases so that all
1231                 // enum counters deeper than enumdepth is also reset.
1232                 switch (pit->enumdepth) {
1233                 case 0:
1234                         textclass.counters().reset("enumi");
1235                 case 1:
1236                         textclass.counters().reset("enumii");
1237                 case 2:
1238                         textclass.counters().reset("enumiii");
1239                 case 3:
1240                         textclass.counters().reset("enumiv");
1241                 }
1242         }
1243 }
1244
1245
1246 // Updates all counters. Paragraphs with changed label string will be rebroken
1247 void LyXText::updateCounters()
1248 {
1249         RowList::iterator rowit = rows().begin();
1250         ParagraphList::iterator pit = rowit->par();
1251
1252         // CHECK if this is really needed. (Lgb)
1253         bv()->buffer()->params.getLyXTextClass().counters().reset();
1254
1255         for (; pit != ownerParagraphs().end(); ++pit) {
1256                 while (rowit->par() != pit)
1257                         ++rowit;
1258
1259                 string const oldLabel = pit->params().labelString();
1260
1261                 size_t maxdepth = 0;
1262                 if (pit != ownerParagraphs().begin())
1263                         maxdepth = boost::prior(pit)->getMaxDepthAfter();
1264
1265                 if (pit->params().depth() > maxdepth)
1266                         pit->params().depth(maxdepth);
1267
1268                 // setCounter can potentially change the labelString.
1269                 setCounter(bv()->buffer(), pit);
1270
1271                 string const & newLabel = pit->params().labelString();
1272
1273                 if (oldLabel.empty() && !newLabel.empty()) {
1274                         removeParagraph(rowit);
1275                         appendParagraph(rowit);
1276                 }
1277         }
1278 }
1279
1280
1281 void LyXText::insertInset(Inset * inset)
1282 {
1283         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1284                 return;
1285         setUndo(bv(), Undo::FINISH, cursor.par());
1286         freezeUndo();
1287         cursor.par()->insertInset(cursor.pos(), inset);
1288         // Just to rebreak and refresh correctly.
1289         // The character will not be inserted a second time
1290         insertChar(Paragraph::META_INSET);
1291         // If we enter a highly editable inset the cursor should be to before
1292         // the inset. This couldn't happen before as Undo was not handled inside
1293         // inset now after the Undo LyX tries to call inset->Edit(...) again
1294         // and cannot do this as the cursor is behind the inset and GetInset
1295         // does not return the inset!
1296         if (isHighlyEditableInset(inset)) {
1297                 cursorLeft(true);
1298         }
1299         unFreezeUndo();
1300 }
1301
1302
1303 void LyXText::cutSelection(bool doclear, bool realcut)
1304 {
1305         // Stuff what we got on the clipboard. Even if there is no selection.
1306
1307         // There is a problem with having the stuffing here in that the
1308         // larger the selection the slower LyX will get. This can be
1309         // solved by running the line below only when the selection has
1310         // finished. The solution used currently just works, to make it
1311         // faster we need to be more clever and probably also have more
1312         // calls to stuffClipboard. (Lgb)
1313         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1314
1315         // This doesn't make sense, if there is no selection
1316         if (!selection.set())
1317                 return;
1318
1319         // OK, we have a selection. This is always between selection.start
1320         // and selection.end
1321
1322         // make sure that the depth behind the selection are restored, too
1323         ParagraphList::iterator endpit = boost::next(selection.end.par());
1324         ParagraphList::iterator undoendpit = endpit;
1325         ParagraphList::iterator pars_end = ownerParagraphs().end();
1326
1327         if (endpit != pars_end && endpit->getDepth()) {
1328                 while (endpit != pars_end && endpit->getDepth()) {
1329                         ++endpit;
1330                         undoendpit = endpit;
1331                 }
1332         } else if (endpit != pars_end) {
1333                 // because of parindents etc.
1334                 ++endpit;
1335         }
1336
1337         setUndo(bv(), Undo::DELETE, selection.start.par(),
1338                 boost::prior(undoendpit));
1339
1340
1341         endpit = selection.end.par();
1342         int endpos = selection.end.pos();
1343
1344         boost::tie(endpit, endpos) = realcut ?
1345                 CutAndPaste::cutSelection(ownerParagraphs(),
1346                                           selection.start.par(), endpit,
1347                                           selection.start.pos(), endpos,
1348                                           bv()->buffer()->params.textclass,
1349                                           doclear)
1350                 : CutAndPaste::eraseSelection(ownerParagraphs(),
1351                                               selection.start.par(), endpit,
1352                                               selection.start.pos(), endpos,
1353                                               doclear);
1354         // sometimes necessary
1355         if (doclear)
1356                 selection.start.par()->stripLeadingSpaces();
1357
1358         redoParagraphs(selection.start, boost::next(endpit));
1359 #warning FIXME latent bug
1360         // endpit will be invalidated on redoParagraphs once ParagraphList
1361         // becomes a std::list? There are maybe other places on which this
1362         // can happend? (Ab)
1363         // cutSelection can invalidate the cursor so we need to set
1364         // it anew. (Lgb)
1365         // we prefer the end for when tracking changes
1366         cursor.pos(endpos);
1367         cursor.par(endpit);
1368
1369         // need a valid cursor. (Lgb)
1370         clearSelection();
1371
1372         setCursor(cursor.par(), cursor.pos());
1373         selection.cursor = cursor;
1374         updateCounters();
1375 }
1376
1377
1378 void LyXText::copySelection()
1379 {
1380         // stuff the selection onto the X clipboard, from an explicit copy request
1381         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1382
1383         // this doesnt make sense, if there is no selection
1384         if (!selection.set())
1385                 return;
1386
1387         // ok we have a selection. This is always between selection.start
1388         // and sel_end cursor
1389
1390         // copy behind a space if there is one
1391         while (selection.start.par()->size() > selection.start.pos()
1392                && selection.start.par()->isLineSeparator(selection.start.pos())
1393                && (selection.start.par() != selection.end.par()
1394                    || selection.start.pos() < selection.end.pos()))
1395                 selection.start.pos(selection.start.pos() + 1);
1396
1397         CutAndPaste::copySelection(selection.start.par(),
1398                                    selection.end.par(),
1399                                    selection.start.pos(), selection.end.pos(),
1400                                    bv()->buffer()->params.textclass);
1401 }
1402
1403
1404 void LyXText::pasteSelection()
1405 {
1406         // this does not make sense, if there is nothing to paste
1407         if (!CutAndPaste::checkPastePossible())
1408                 return;
1409
1410         setUndo(bv(), Undo::INSERT, cursor.par());
1411
1412         ParagraphList::iterator endpit;
1413         PitPosPair ppp;
1414
1415         ErrorList el;
1416
1417         boost::tie(ppp, endpit) =
1418                 CutAndPaste::pasteSelection(ownerParagraphs(),
1419                                             cursor.par(), cursor.pos(),
1420                                             bv()->buffer()->params.textclass,
1421                                             el);
1422         bv()->setErrorList(el);
1423         bv()->showErrorList(_("Paste"));
1424
1425         redoParagraphs(cursor, endpit);
1426
1427         setCursor(cursor.par(), cursor.pos());
1428         clearSelection();
1429
1430         selection.cursor = cursor;
1431         setCursor(ppp.first, ppp.second);
1432         setSelection();
1433         updateCounters();
1434 }
1435
1436
1437 void LyXText::setSelectionRange(lyx::pos_type length)
1438 {
1439         if (!length)
1440                 return;
1441
1442         selection.cursor = cursor;
1443         while (length--)
1444                 cursorRight(bv());
1445         setSelection();
1446 }
1447
1448
1449 // simple replacing. The font of the first selected character is used
1450 void LyXText::replaceSelectionWithString(string const & str)
1451 {
1452         setCursorParUndo(bv());
1453         freezeUndo();
1454
1455         if (!selection.set()) { // create a dummy selection
1456                 selection.end = cursor;
1457                 selection.start = cursor;
1458         }
1459
1460         // Get font setting before we cut
1461         pos_type pos = selection.end.pos();
1462         LyXFont const font = selection.start.par()
1463                 ->getFontSettings(bv()->buffer()->params,
1464                                   selection.start.pos());
1465
1466         // Insert the new string
1467         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1468                 selection.end.par()->insertChar(pos, (*cit), font);
1469                 ++pos;
1470         }
1471
1472         // Cut the selection
1473         cutSelection(true, false);
1474
1475         unFreezeUndo();
1476 }
1477
1478
1479 // needed to insert the selection
1480 void LyXText::insertStringAsLines(string const & str)
1481 {
1482         ParagraphList::iterator pit = cursor.par();
1483         pos_type pos = cursor.pos();
1484         ParagraphList::iterator endpit = boost::next(cursor.par());
1485
1486         setCursorParUndo(bv());
1487
1488         // only to be sure, should not be neccessary
1489         clearSelection();
1490
1491         bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1492
1493         redoParagraphs(cursor, endpit);
1494         setCursor(cursor.par(), cursor.pos());
1495         selection.cursor = cursor;
1496         setCursor(pit, pos);
1497         setSelection();
1498 }
1499
1500
1501 // turns double-CR to single CR, others where converted into one
1502 // blank. Then InsertStringAsLines is called
1503 void LyXText::insertStringAsParagraphs(string const & str)
1504 {
1505         string linestr(str);
1506         bool newline_inserted = false;
1507         for (string::size_type i = 0; i < linestr.length(); ++i) {
1508                 if (linestr[i] == '\n') {
1509                         if (newline_inserted) {
1510                                 // we know that \r will be ignored by
1511                                 // InsertStringA. Of course, it is a dirty
1512                                 // trick, but it works...
1513                                 linestr[i - 1] = '\r';
1514                                 linestr[i] = '\n';
1515                         } else {
1516                                 linestr[i] = ' ';
1517                                 newline_inserted = true;
1518                         }
1519                 } else if (IsPrintable(linestr[i])) {
1520                         newline_inserted = false;
1521                 }
1522         }
1523         insertStringAsLines(linestr);
1524 }
1525
1526
1527 void LyXText::checkParagraph(ParagraphList::iterator pit, pos_type pos)
1528 {
1529         LyXCursor tmpcursor;
1530
1531         int y = 0;
1532         pos_type z;
1533         RowList::iterator row = getRow(pit, pos, y);
1534         RowList::iterator beg = rows().begin();
1535
1536         // is there a break one row above
1537         if (row != beg
1538             && boost::prior(row)->par() == row->par()) {
1539                 z = rowBreakPoint(*boost::prior(row));
1540                 if (z >= row->pos()) {
1541                         // set the dimensions of the row above
1542                         y -= boost::prior(row)->height();
1543                         postPaint(y);
1544
1545                         breakAgain(boost::prior(row));
1546
1547                         // set the cursor again. Otherwise
1548                         // dangling pointers are possible
1549                         setCursor(cursor.par(), cursor.pos(),
1550                                   false, cursor.boundary());
1551                         selection.cursor = cursor;
1552                         return;
1553                 }
1554         }
1555
1556         int const tmpheight = row->height();
1557         pos_type const tmplast = lastPos(*this, row);
1558
1559         breakAgain(row);
1560         if (row->height() == tmpheight && lastPos(*this, row) == tmplast) {
1561                 postRowPaint(row, y);
1562         } else {
1563                 postPaint(y);
1564         }
1565
1566         // check the special right address boxes
1567         if (pit->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1568                 tmpcursor.par(pit);
1569                 tmpcursor.row(row);
1570                 tmpcursor.y(y);
1571                 tmpcursor.x(0);
1572                 tmpcursor.x_fix(0);
1573                 tmpcursor.pos(pos);
1574                 redoDrawingOfParagraph(tmpcursor);
1575         }
1576
1577         // set the cursor again. Otherwise dangling pointers are possible
1578         // also set the selection
1579
1580         if (selection.set()) {
1581                 tmpcursor = cursor;
1582                 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1583                                 false, selection.cursor.boundary());
1584                 selection.cursor = cursor;
1585                 setCursorIntern(selection.start.par(),
1586                                 selection.start.pos(),
1587                                 false, selection.start.boundary());
1588                 selection.start = cursor;
1589                 setCursorIntern(selection.end.par(),
1590                                 selection.end.pos(),
1591                                 false, selection.end.boundary());
1592                 selection.end = cursor;
1593                 setCursorIntern(last_sel_cursor.par(),
1594                                 last_sel_cursor.pos(),
1595                                 false, last_sel_cursor.boundary());
1596                 last_sel_cursor = cursor;
1597                 cursor = tmpcursor;
1598         }
1599         setCursorIntern(cursor.par(), cursor.pos(),
1600                         false, cursor.boundary());
1601 }
1602
1603
1604 // returns false if inset wasn't found
1605 bool LyXText::updateInset(Inset * inset)
1606 {
1607         // first check the current paragraph
1608         int pos = cursor.par()->getPositionOfInset(inset);
1609         if (pos != -1) {
1610                 checkParagraph(cursor.par(), pos);
1611                 return true;
1612         }
1613
1614         // check every paragraph
1615
1616         ParagraphList::iterator par = ownerParagraphs().begin();
1617         ParagraphList::iterator end = ownerParagraphs().end();
1618         for (; par != end; ++par) {
1619                 pos = par->getPositionOfInset(inset);
1620                 if (pos != -1) {
1621                         checkParagraph(par, pos);
1622                         return true;
1623                 }
1624         };
1625
1626         return false;
1627 }
1628
1629
1630 bool LyXText::setCursor(ParagraphList::iterator pit,
1631                         pos_type pos,
1632                         bool setfont, bool boundary)
1633 {
1634         LyXCursor old_cursor = cursor;
1635         setCursorIntern(pit, pos, setfont, boundary);
1636         return deleteEmptyParagraphMechanism(old_cursor);
1637 }
1638
1639
1640 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1641                         pos_type pos, bool boundary)
1642 {
1643         lyx::Assert(pit != ownerParagraphs().end());
1644
1645         cur.par(pit);
1646         cur.pos(pos);
1647         cur.boundary(boundary);
1648
1649         // get the cursor y position in text
1650         int y = 0;
1651         RowList::iterator row = getRow(pit, pos, y);
1652         RowList::iterator beg = rows().begin();
1653
1654         RowList::iterator old_row = row;
1655         cur.irow(row);
1656         // if we are before the first char of this row and are still in the
1657         // same paragraph and there is a previous row then put the cursor on
1658         // the end of the previous row
1659         cur.iy(y + row->baseline());
1660         if (row != beg &&
1661             pos &&
1662             boost::prior(row)->par() == row->par() &&
1663             pos < pit->size() &&
1664             pit->getChar(pos) == Paragraph::META_INSET) {
1665                 Inset * ins = pit->getInset(pos);
1666                 if (ins && (ins->needFullRow() || ins->display())) {
1667                         --row;
1668                         y -= row->height();
1669                 }
1670         }
1671
1672         cur.row(row);
1673         // y is now the beginning of the cursor row
1674         y += row->baseline();
1675         // y is now the cursor baseline
1676         cur.y(y);
1677
1678         pos_type last = lastPrintablePos(*this, old_row);
1679
1680         // None of these should happen, but we're scaredy-cats
1681         if (pos > pit->size()) {
1682                 lyxerr << "dont like 1 please report" << endl;
1683                 pos = 0;
1684                 cur.pos(0);
1685         } else if (pos > last + 1) {
1686                 lyxerr << "dont like 2 please report" << endl;
1687                 // This shouldn't happen.
1688                 pos = last + 1;
1689                 cur.pos(pos);
1690         } else if (pos < row->pos()) {
1691                 lyxerr << "dont like 3 please report" << endl;
1692                 pos = row->pos();
1693                 cur.pos(pos);
1694         }
1695
1696         // now get the cursors x position
1697         float x = getCursorX(row, pos, last, boundary);
1698         cur.x(int(x));
1699         cur.x_fix(cur.x());
1700         if (old_row != row) {
1701                 x = getCursorX(old_row, pos, last, boundary);
1702                 cur.ix(int(x));
1703         } else
1704                 cur.ix(cur.x());
1705 /* We take out this for the time being because 1) the redraw code is not
1706    prepared to this yet and 2) because some good policy has yet to be decided
1707    while editting: for instance how to act on rows being created/deleted
1708    because of DEPM.
1709 */
1710 #if 0
1711         //if the cursor is in a visible row, anchor to it
1712         int topy = top_y();
1713         if (topy < y && y < topy + bv()->workHeight())
1714                 anchor_row(row);
1715 #endif
1716 }
1717
1718
1719 float LyXText::getCursorX(RowList::iterator rit,
1720                           pos_type pos, pos_type last, bool boundary) const
1721 {
1722         pos_type cursor_vpos = 0;
1723         float x;
1724         float fill_separator;
1725         float fill_hfill;
1726         float fill_label_hfill;
1727         // This call HAS to be here because of the BidiTables!!!
1728         prepareToPrint(rit, x, fill_separator, fill_hfill,
1729                        fill_label_hfill);
1730
1731         ParagraphList::iterator rit_par = rit->par();
1732         pos_type const rit_pos = rit->pos();
1733
1734         if (last < rit_pos)
1735                 cursor_vpos = rit_pos;
1736         else if (pos > last && !boundary)
1737                 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1738                         ? rit_pos : last + 1;
1739         else if (pos > rit_pos && (pos > last || boundary))
1740                 /// Place cursor after char at (logical) position pos - 1
1741                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1742                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1743         else
1744                 /// Place cursor before char at (logical) position pos
1745                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1746                         ? log2vis(pos) : log2vis(pos) + 1;
1747
1748         pos_type body_pos = rit_par->beginningOfBody();
1749         if ((body_pos > 0) &&
1750             ((body_pos - 1 > last) || !rit_par->isLineSeparator(body_pos - 1)))
1751                 body_pos = 0;
1752
1753         for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1754                 pos_type pos = vis2log(vpos);
1755                 if (body_pos > 0 && pos == body_pos - 1) {
1756                         x += fill_label_hfill +
1757                                 font_metrics::width(
1758                                         rit_par->layout()->labelsep,
1759                                         getLabelFont(bv()->buffer(), rit_par));
1760                         if (rit_par->isLineSeparator(body_pos - 1))
1761                                 x -= singleWidth(rit_par, body_pos - 1);
1762                 }
1763
1764                 if (hfillExpansion(*this, rit, pos)) {
1765                         x += singleWidth(rit_par, pos);
1766                         if (pos >= body_pos)
1767                                 x += fill_hfill;
1768                         else
1769                                 x += fill_label_hfill;
1770                 } else if (rit_par->isSeparator(pos)) {
1771                         x += singleWidth(rit_par, pos);
1772                         if (pos >= body_pos)
1773                                 x += fill_separator;
1774                 } else
1775                         x += singleWidth(rit_par, pos);
1776         }
1777         return x;
1778 }
1779
1780
1781 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1782                               pos_type pos, bool setfont, bool boundary)
1783 {
1784         InsetText * it = static_cast<InsetText *>(pit->inInset());
1785         if (it) {
1786                 if (it != inset_owner) {
1787                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1788                                               << endl
1789                                               << "inset_owner is "
1790                                               << inset_owner << endl;
1791 #ifdef WITH_WARNINGS
1792 #warning I believe this code is wrong. (Lgb)
1793 #warning Jürgen, have a look at this. (Lgb)
1794 #warning Hmmm, I guess you are right but we
1795 #warning should verify when this is needed
1796 #endif
1797                         // Jürgen, would you like to have a look?
1798                         // I guess we need to move the outer cursor
1799                         // and open and lock the inset (bla bla bla)
1800                         // stuff I don't know... so can you have a look?
1801                         // (Lgb)
1802                         // I moved the lyxerr stuff in here so we can see if
1803                         // this is actually really needed and where!
1804                         // (Jug)
1805                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1806                         return;
1807                 }
1808         }
1809
1810         setCursor(cursor, pit, pos, boundary);
1811         if (setfont)
1812                 setCurrentFont();
1813 }
1814
1815
1816 void LyXText::setCurrentFont()
1817 {
1818         pos_type pos = cursor.pos();
1819         ParagraphList::iterator pit = cursor.par();
1820
1821         if (cursor.boundary() && pos > 0)
1822                 --pos;
1823
1824         if (pos > 0) {
1825                 if (pos == pit->size())
1826                         --pos;
1827                 else // potentional bug... BUG (Lgb)
1828                         if (pit->isSeparator(pos)) {
1829                                 if (pos > cursor.row()->pos() &&
1830                                     bidi_level(pos) % 2 ==
1831                                     bidi_level(pos - 1) % 2)
1832                                         --pos;
1833                                 else if (pos + 1 < pit->size())
1834                                         ++pos;
1835                         }
1836         }
1837
1838         current_font =
1839                 pit->getFontSettings(bv()->buffer()->params, pos);
1840         real_current_font = getFont(bv()->buffer(), pit, pos);
1841
1842         if (cursor.pos() == pit->size() &&
1843             isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1844             !cursor.boundary()) {
1845                 Language const * lang =
1846                         pit->getParLanguage(bv()->buffer()->params);
1847                 current_font.setLanguage(lang);
1848                 current_font.setNumber(LyXFont::OFF);
1849                 real_current_font.setLanguage(lang);
1850                 real_current_font.setNumber(LyXFont::OFF);
1851         }
1852 }
1853
1854
1855 // returns the column near the specified x-coordinate of the row
1856 // x is set to the real beginning of this column
1857 pos_type
1858 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1859 {
1860         float tmpx = 0.0;
1861         float fill_separator;
1862         float fill_hfill;
1863         float fill_label_hfill;
1864
1865         prepareToPrint(rit, tmpx, fill_separator,
1866                        fill_hfill, fill_label_hfill);
1867
1868         pos_type vc = rit->pos();
1869         pos_type last = lastPrintablePos(*this, rit);
1870         pos_type c = 0;
1871
1872         ParagraphList::iterator rit_par = rit->par();
1873         LyXLayout_ptr const & layout = rit->par()->layout();
1874
1875         bool left_side = false;
1876
1877         pos_type body_pos = rit_par->beginningOfBody();
1878         float last_tmpx = tmpx;
1879
1880         if (body_pos > 0 &&
1881             (body_pos - 1 > last ||
1882              !rit_par->isLineSeparator(body_pos - 1)))
1883                 body_pos = 0;
1884
1885         // check for empty row
1886         if (!rit_par->size()) {
1887                 x = int(tmpx);
1888                 return 0;
1889         }
1890
1891         while (vc <= last && tmpx <= x) {
1892                 c = vis2log(vc);
1893                 last_tmpx = tmpx;
1894                 if (body_pos > 0 && c == body_pos - 1) {
1895                         tmpx += fill_label_hfill +
1896                                 font_metrics::width(layout->labelsep,
1897                                                getLabelFont(bv()->buffer(), rit_par));
1898                         if (rit_par->isLineSeparator(body_pos - 1))
1899                                 tmpx -= singleWidth(rit_par, body_pos - 1);
1900                 }
1901
1902                 if (hfillExpansion(*this, rit, c)) {
1903                         tmpx += singleWidth(rit_par, c);
1904                         if (c >= body_pos)
1905                                 tmpx += fill_hfill;
1906                         else
1907                                 tmpx += fill_label_hfill;
1908                 } else if (rit_par->isSeparator(c)) {
1909                         tmpx += singleWidth(rit_par, c);
1910                         if (c >= body_pos)
1911                                 tmpx+= fill_separator;
1912                 } else {
1913                         tmpx += singleWidth(rit_par, c);
1914                 }
1915                 ++vc;
1916         }
1917
1918         if ((tmpx + last_tmpx) / 2 > x) {
1919                 tmpx = last_tmpx;
1920                 left_side = true;
1921         }
1922
1923         if (vc > last + 1)  // This shouldn't happen.
1924                 vc = last + 1;
1925
1926         boundary = false;
1927         // This (rtl_support test) is not needed, but gives
1928         // some speedup if rtl_support=false
1929         bool const lastrow = lyxrc.rtl_support &&
1930                 (boost::next(rit) == rowlist_.end() ||
1931                  boost::next(rit)->par() != rit_par);
1932         // If lastrow is false, we don't need to compute
1933         // the value of rtl.
1934         bool const rtl = (lastrow)
1935                 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1936                 : false;
1937         if (lastrow &&
1938                  ((rtl &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
1939                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
1940                 c = last + 1;
1941         else if (vc == rit->pos()) {
1942                 c = vis2log(vc);
1943                 if (bidi_level(c) % 2 == 1)
1944                         ++c;
1945         } else {
1946                 c = vis2log(vc - 1);
1947                 bool const rtl = (bidi_level(c) % 2 == 1);
1948                 if (left_side == rtl) {
1949                         ++c;
1950                         boundary = isBoundary(bv()->buffer(), *rit_par, c);
1951                 }
1952         }
1953
1954         if (rit->pos() <= last && c > last
1955             && rit_par->isNewline(last)) {
1956                 if (bidi_level(last) % 2 == 0)
1957                         tmpx -= singleWidth(rit_par, last);
1958                 else
1959                         tmpx += singleWidth(rit_par, last);
1960                 c = last;
1961         }
1962
1963         c -= rit->pos();
1964         x = int(tmpx);
1965         return c;
1966 }
1967
1968
1969 void LyXText::setCursorFromCoordinates(int x, int y)
1970 {
1971         LyXCursor old_cursor = cursor;
1972
1973         setCursorFromCoordinates(cursor, x, y);
1974         setCurrentFont();
1975         deleteEmptyParagraphMechanism(old_cursor);
1976 }
1977
1978
1979 namespace {
1980
1981         /**
1982          * return true if the cursor given is at the end of a row,
1983          * and the next row is filled by an inset that spans an entire
1984          * row.
1985          */
1986         bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur) {
1987                 RowList::iterator row = cur.row();
1988                 if (boost::next(row) == lt.rows().end())
1989                         return false;
1990                 Row const & next = *boost::next(row);
1991
1992                 if (next.pos() != cur.pos() || next.par() != cur.par())
1993                         return false;
1994
1995                 if (cur.pos() == cur.par()->size()
1996                     || !cur.par()->isInset(cur.pos()))
1997                         return false;
1998                 Inset const * inset = cur.par()->getInset(cur.pos());
1999                 if (inset->needFullRow() || inset->display())
2000                         return true;
2001                 return false;
2002         }
2003 }
2004
2005
2006 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
2007 {
2008         // Get the row first.
2009
2010         RowList::iterator row = getRowNearY(y);
2011         bool bound = false;
2012         pos_type const column = getColumnNearX(row, x, bound);
2013         cur.par(row->par());
2014         cur.pos(row->pos() + column);
2015         cur.x(x);
2016         cur.y(y + row->baseline());
2017         cur.row(row);
2018
2019         if (beforeFullRowInset(*this, cur)) {
2020                 pos_type last = lastPrintablePos(*this, row);
2021                 float x = getCursorX(boost::next(row), cur.pos(), last, bound);
2022                 cur.ix(int(x));
2023                 cur.iy(y + row->height() + boost::next(row)->baseline());
2024                 cur.irow(boost::next(row));
2025         } else {
2026                 cur.iy(cur.y());
2027                 cur.ix(cur.x());
2028                 cur.irow(row);
2029         }
2030         cur.boundary(bound);
2031 }
2032
2033
2034 void LyXText::cursorLeft(bool internal)
2035 {
2036         if (cursor.pos() > 0) {
2037                 bool boundary = cursor.boundary();
2038                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
2039                 if (!internal && !boundary &&
2040                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
2041                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
2042         } else if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph.
2043                 ParagraphList::iterator pit = boost::prior(cursor.par());
2044                 setCursor(pit, pit->size());
2045         }
2046 }
2047
2048
2049 void LyXText::cursorRight(bool internal)
2050 {
2051         bool const at_end = (cursor.pos() == cursor.par()->size());
2052         bool const at_newline = !at_end &&
2053                 cursor.par()->isNewline(cursor.pos());
2054
2055         if (!internal && cursor.boundary() && !at_newline)
2056                 setCursor(cursor.par(), cursor.pos(), true, false);
2057         else if (!at_end) {
2058                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
2059                 if (!internal &&
2060                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
2061                         setCursor(cursor.par(), cursor.pos(), true, true);
2062         } else if (boost::next(cursor.par()) != ownerParagraphs().end())
2063                 setCursor(boost::next(cursor.par()), 0);
2064 }
2065
2066
2067 void LyXText::cursorUp(bool selecting)
2068 {
2069 #if 1
2070         int x = cursor.x_fix();
2071         int y = cursor.y() - cursor.row()->baseline() - 1;
2072         setCursorFromCoordinates(x, y);
2073         if (!selecting) {
2074                 int topy = top_y();
2075                 int y1 = cursor.iy() - topy;
2076                 int y2 = y1;
2077                 y -= topy;
2078                 Inset * inset_hit = checkInsetHit(x, y1);
2079                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2080                         inset_hit->localDispatch(
2081                                 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
2082                 }
2083         }
2084 #else
2085         setCursorFromCoordinates(bv(), cursor.x_fix(),
2086                                  cursor.y() - cursor.row()->baseline() - 1);
2087 #endif
2088 }
2089
2090
2091 void LyXText::cursorDown(bool selecting)
2092 {
2093 #if 1
2094         int x = cursor.x_fix();
2095         int y = cursor.y() - cursor.row()->baseline() +
2096                 cursor.row()->height() + 1;
2097         setCursorFromCoordinates(x, y);
2098         if (!selecting && cursor.row() == cursor.irow()) {
2099                 int topy = top_y();
2100                 int y1 = cursor.iy() - topy;
2101                 int y2 = y1;
2102                 y -= topy;
2103                 Inset * inset_hit = checkInsetHit(x, y1);
2104                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2105                         FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
2106                         inset_hit->localDispatch(cmd);
2107                 }
2108         }
2109 #else
2110         setCursorFromCoordinates(bv(), cursor.x_fix(),
2111                                  cursor.y() - cursor.row()->baseline()
2112                                  + cursor.row()->height() + 1);
2113 #endif
2114 }
2115
2116
2117 void LyXText::cursorUpParagraph()
2118 {
2119         if (cursor.pos() > 0) {
2120                 setCursor(cursor.par(), 0);
2121         }
2122         else if (cursor.par() != ownerParagraphs().begin()) {
2123                 setCursor(boost::prior(cursor.par()), 0);
2124         }
2125 }
2126
2127
2128 void LyXText::cursorDownParagraph()
2129 {
2130         if (boost::next(cursor.par()) != ownerParagraphs().end()) {
2131                 setCursor(boost::next(cursor.par()), 0);
2132         } else {
2133                 setCursor(cursor.par(), cursor.par()->size());
2134         }
2135 }
2136
2137 // fix the cursor `cur' after a characters has been deleted at `where'
2138 // position. Called by deleteEmptyParagraphMechanism
2139 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2140                                    LyXCursor const & where)
2141 {
2142         // if cursor is not in the paragraph where the delete occured,
2143         // do nothing
2144         if (cur.par() != where.par())
2145                 return;
2146
2147         // if cursor position is after the place where the delete occured,
2148         // update it
2149         if (cur.pos() > where.pos())
2150                 cur.pos(cur.pos()-1);
2151
2152         // check also if we don't want to set the cursor on a spot behind the
2153         // pagragraph because we erased the last character.
2154         if (cur.pos() > cur.par()->size())
2155                 cur.pos(cur.par()->size());
2156
2157         // recompute row et al. for this cursor
2158         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2159 }
2160
2161
2162 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2163 {
2164         // Would be wrong to delete anything if we have a selection.
2165         if (selection.set())
2166                 return false;
2167
2168         // We allow all kinds of "mumbo-jumbo" when freespacing.
2169         if (old_cursor.par()->layout()->free_spacing
2170             || old_cursor.par()->isFreeSpacing()) {
2171                 return false;
2172         }
2173
2174         /* Ok I'll put some comments here about what is missing.
2175            I have fixed BackSpace (and thus Delete) to not delete
2176            double-spaces automagically. I have also changed Cut,
2177            Copy and Paste to hopefully do some sensible things.
2178            There are still some small problems that can lead to
2179            double spaces stored in the document file or space at
2180            the beginning of paragraphs. This happens if you have
2181            the cursor betwenn to spaces and then save. Or if you
2182            cut and paste and the selection have a space at the
2183            beginning and then save right after the paste. I am
2184            sure none of these are very hard to fix, but I will
2185            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2186            that I can get some feedback. (Lgb)
2187         */
2188
2189         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2190         // delete the LineSeparator.
2191         // MISSING
2192
2193         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2194         // delete the LineSeparator.
2195         // MISSING
2196
2197         // If the pos around the old_cursor were spaces, delete one of them.
2198         if (old_cursor.par() != cursor.par()
2199             || old_cursor.pos() != cursor.pos()) {
2200                 // Only if the cursor has really moved
2201
2202                 if (old_cursor.pos() > 0
2203                     && old_cursor.pos() < old_cursor.par()->size()
2204                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2205                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2206                         old_cursor.par()->erase(old_cursor.pos() - 1);
2207                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2208
2209 #ifdef WITH_WARNINGS
2210 #warning This will not work anymore when we have multiple views of the same buffer
2211 // In this case, we will have to correct also the cursors held by
2212 // other bufferviews. It will probably be easier to do that in a more
2213 // automated way in LyXCursor code. (JMarc 26/09/2001)
2214 #endif
2215                         // correct all cursors held by the LyXText
2216                         fixCursorAfterDelete(cursor, old_cursor);
2217                         fixCursorAfterDelete(selection.cursor,
2218                                              old_cursor);
2219                         fixCursorAfterDelete(selection.start,
2220                                              old_cursor);
2221                         fixCursorAfterDelete(selection.end, old_cursor);
2222                         fixCursorAfterDelete(last_sel_cursor,
2223                                              old_cursor);
2224                         fixCursorAfterDelete(toggle_cursor, old_cursor);
2225                         fixCursorAfterDelete(toggle_end_cursor,
2226                                              old_cursor);
2227                         return false;
2228                 }
2229         }
2230
2231         // don't delete anything if this is the ONLY paragraph!
2232         if (ownerParagraphs().size() == 1)
2233                 return false;
2234
2235         // Do not delete empty paragraphs with keepempty set.
2236         if (old_cursor.par()->layout()->keepempty)
2237                 return false;
2238
2239         // only do our magic if we changed paragraph
2240         if (old_cursor.par() == cursor.par())
2241                 return false;
2242
2243         // record if we have deleted a paragraph
2244         // we can't possibly have deleted a paragraph before this point
2245         bool deleted = false;
2246
2247         if (old_cursor.par()->empty() ||
2248             (old_cursor.par()->size() == 1 &&
2249              old_cursor.par()->isLineSeparator(0))) {
2250                 // ok, we will delete anything
2251                 LyXCursor tmpcursor;
2252
2253                 deleted = true;
2254
2255                 bool selection_position_was_oldcursor_position = (
2256                         selection.cursor.par()  == old_cursor.par()
2257                         && selection.cursor.pos() == old_cursor.pos());
2258
2259                 if (old_cursor.row() != rows().begin()) {
2260                         RowList::iterator
2261                                 prevrow = boost::prior(old_cursor.row());
2262                         const_cast<LyXText *>(this)->postPaint(old_cursor.y() - old_cursor.row()->baseline() - prevrow->height());
2263                         tmpcursor = cursor;
2264                         cursor = old_cursor; // that undo can restore the right cursor position
2265                         #warning FIXME. --end() iterator is usable here
2266                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2267                         while (endpit != ownerParagraphs().end() &&
2268                                endpit->getDepth()) {
2269                                 ++endpit;
2270                         }
2271
2272                         setUndo(bv(), Undo::DELETE, old_cursor.par(),
2273                                 boost::prior(endpit));
2274                         cursor = tmpcursor;
2275
2276                         // delete old row
2277                         removeRow(old_cursor.row());
2278                         // delete old par
2279                         ownerParagraphs().erase(old_cursor.par());
2280
2281                         /* Breakagain the next par. Needed because of
2282                          * the parindent that can occur or dissappear.
2283                          * The next row can change its height, if
2284                          * there is another layout before */
2285                         if (boost::next(prevrow) != rows().end()) {
2286                                 breakAgain(boost::next(prevrow));
2287                                 updateCounters();
2288                         }
2289                         setHeightOfRow(prevrow);
2290                 } else {
2291                         RowList::iterator nextrow = boost::next(old_cursor.row());
2292                         const_cast<LyXText *>(this)->postPaint(
2293                                 old_cursor.y() - old_cursor.row()->baseline());
2294
2295                         tmpcursor = cursor;
2296                         cursor = old_cursor; // that undo can restore the right cursor position
2297 #warning FIXME. --end() iterator is usable here
2298                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2299                         while (endpit != ownerParagraphs().end() &&
2300                                endpit->getDepth()) {
2301                                 ++endpit;
2302                         }
2303
2304                         setUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2305                         cursor = tmpcursor;
2306
2307                         // delete old row
2308                         removeRow(old_cursor.row());
2309                         // delete old par
2310                         ownerParagraphs().erase(old_cursor.par());
2311
2312                         /* Breakagain the next par. Needed because of
2313                            the parindent that can occur or dissappear.
2314                            The next row can change its height, if
2315                            there is another layout before */
2316                         if (nextrow != rows().end()) {
2317                                 breakAgain(nextrow);
2318                                 updateCounters();
2319                         }
2320                 }
2321
2322                 // correct cursor y
2323                 setCursorIntern(cursor.par(), cursor.pos());
2324
2325                 if (selection_position_was_oldcursor_position) {
2326                         // correct selection
2327                         selection.cursor = cursor;
2328                 }
2329         }
2330         if (!deleted) {
2331                 if (old_cursor.par()->stripLeadingSpaces()) {
2332                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2333                         // correct cursor y
2334                         setCursorIntern(cursor.par(), cursor.pos());
2335                         selection.cursor = cursor;
2336                 }
2337         }
2338         return deleted;
2339 }
2340
2341
2342 ParagraphList & LyXText::ownerParagraphs() const
2343 {
2344         if (inset_owner) {
2345                 return inset_owner->paragraphs;
2346         }
2347         return bv_owner->buffer()->paragraphs;
2348 }
2349
2350
2351 LyXText::refresh_status LyXText::refreshStatus() const
2352 {
2353         return refresh_status_;
2354 }
2355
2356
2357 void LyXText::clearPaint()
2358 {
2359         refresh_status_ = REFRESH_NONE;
2360         refresh_row = rows().end();
2361         refresh_y = 0;
2362 }
2363
2364
2365 void LyXText::postPaint(int start_y)
2366 {
2367         refresh_status old = refresh_status_;
2368
2369         refresh_status_ = REFRESH_AREA;
2370         refresh_row = rows().end();
2371
2372         if (old != REFRESH_NONE && refresh_y < start_y)
2373                 return;
2374
2375         refresh_y = start_y;
2376
2377         if (!inset_owner)
2378                 return;
2379
2380         // We are an inset's lyxtext. Tell the top-level lyxtext
2381         // it needs to update the row we're in.
2382         LyXText * t = bv()->text;
2383         t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2384 }
2385
2386
2387 // FIXME: we should probably remove this y parameter,
2388 // make refresh_y be 0, and use row->y etc.
2389 void LyXText::postRowPaint(RowList::iterator rit, int start_y)
2390 {
2391         if (refresh_status_ != REFRESH_NONE && refresh_y < start_y) {
2392                 refresh_status_ = REFRESH_AREA;
2393                 return;
2394         } else {
2395                 refresh_y = start_y;
2396         }
2397
2398         if (refresh_status_ == REFRESH_AREA)
2399                 return;
2400
2401         refresh_status_ = REFRESH_ROW;
2402         refresh_row = rit;
2403
2404         if (!inset_owner)
2405                 return;
2406
2407         // We are an inset's lyxtext. Tell the top-level lyxtext
2408         // it needs to update the row we're in.
2409         LyXText * t = bv()->text;
2410         t->postRowPaint(t->cursor.row(), t->cursor.y() - t->cursor.row()->baseline());
2411 }
2412
2413
2414 bool LyXText::isInInset() const
2415 {
2416         // Sub-level has non-null bv owner and
2417         // non-null inset owner.
2418         return inset_owner != 0 && bv_owner != 0;
2419 }
2420
2421
2422 int defaultRowHeight()
2423 {
2424         LyXFont const font(LyXFont::ALL_SANE);
2425         return int(font_metrics::maxAscent(font)
2426                  + font_metrics::maxDescent(font) * 1.5);
2427 }