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