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