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