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