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