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