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