]> git.lyx.org Git - lyx.git/blob - src/text2.C
fix bibkey writing, avoid a few copies, whitespace changes..
[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                 //      Inset * inset = new InsetBibKey(InsetCommandParams("bibitem"));
1268                 //      //par->insertInset(0, inset);
1269                 //}
1270                 if (par->bibkey()) {
1271                         par->bibkey()->setCounter(number);
1272                         par->params().labelString(layout->labelstring());
1273                 }
1274                 // In biblio should't be following counters but...
1275         } else {
1276                 string s = layout->labelstring();
1277
1278                 // the caption hack:
1279                 if (layout->labeltype == LABEL_SENSITIVE) {
1280                         Paragraph * tmppar = par;
1281                         Inset * in = 0;
1282                         bool isOK = false;
1283                         while (tmppar && tmppar->inInset()
1284                                // the single '=' is intended below
1285                                && (in = tmppar->inInset()->owner())) {
1286                                 if (in->lyxCode() == Inset::FLOAT_CODE ||
1287                                     in->lyxCode() == Inset::WRAP_CODE) {
1288                                         isOK = true;
1289                                         break;
1290                                 } else {
1291                                         tmppar = in->parOwner();
1292                                 }
1293                         }
1294
1295                         if (isOK) {
1296                                 Floating const & fl
1297                                         = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1298
1299                                 textclass.counters().step(fl.type());
1300
1301                                 // Doesn't work... yet.
1302 #warning use boost.format
1303 #if USE_BOOST_FORMAT
1304                                 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1305                                 // s << boost::format(_("%1$s %1$d:")
1306                                 //        % fl.name()
1307                                 //        % buf->counters().value(fl.name());
1308 #else
1309                                 ostringstream o;
1310                                 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1311                                 o << fl.name() << " #:";
1312                                 s = STRCONV(o.str());
1313 #endif
1314                         } else {
1315                                 // par->SetLayout(0);
1316                                 // s = layout->labelstring;
1317                                 s = _("Senseless: ");
1318                         }
1319                 }
1320                 par->params().labelString(s);
1321
1322                 // reset the enumeration counter. They are always reset
1323                 // when there is any other layout between
1324                 // Just fall-through between the cases so that all
1325                 // enum counters deeper than enumdepth is also reset.
1326                 switch (par->enumdepth) {
1327                 case 0:
1328                         textclass.counters().reset("enumi");
1329                 case 1:
1330                         textclass.counters().reset("enumii");
1331                 case 2:
1332                         textclass.counters().reset("enumiii");
1333                 case 3:
1334                         textclass.counters().reset("enumiv");
1335                 }
1336         }
1337 }
1338
1339
1340 // Updates all counters. Paragraphs with changed label string will be rebroken
1341 void LyXText::updateCounters(BufferView * bview) const
1342 {
1343         Row * row = firstrow;
1344         Paragraph * par = row->par();
1345
1346         // CHECK if this is really needed. (Lgb)
1347         bview->buffer()->params.getLyXTextClass().counters().reset();
1348
1349         while (par) {
1350                 while (row->par() != par)
1351                         row = row->next();
1352
1353                 string const oldLabel = par->params().labelString();
1354
1355                 // setCounter can potentially change the labelString.
1356                 setCounter(bview->buffer(), par);
1357
1358                 string const & newLabel = par->params().labelString();
1359
1360                 if (oldLabel.empty() && !newLabel.empty()) {
1361                         removeParagraph(row);
1362                         appendParagraph(bview, row);
1363                 }
1364
1365                 par = par->next();
1366         }
1367 }
1368
1369
1370 void LyXText::insertInset(BufferView * bview, Inset * inset)
1371 {
1372         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1373                 return;
1374         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1375         freezeUndo();
1376         cursor.par()->insertInset(cursor.pos(), inset);
1377         // Just to rebreak and refresh correctly.
1378         // The character will not be inserted a second time
1379         insertChar(bview, Paragraph::META_INSET);
1380         // If we enter a highly editable inset the cursor should be to before
1381         // the inset. This couldn't happen before as Undo was not handled inside
1382         // inset now after the Undo LyX tries to call inset->Edit(...) again
1383         // and cannot do this as the cursor is behind the inset and GetInset
1384         // does not return the inset!
1385         if (isHighlyEditableInset(inset)) {
1386                 cursorLeft(bview, true);
1387         }
1388         unFreezeUndo();
1389 }
1390
1391
1392 void LyXText::copyEnvironmentType()
1393 {
1394         copylayouttype = cursor.par()->layout()->name();
1395 }
1396
1397
1398 void LyXText::pasteEnvironmentType(BufferView * bview)
1399 {
1400         // do nothing if there has been no previous copyEnvironmentType()
1401         if (!copylayouttype.empty())
1402                 setLayout(bview, copylayouttype);
1403 }
1404
1405
1406 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1407 {
1408         // Stuff what we got on the clipboard. Even if there is no selection.
1409
1410         // There is a problem with having the stuffing here in that the
1411         // larger the selection the slower LyX will get. This can be
1412         // solved by running the line below only when the selection has
1413         // finished. The solution used currently just works, to make it
1414         // faster we need to be more clever and probably also have more
1415         // calls to stuffClipboard. (Lgb)
1416         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1417
1418         // This doesn't make sense, if there is no selection
1419         if (!selection.set())
1420                 return;
1421
1422         // OK, we have a selection. This is always between selection.start
1423         // and selection.end
1424
1425         // make sure that the depth behind the selection are restored, too
1426         Paragraph * endpar = selection.end.par()->next();
1427         Paragraph * undoendpar = endpar;
1428
1429         if (endpar && endpar->getDepth()) {
1430                 while (endpar && endpar->getDepth()) {
1431                         endpar = endpar->next();
1432                         undoendpar = endpar;
1433                 }
1434         } else if (endpar) {
1435                 endpar = endpar->next(); // because of parindents etc.
1436         }
1437
1438         setUndo(bview, Undo::DELETE,
1439                 selection.start.par(), undoendpar);
1440
1441         // there are two cases: cut only within one paragraph or
1442         // more than one paragraph
1443         if (selection.start.par() == selection.end.par()) {
1444                 // only within one paragraph
1445                 endpar = selection.end.par();
1446                 int pos = selection.end.pos();
1447                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1448                                           selection.start.pos(), pos,
1449                                           bview->buffer()->params.textclass,
1450                                           doclear, realcut);
1451                 selection.end.pos(pos);
1452         } else {
1453                 endpar = selection.end.par();
1454                 int pos = selection.end.pos();
1455                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1456                                           selection.start.pos(), pos,
1457                                           bview->buffer()->params.textclass,
1458                                           doclear, realcut);
1459                 cursor.par(endpar);
1460                 selection.end.par(endpar);
1461                 selection.end.pos(pos);
1462                 cursor.pos(selection.end.pos());
1463         }
1464         endpar = endpar->next();
1465
1466         // sometimes necessary
1467         if (doclear)
1468                 selection.start.par()->stripLeadingSpaces();
1469
1470         redoParagraphs(bview, selection.start, endpar);
1471
1472         // cutSelection can invalidate the cursor so we need to set
1473         // it anew. (Lgb)
1474         // we prefer the end for when tracking changes
1475         cursor = selection.end;
1476
1477         // need a valid cursor. (Lgb)
1478         clearSelection();
1479
1480         setCursor(bview, cursor.par(), cursor.pos());
1481         selection.cursor = cursor;
1482         updateCounters(bview);
1483 }
1484
1485
1486 void LyXText::copySelection(BufferView * bview)
1487 {
1488         // stuff the selection onto the X clipboard, from an explicit copy request
1489         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1490
1491         // this doesnt make sense, if there is no selection
1492         if (!selection.set())
1493                 return;
1494
1495         // ok we have a selection. This is always between selection.start
1496         // and sel_end cursor
1497
1498         // copy behind a space if there is one
1499         while (selection.start.par()->size() > selection.start.pos()
1500                && selection.start.par()->isLineSeparator(selection.start.pos())
1501                && (selection.start.par() != selection.end.par()
1502                    || selection.start.pos() < selection.end.pos()))
1503                 selection.start.pos(selection.start.pos() + 1);
1504
1505         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1506                                    selection.start.pos(), selection.end.pos(),
1507                                    bview->buffer()->params.textclass);
1508 }
1509
1510
1511 void LyXText::pasteSelection(BufferView * bview)
1512 {
1513         // this does not make sense, if there is nothing to paste
1514         if (!CutAndPaste::checkPastePossible(cursor.par()))
1515                 return;
1516
1517         setUndo(bview, Undo::INSERT,
1518                 cursor.par(), cursor.par()->next());
1519
1520         Paragraph * endpar;
1521         Paragraph * actpar = cursor.par();
1522         int pos = cursor.pos();
1523
1524         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1525                                     bview->buffer()->params.textclass);
1526
1527         redoParagraphs(bview, cursor, endpar);
1528
1529         setCursor(bview, cursor.par(), cursor.pos());
1530         clearSelection();
1531
1532         selection.cursor = cursor;
1533         setCursor(bview, actpar, pos);
1534         setSelection(bview);
1535         updateCounters(bview);
1536 }
1537
1538
1539 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1540 {
1541         if (!length)
1542                 return;
1543
1544         selection.cursor = cursor;
1545         while (length--)
1546                 cursorRight(bview);
1547         setSelection(bview);
1548 }
1549
1550
1551 // simple replacing. The font of the first selected character is used
1552 void LyXText::replaceSelectionWithString(BufferView * bview,
1553                                          string const & str)
1554 {
1555         setCursorParUndo(bview);
1556         freezeUndo();
1557
1558         if (!selection.set()) { // create a dummy selection
1559                 selection.end = cursor;
1560                 selection.start = cursor;
1561         }
1562
1563         // Get font setting before we cut
1564         pos_type pos = selection.end.pos();
1565         LyXFont const font = selection.start.par()
1566                 ->getFontSettings(bview->buffer()->params,
1567                                   selection.start.pos());
1568
1569         // Insert the new string
1570         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1571                 selection.end.par()->insertChar(pos, (*cit), font);
1572                 ++pos;
1573         }
1574
1575         // Cut the selection
1576         cutSelection(bview, true, false);
1577
1578         unFreezeUndo();
1579 }
1580
1581
1582 // needed to insert the selection
1583 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1584 {
1585         Paragraph * par = cursor.par();
1586         pos_type pos = cursor.pos();
1587         Paragraph * endpar = cursor.par()->next();
1588
1589         setCursorParUndo(bview);
1590
1591         // only to be sure, should not be neccessary
1592         clearSelection();
1593
1594         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1595
1596         redoParagraphs(bview, cursor, endpar);
1597         setCursor(bview, cursor.par(), cursor.pos());
1598         selection.cursor = cursor;
1599         setCursor(bview, par, pos);
1600         setSelection(bview);
1601 }
1602
1603
1604 // turns double-CR to single CR, others where converted into one
1605 // blank. Then InsertStringAsLines is called
1606 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1607 {
1608         string linestr(str);
1609         bool newline_inserted = false;
1610         for (string::size_type i = 0; i < linestr.length(); ++i) {
1611                 if (linestr[i] == '\n') {
1612                         if (newline_inserted) {
1613                                 // we know that \r will be ignored by
1614                                 // InsertStringA. Of course, it is a dirty
1615                                 // trick, but it works...
1616                                 linestr[i - 1] = '\r';
1617                                 linestr[i] = '\n';
1618                         } else {
1619                                 linestr[i] = ' ';
1620                                 newline_inserted = true;
1621                         }
1622                 } else if (IsPrintable(linestr[i])) {
1623                         newline_inserted = false;
1624                 }
1625         }
1626         insertStringAsLines(bview, linestr);
1627 }
1628
1629
1630 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1631                              pos_type pos)
1632 {
1633         LyXCursor tmpcursor;
1634
1635         int y = 0;
1636         pos_type z;
1637         Row * row = getRow(par, pos, y);
1638
1639         // is there a break one row above
1640         if (row->previous() && row->previous()->par() == row->par()) {
1641                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1642                 if (z >= row->pos()) {
1643                         // set the dimensions of the row above
1644                         y -= row->previous()->height();
1645                         refresh_y = y;
1646                         refresh_row = row->previous();
1647                         status(bview, LyXText::NEED_MORE_REFRESH);
1648
1649                         breakAgain(bview, row->previous());
1650
1651                         // set the cursor again. Otherwise
1652                         // dangling pointers are possible
1653                         setCursor(bview, cursor.par(), cursor.pos(),
1654                                   false, cursor.boundary());
1655                         selection.cursor = cursor;
1656                         return;
1657                 }
1658         }
1659
1660         int const tmpheight = row->height();
1661         pos_type const tmplast = row->lastPos();
1662         refresh_y = y;
1663         refresh_row = row;
1664
1665         breakAgain(bview, row);
1666         if (row->height() == tmpheight && row->lastPos() == tmplast)
1667                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1668         else
1669                 status(bview, LyXText::NEED_MORE_REFRESH);
1670
1671         // check the special right address boxes
1672         if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1673                 tmpcursor.par(par);
1674                 tmpcursor.row(row);
1675                 tmpcursor.y(y);
1676                 tmpcursor.x(0);
1677                 tmpcursor.x_fix(0);
1678                 tmpcursor.pos(pos);
1679                 redoDrawingOfParagraph(bview, tmpcursor);
1680         }
1681
1682         // set the cursor again. Otherwise dangling pointers are possible
1683         // also set the selection
1684
1685         if (selection.set()) {
1686                 tmpcursor = cursor;
1687                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1688                                 false, selection.cursor.boundary());
1689                 selection.cursor = cursor;
1690                 setCursorIntern(bview, selection.start.par(),
1691                                 selection.start.pos(),
1692                                 false, selection.start.boundary());
1693                 selection.start = cursor;
1694                 setCursorIntern(bview, selection.end.par(),
1695                                 selection.end.pos(),
1696                                 false, selection.end.boundary());
1697                 selection.end = cursor;
1698                 setCursorIntern(bview, last_sel_cursor.par(),
1699                                 last_sel_cursor.pos(),
1700                                 false, last_sel_cursor.boundary());
1701                 last_sel_cursor = cursor;
1702                 cursor = tmpcursor;
1703         }
1704         setCursorIntern(bview, cursor.par(), cursor.pos(),
1705                         false, cursor.boundary());
1706 }
1707
1708
1709 // returns false if inset wasn't found
1710 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1711 {
1712         // first check the current paragraph
1713         int pos = cursor.par()->getPositionOfInset(inset);
1714         if (pos != -1) {
1715                 checkParagraph(bview, cursor.par(), pos);
1716                 return true;
1717         }
1718
1719         // check every paragraph
1720
1721         Paragraph * par = ownerParagraph();
1722         do {
1723                 pos = par->getPositionOfInset(inset);
1724                 if (pos != -1) {
1725                         checkParagraph(bview, par, pos);
1726                         return true;
1727                 }
1728                 par = par->next();
1729         } while (par);
1730
1731         return false;
1732 }
1733
1734
1735 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1736                         pos_type pos,
1737                         bool setfont, bool boundary) const
1738 {
1739         LyXCursor old_cursor = cursor;
1740         setCursorIntern(bview, par, pos, setfont, boundary);
1741         return deleteEmptyParagraphMechanism(bview, old_cursor);
1742 }
1743
1744
1745 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1746                         pos_type pos, bool boundary) const
1747 {
1748         lyx::Assert(par);
1749         lyx::Assert(bview);
1750
1751         cur.par(par);
1752         cur.pos(pos);
1753         cur.boundary(boundary);
1754
1755         // get the cursor y position in text
1756         int y = 0;
1757         Row * row = getRow(par, pos, y);
1758         Row * old_row = row;
1759         cur.irow(row);
1760         // if we are before the first char of this row and are still in the
1761         // same paragraph and there is a previous row then put the cursor on
1762         // the end of the previous row
1763         cur.iy(y + row->baseline());
1764         Inset * ins;
1765         if (row->previous() && pos &&
1766                 row->previous()->par() == row->par() &&
1767                 par->getChar(pos) == Paragraph::META_INSET &&
1768                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1769         {
1770                 row = row->previous();
1771                 y -= row->height();
1772         }
1773
1774         cur.row(row);
1775         // y is now the beginning of the cursor row
1776         y += row->baseline();
1777         // y is now the cursor baseline
1778         cur.y(y);
1779
1780         pos_type last = old_row->lastPrintablePos();
1781
1782         // None of these should happen, but we're scaredy-cats
1783         if (pos > par->size()) {
1784                 pos = 0;
1785                 cur.pos(0);
1786         } else if (pos > last + 1) {
1787                 // This shouldn't happen.
1788                 pos = last + 1;
1789                 cur.pos(pos);
1790         } else if (pos < row->pos()) {
1791                 pos = row->pos();
1792                 cur.pos(pos);
1793         }
1794
1795         // now get the cursors x position
1796         float x = getCursorX(bview, row, pos, last, boundary);
1797         cur.x(int(x));
1798         cur.x_fix(cur.x());
1799         if (old_row != row) {
1800                 x = getCursorX(bview, old_row, pos, last, boundary);
1801                 cur.ix(int(x));
1802         } else
1803                 cur.ix(cur.x());
1804 }
1805
1806
1807 float LyXText::getCursorX(BufferView * bview, Row * row,
1808                                                   pos_type pos, pos_type last, bool boundary) const
1809 {
1810         pos_type cursor_vpos = 0;
1811         float x;
1812         float fill_separator;
1813         float fill_hfill;
1814         float fill_label_hfill;
1815         // This call HAS to be here because of the BidiTables!!!
1816         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1817                        fill_label_hfill);
1818
1819         if (last < row->pos())
1820                 cursor_vpos = row->pos();
1821         else if (pos > last && !boundary)
1822                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1823                         ? row->pos() : last + 1;
1824         else if (pos > row->pos() &&
1825                  (pos > last || boundary))
1826                 /// Place cursor after char at (logical) position pos - 1
1827                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1828                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1829         else
1830                 /// Place cursor before char at (logical) position pos
1831                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1832                         ? log2vis(pos) : log2vis(pos) + 1;
1833
1834         pos_type main_body = row->par()->beginningOfMainBody();
1835         if ((main_body > 0) &&
1836             ((main_body-1 > last) ||
1837              !row->par()->isLineSeparator(main_body-1)))
1838                 main_body = 0;
1839
1840         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1841                 pos_type pos = vis2log(vpos);
1842                 if (main_body > 0 && pos == main_body - 1) {
1843                         x += fill_label_hfill +
1844                                 font_metrics::width(
1845                                         row->par()->layout()->labelsep,
1846                                         getLabelFont(bview->buffer(),
1847                                                      row->par()));
1848                         if (row->par()->isLineSeparator(main_body - 1))
1849                                 x -= singleWidth(bview,
1850                                                  row->par(), main_body - 1);
1851                 }
1852                 if (row->hfillExpansion(pos)) {
1853                         x += singleWidth(bview, row->par(), pos);
1854                         if (pos >= main_body)
1855                                 x += fill_hfill;
1856                         else
1857                                 x += fill_label_hfill;
1858                 } else if (row->par()->isSeparator(pos)) {
1859                         x += singleWidth(bview, row->par(), pos);
1860                         if (pos >= main_body)
1861                                 x += fill_separator;
1862                 } else
1863                         x += singleWidth(bview, row->par(), pos);
1864         }
1865         return x;
1866 }
1867
1868
1869 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1870                               pos_type pos, bool setfont, bool boundary) const
1871 {
1872         InsetText * it = static_cast<InsetText *>(par->inInset());
1873         if (it) {
1874                 if (it != inset_owner) {
1875                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1876                                               << endl
1877                                               << "inset_owner is "
1878                                               << inset_owner << endl;
1879 #ifdef WITH_WARNINGS
1880 #warning I believe this code is wrong. (Lgb)
1881 #warning Jürgen, have a look at this. (Lgb)
1882 #warning Hmmm, I guess you are right but we
1883 #warning should verify when this is needed
1884 #endif
1885                         // Jürgen, would you like to have a look?
1886                         // I guess we need to move the outer cursor
1887                         // and open and lock the inset (bla bla bla)
1888                         // stuff I don't know... so can you have a look?
1889                         // (Lgb)
1890                         // I moved the lyxerr stuff in here so we can see if
1891                         // this is actually really needed and where!
1892                         // (Jug)
1893                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1894                         return;
1895                 }
1896         }
1897
1898         setCursor(bview, cursor, par, pos, boundary);
1899         if (setfont)
1900                 setCurrentFont(bview);
1901 }
1902
1903
1904 void LyXText::setCurrentFont(BufferView * bview) const
1905 {
1906         pos_type pos = cursor.pos();
1907         if (cursor.boundary() && pos > 0)
1908                 --pos;
1909
1910         if (pos > 0) {
1911                 if (pos == cursor.par()->size())
1912                         --pos;
1913                 else // potentional bug... BUG (Lgb)
1914                         if (cursor.par()->isSeparator(pos)) {
1915                                 if (pos > cursor.row()->pos() &&
1916                                     bidi_level(pos) % 2 ==
1917                                     bidi_level(pos - 1) % 2)
1918                                         --pos;
1919                                 else if (pos + 1 < cursor.par()->size())
1920                                         ++pos;
1921                         }
1922         }
1923
1924         current_font =
1925                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1926         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1927
1928         if (cursor.pos() == cursor.par()->size() &&
1929             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1930             !cursor.boundary()) {
1931                 Language const * lang =
1932                         cursor.par()->getParLanguage(bview->buffer()->params);
1933                 current_font.setLanguage(lang);
1934                 current_font.setNumber(LyXFont::OFF);
1935                 real_current_font.setLanguage(lang);
1936                 real_current_font.setNumber(LyXFont::OFF);
1937         }
1938 }
1939
1940
1941 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1942 {
1943         LyXCursor old_cursor = cursor;
1944
1945         setCursorFromCoordinates(bview, cursor, x, y);
1946         setCurrentFont(bview);
1947         deleteEmptyParagraphMechanism(bview, old_cursor);
1948 }
1949
1950
1951 namespace {
1952
1953         /**
1954          * return true if the cursor given is at the end of a row,
1955          * and the next row is filled by an inset that spans an entire
1956          * row.
1957          */
1958         bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1959                 if (!row.next())
1960                         return false;
1961                 Row const & next = *row.next();
1962
1963                 if (next.pos() != cur.pos() || next.par() != cur.par())
1964                         return false;
1965                 if (!cur.par()->isInset(cur.pos()))
1966                         return false;
1967                 Inset const * inset = cur.par()->getInset(cur.pos());
1968                 if (inset->needFullRow() || inset->display())
1969                         return true;
1970                 return false;
1971         }
1972 }
1973
1974
1975 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1976                                        int x, int y) const
1977 {
1978         // Get the row first.
1979
1980         Row * row = getRowNearY(y);
1981         bool bound = false;
1982         pos_type const column = getColumnNearX(bview, row, x, bound);
1983         cur.par(row->par());
1984         cur.pos(row->pos() + column);
1985         cur.x(x);
1986         cur.y(y + row->baseline());
1987         cur.row(row);
1988
1989         if (beforeFullRowInset(*row, cur)) {
1990                 pos_type last = row->lastPrintablePos();
1991                 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
1992                 cur.ix(int(x));
1993                 cur.iy(y + row->height() + row->next()->baseline());
1994                 cur.irow(row->next());
1995         } else {
1996                 cur.iy(cur.y());
1997                 cur.ix(cur.x());
1998                 cur.irow(row);
1999         }
2000         cur.boundary(bound);
2001 }
2002
2003
2004 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2005 {
2006         if (cursor.pos() > 0) {
2007                 bool boundary = cursor.boundary();
2008                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2009                 if (!internal && !boundary &&
2010                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2011                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2012         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2013                 Paragraph * par = cursor.par()->previous();
2014                 setCursor(bview, par, par->size());
2015         }
2016 }
2017
2018
2019 void LyXText::cursorRight(BufferView * bview, bool internal) const
2020 {
2021         if (!internal && cursor.boundary() &&
2022             !cursor.par()->isNewline(cursor.pos()))
2023                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2024         else if (cursor.pos() < cursor.par()->size()) {
2025                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2026                 if (!internal &&
2027                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2028                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2029         } else if (cursor.par()->next())
2030                 setCursor(bview, cursor.par()->next(), 0);
2031 }
2032
2033
2034 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2035 {
2036 #if 1
2037         int x = cursor.x_fix();
2038         int y = cursor.y() - cursor.row()->baseline() - 1;
2039         setCursorFromCoordinates(bview, x, y);
2040         if (!selecting) {
2041                 int y1 = cursor.iy() - first_y;
2042                 int y2 = y1;
2043                 y -= first_y;
2044                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2045                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2046                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2047                 }
2048         }
2049 #else
2050         setCursorFromCoordinates(bview, cursor.x_fix(),
2051                                  cursor.y() - cursor.row()->baseline() - 1);
2052 #endif
2053 }
2054
2055
2056 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2057 {
2058 #if 1
2059         int x = cursor.x_fix();
2060         int y = cursor.y() - cursor.row()->baseline() +
2061                 cursor.row()->height() + 1;
2062         setCursorFromCoordinates(bview, x, y);
2063         if (!selecting && cursor.row() == cursor.irow()) {
2064                 int y1 = cursor.iy() - first_y;
2065                 int y2 = y1;
2066                 y -= first_y;
2067                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2068                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2069                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2070                 }
2071         }
2072 #else
2073         setCursorFromCoordinates(bview, cursor.x_fix(),
2074                                  cursor.y() - cursor.row()->baseline()
2075                                  + cursor.row()->height() + 1);
2076 #endif
2077 }
2078
2079
2080 void LyXText::cursorUpParagraph(BufferView * bview) const
2081 {
2082         if (cursor.pos() > 0) {
2083                 setCursor(bview, cursor.par(), 0);
2084         }
2085         else if (cursor.par()->previous()) {
2086                 setCursor(bview, cursor.par()->previous(), 0);
2087         }
2088 }
2089
2090
2091 void LyXText::cursorDownParagraph(BufferView * bview) const
2092 {
2093         if (cursor.par()->next()) {
2094                 setCursor(bview, cursor.par()->next(), 0);
2095         } else {
2096                 setCursor(bview, cursor.par(), cursor.par()->size());
2097         }
2098 }
2099
2100 // fix the cursor `cur' after a characters has been deleted at `where'
2101 // position. Called by deleteEmptyParagraphMechanism
2102 void LyXText::fixCursorAfterDelete(BufferView * bview,
2103                                    LyXCursor & cur,
2104                                    LyXCursor const & where) const
2105 {
2106         // if cursor is not in the paragraph where the delete occured,
2107         // do nothing
2108         if (cur.par() != where.par())
2109                 return;
2110
2111         // if cursor position is after the place where the delete occured,
2112         // update it
2113         if (cur.pos() > where.pos())
2114                 cur.pos(cur.pos()-1);
2115
2116         // check also if we don't want to set the cursor on a spot behind the
2117         // pagragraph because we erased the last character.
2118         if (cur.pos() > cur.par()->size())
2119                 cur.pos(cur.par()->size());
2120
2121         // recompute row et al. for this cursor
2122         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2123 }
2124
2125
2126 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2127                                             LyXCursor const & old_cursor) const
2128 {
2129         // Would be wrong to delete anything if we have a selection.
2130         if (selection.set())
2131                 return false;
2132
2133         // We allow all kinds of "mumbo-jumbo" when freespacing.
2134         if (old_cursor.par()->layout()->free_spacing
2135             || old_cursor.par()->isFreeSpacing()) {
2136                 return false;
2137         }
2138
2139         /* Ok I'll put some comments here about what is missing.
2140            I have fixed BackSpace (and thus Delete) to not delete
2141            double-spaces automagically. I have also changed Cut,
2142            Copy and Paste to hopefully do some sensible things.
2143            There are still some small problems that can lead to
2144            double spaces stored in the document file or space at
2145            the beginning of paragraphs. This happens if you have
2146            the cursor betwenn to spaces and then save. Or if you
2147            cut and paste and the selection have a space at the
2148            beginning and then save right after the paste. I am
2149            sure none of these are very hard to fix, but I will
2150            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2151            that I can get some feedback. (Lgb)
2152         */
2153
2154         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2155         // delete the LineSeparator.
2156         // MISSING
2157
2158         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2159         // delete the LineSeparator.
2160         // MISSING
2161
2162         // If the pos around the old_cursor were spaces, delete one of them.
2163         if (old_cursor.par() != cursor.par()
2164             || old_cursor.pos() != cursor.pos()) {
2165                 // Only if the cursor has really moved
2166
2167                 if (old_cursor.pos() > 0
2168                     && old_cursor.pos() < old_cursor.par()->size()
2169                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2170                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2171                         old_cursor.par()->erase(old_cursor.pos() - 1);
2172                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2173
2174 #ifdef WITH_WARNINGS
2175 #warning This will not work anymore when we have multiple views of the same buffer
2176 // In this case, we will have to correct also the cursors held by
2177 // other bufferviews. It will probably be easier to do that in a more
2178 // automated way in LyXCursor code. (JMarc 26/09/2001)
2179 #endif
2180                         // correct all cursors held by the LyXText
2181                         fixCursorAfterDelete(bview, cursor, old_cursor);
2182                         fixCursorAfterDelete(bview, selection.cursor,
2183                                              old_cursor);
2184                         fixCursorAfterDelete(bview, selection.start,
2185                                              old_cursor);
2186                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2187                         fixCursorAfterDelete(bview, last_sel_cursor,
2188                                              old_cursor);
2189                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2190                         fixCursorAfterDelete(bview, toggle_end_cursor,
2191                                              old_cursor);
2192                         return false;
2193                 }
2194         }
2195
2196         // don't delete anything if this is the ONLY paragraph!
2197         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2198                 return false;
2199
2200         // Do not delete empty paragraphs with keepempty set.
2201         if (old_cursor.par()->layout()->keepempty)
2202                 return false;
2203
2204         // only do our magic if we changed paragraph
2205         if (old_cursor.par() == cursor.par())
2206                 return false;
2207
2208         // record if we have deleted a paragraph
2209         // we can't possibly have deleted a paragraph before this point
2210         bool deleted = false;
2211
2212         if ((old_cursor.par()->empty()
2213              || (old_cursor.par()->size() == 1
2214                  && old_cursor.par()->isLineSeparator(0)))) {
2215                 // ok, we will delete anything
2216                 LyXCursor tmpcursor;
2217
2218                 // make sure that you do not delete any environments
2219                 status(bview, LyXText::NEED_MORE_REFRESH);
2220                 deleted = true;
2221
2222                 if (old_cursor.row()->previous()) {
2223                         refresh_row = old_cursor.row()->previous();
2224                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2225                         tmpcursor = cursor;
2226                         cursor = old_cursor; // that undo can restore the right cursor position
2227                         Paragraph * endpar = old_cursor.par()->next();
2228                         if (endpar && endpar->getDepth()) {
2229                                 while (endpar && endpar->getDepth()) {
2230                                         endpar = endpar->next();
2231                                 }
2232                         }
2233                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2234                         cursor = tmpcursor;
2235
2236                         // delete old row
2237                         removeRow(old_cursor.row());
2238                         if (ownerParagraph() == old_cursor.par()) {
2239                                 ownerParagraph(ownerParagraph()->next());
2240                         }
2241                         // delete old par
2242                         delete old_cursor.par();
2243
2244                         /* Breakagain the next par. Needed because of
2245                          * the parindent that can occur or dissappear.
2246                          * The next row can change its height, if
2247                          * there is another layout before */
2248                         if (refresh_row->next()) {
2249                                 breakAgain(bview, refresh_row->next());
2250                                 updateCounters(bview);
2251                         }
2252                         setHeightOfRow(bview, refresh_row);
2253                 } else {
2254                         refresh_row = old_cursor.row()->next();
2255                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2256
2257                         tmpcursor = cursor;
2258                         cursor = old_cursor; // that undo can restore the right cursor position
2259                         Paragraph * endpar = old_cursor.par()->next();
2260                         if (endpar && endpar->getDepth()) {
2261                                 while (endpar && endpar->getDepth()) {
2262                                         endpar = endpar->next();
2263                                 }
2264                         }
2265                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2266                         cursor = tmpcursor;
2267
2268                         // delete old row
2269                         removeRow(old_cursor.row());
2270                         // delete old par
2271                         if (ownerParagraph() == old_cursor.par()) {
2272                                 ownerParagraph(ownerParagraph()->next());
2273                         }
2274
2275                         delete old_cursor.par();
2276
2277                         /* Breakagain the next par. Needed because of
2278                            the parindent that can occur or dissappear.
2279                            The next row can change its height, if
2280                            there is another layout before */
2281                         if (refresh_row) {
2282                                 breakAgain(bview, refresh_row);
2283                                 updateCounters(bview);
2284                         }
2285                 }
2286
2287                 // correct cursor y
2288                 setCursorIntern(bview, cursor.par(), cursor.pos());
2289
2290                 if (selection.cursor.par()  == old_cursor.par()
2291                     && selection.cursor.pos() == old_cursor.pos()) {
2292                         // correct selection
2293                         selection.cursor = cursor;
2294                 }
2295         }
2296         if (!deleted) {
2297                 if (old_cursor.par()->stripLeadingSpaces()) {
2298                         redoParagraphs(bview, old_cursor,
2299                                        old_cursor.par()->next());
2300                         // correct cursor y
2301                         setCursorIntern(bview, cursor.par(), cursor.pos());
2302                         selection.cursor = cursor;
2303                 }
2304         }
2305         return deleted;
2306 }
2307
2308
2309 Paragraph * LyXText::ownerParagraph() const
2310 {
2311         if (inset_owner) {
2312                 return inset_owner->paragraph();
2313         }
2314         return &*(bv_owner->buffer()->paragraphs.begin());
2315 }
2316
2317
2318 void LyXText::ownerParagraph(Paragraph * p) const
2319 {
2320         if (inset_owner) {
2321                 inset_owner->paragraph(p);
2322         } else {
2323                 bv_owner->buffer()->paragraphs.set(p);
2324         }
2325 }
2326
2327
2328 void LyXText::ownerParagraph(int id, Paragraph * p) const
2329 {
2330         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2331         if (op && op->inInset()) {
2332                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2333         } else {
2334                 ownerParagraph(p);
2335         }
2336 }
2337
2338
2339 LyXText::text_status LyXText::status() const
2340 {
2341         return status_;
2342 }
2343
2344
2345 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2346 {
2347         LyXText * t = bview->text;
2348
2349         // We should only go up with refreshing code so this means that if
2350         // we have a MORE refresh we should never set it to LITTLE if we still
2351         // didn't handle it (and then it will be UNCHANGED. Now as long as
2352         // we stay inside one LyXText this may work but we need to tell the
2353         // outermost LyXText that it should REALLY draw us if there is some
2354         // change in a Inset::LyXText. So you see that when we are inside a
2355         // inset's LyXText we give the LITTLE to the outermost LyXText to
2356         // tell'em that it should redraw the actual row (where the inset
2357         // resides! Capito?!
2358
2359         if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2360                 status_ = st;
2361                 if (inset_owner && st != UNCHANGED) {
2362                         t->status(bview, NEED_VERY_LITTLE_REFRESH);
2363                         if (!t->refresh_row) {
2364                                 t->refresh_row = t->cursor.row();
2365                                 t->refresh_y = t->cursor.y() -
2366                                         t->cursor.row()->baseline();
2367                         }
2368                 }
2369         }
2370 }
2371
2372
2373 bool LyXText::isTopLevel() const
2374 {
2375         /// only the top-level lyxtext has a non-null bv owner
2376         return bv_owner;
2377 }
2378
2379
2380 int defaultRowHeight()
2381 {
2382         LyXFont const font(LyXFont::ALL_SANE);
2383         return int(font_metrics::maxAscent(font)
2384                  + font_metrics::maxDescent(font) * 1.5);
2385 }