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