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