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