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