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