]> git.lyx.org Git - lyx.git/blob - src/text2.C
the string -> char patch
[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. Paragraphs with changed label string will be rebroken
1366 void LyXText::updateCounters(BufferView * bview) const
1367 {
1368         Paragraph * par;
1369
1370         Row * row = firstrow;
1371         par = row->par();
1372
1373         // CHECK if this is really needed. (Lgb)
1374         bview->buffer()->params.getLyXTextClass().counters().reset();
1375
1376         while (par) {
1377                 while (row->par() != par)
1378                         row = row->next();
1379
1380                 string const oldLabel = par->params().labelString();
1381
1382                 setCounter(bview->buffer(), par);
1383
1384                 string const & newLabel = par->params().labelString();
1385
1386                 if (oldLabel.empty() && !newLabel.empty()) {
1387                         removeParagraph(row);
1388                         appendParagraph(bview, row);
1389                 }
1390
1391                 par = par->next();
1392         }
1393 }
1394
1395
1396 void LyXText::insertInset(BufferView * bview, Inset * inset)
1397 {
1398         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1399                 return;
1400         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1401         freezeUndo();
1402         cursor.par()->insertInset(cursor.pos(), inset);
1403         // Just to rebreak and refresh correctly.
1404         // The character will not be inserted a second time
1405         insertChar(bview, Paragraph::META_INSET);
1406         // If we enter a highly editable inset the cursor should be to before
1407         // the inset. This couldn't happen before as Undo was not handled inside
1408         // inset now after the Undo LyX tries to call inset->Edit(...) again
1409         // and cannot do this as the cursor is behind the inset and GetInset
1410         // does not return the inset!
1411         if (isHighlyEditableInset(inset)) {
1412                 cursorLeft(bview, true);
1413         }
1414         unFreezeUndo();
1415 }
1416
1417
1418 void LyXText::copyEnvironmentType()
1419 {
1420         copylayouttype = cursor.par()->layout()->name();
1421 }
1422
1423
1424 void LyXText::pasteEnvironmentType(BufferView * bview)
1425 {
1426         setLayout(bview, copylayouttype);
1427 }
1428
1429
1430 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1431 {
1432         // Stuff what we got on the clipboard. Even if there is no selection.
1433
1434         // There is a problem with having the stuffing here in that the
1435         // larger the selection the slower LyX will get. This can be
1436         // solved by running the line below only when the selection has
1437         // finished. The solution used currently just works, to make it
1438         // faster we need to be more clever and probably also have more
1439         // calls to stuffClipboard. (Lgb)
1440         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1441
1442         // This doesn't make sense, if there is no selection
1443         if (!selection.set())
1444                 return;
1445
1446         // OK, we have a selection. This is always between selection.start
1447         // and selection.end
1448
1449         // make sure that the depth behind the selection are restored, too
1450         Paragraph * endpar = selection.end.par()->next();
1451         Paragraph * undoendpar = endpar;
1452
1453         if (endpar && endpar->getDepth()) {
1454                 while (endpar && endpar->getDepth()) {
1455                         endpar = endpar->next();
1456                         undoendpar = endpar;
1457                 }
1458         } else if (endpar) {
1459                 endpar = endpar->next(); // because of parindents etc.
1460         }
1461
1462         setUndo(bview, Undo::DELETE,
1463                 selection.start.par(), undoendpar);
1464
1465         // there are two cases: cut only within one paragraph or
1466         // more than one paragraph
1467         if (selection.start.par() == selection.end.par()) {
1468                 // only within one paragraph
1469                 endpar = selection.end.par();
1470                 int pos = selection.end.pos();
1471                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1472                                           selection.start.pos(), pos,
1473                                           bview->buffer()->params.textclass,
1474                                           doclear, realcut);
1475                 selection.end.pos(pos);
1476         } else {
1477                 endpar = selection.end.par();
1478                 int pos = selection.end.pos();
1479                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1480                                           selection.start.pos(), pos,
1481                                           bview->buffer()->params.textclass,
1482                                           doclear, realcut);
1483                 cursor.par(endpar);
1484                 selection.end.par(endpar);
1485                 selection.end.pos(pos);
1486                 cursor.pos(selection.end.pos());
1487         }
1488         endpar = endpar->next();
1489
1490         // sometimes necessary
1491         if (doclear)
1492                 selection.start.par()->stripLeadingSpaces();
1493
1494         redoParagraphs(bview, selection.start, endpar);
1495
1496         // cutSelection can invalidate the cursor so we need to set
1497         // it anew. (Lgb)
1498         cursor = selection.start;
1499
1500         // need a valid cursor. (Lgb)
1501         clearSelection();
1502
1503         setCursor(bview, cursor.par(), cursor.pos());
1504         selection.cursor = cursor;
1505         updateCounters(bview);
1506 }
1507
1508
1509 void LyXText::copySelection(BufferView * bview)
1510 {
1511         // stuff the selection onto the X clipboard, from an explicit copy request
1512         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1513
1514         // this doesnt make sense, if there is no selection
1515         if (!selection.set())
1516                 return;
1517
1518         // ok we have a selection. This is always between selection.start
1519         // and sel_end cursor
1520
1521         // copy behind a space if there is one
1522         while (selection.start.par()->size() > selection.start.pos()
1523                && selection.start.par()->isLineSeparator(selection.start.pos())
1524                && (selection.start.par() != selection.end.par()
1525                    || selection.start.pos() < selection.end.pos()))
1526                 selection.start.pos(selection.start.pos() + 1);
1527
1528         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1529                                    selection.start.pos(), selection.end.pos(),
1530                                    bview->buffer()->params.textclass);
1531 }
1532
1533
1534 void LyXText::pasteSelection(BufferView * bview)
1535 {
1536         // this does not make sense, if there is nothing to paste
1537         if (!CutAndPaste::checkPastePossible(cursor.par()))
1538                 return;
1539
1540         setUndo(bview, Undo::INSERT,
1541                 cursor.par(), cursor.par()->next());
1542
1543         Paragraph * endpar;
1544         Paragraph * actpar = cursor.par();
1545         int pos = cursor.pos();
1546
1547         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1548                                     bview->buffer()->params.textclass);
1549
1550         redoParagraphs(bview, cursor, endpar);
1551
1552         setCursor(bview, cursor.par(), cursor.pos());
1553         clearSelection();
1554
1555         selection.cursor = cursor;
1556         setCursor(bview, actpar, pos);
1557         setSelection(bview);
1558         updateCounters(bview);
1559 }
1560
1561
1562 // sets the selection over the number of characters of string, no check!!
1563 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1564 {
1565         if (str.empty())
1566                 return;
1567
1568         selection.cursor = cursor;
1569         for (string::size_type i = 0; i < str.length(); ++i)
1570                 cursorRight(bview);
1571         setSelection(bview);
1572 }
1573
1574
1575 // simple replacing. The font of the first selected character is used
1576 void LyXText::replaceSelectionWithString(BufferView * bview,
1577                                          string const & str)
1578 {
1579         setCursorParUndo(bview);
1580         freezeUndo();
1581
1582         if (!selection.set()) { // create a dummy selection
1583                 selection.end = cursor;
1584                 selection.start = cursor;
1585         }
1586
1587         // Get font setting before we cut
1588         pos_type pos = selection.end.pos();
1589         LyXFont const font = selection.start.par()
1590                 ->getFontSettings(bview->buffer()->params,
1591                                   selection.start.pos());
1592
1593         // Insert the new string
1594         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1595                 selection.end.par()->insertChar(pos, (*cit), font);
1596                 ++pos;
1597         }
1598
1599         // Cut the selection
1600         cutSelection(bview, true, false);
1601
1602         unFreezeUndo();
1603 }
1604
1605
1606 // needed to insert the selection
1607 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1608 {
1609         Paragraph * par = cursor.par();
1610         pos_type pos = cursor.pos();
1611         Paragraph * endpar = cursor.par()->next();
1612
1613         setCursorParUndo(bview);
1614
1615         // only to be sure, should not be neccessary
1616         clearSelection();
1617
1618         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1619
1620         redoParagraphs(bview, cursor, endpar);
1621         setCursor(bview, cursor.par(), cursor.pos());
1622         selection.cursor = cursor;
1623         setCursor(bview, par, pos);
1624         setSelection(bview);
1625 }
1626
1627
1628 // turns double-CR to single CR, others where converted into one
1629 // blank. Then InsertStringAsLines is called
1630 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1631 {
1632         string linestr(str);
1633         bool newline_inserted = false;
1634         for (string::size_type i = 0; i < linestr.length(); ++i) {
1635                 if (linestr[i] == '\n') {
1636                         if (newline_inserted) {
1637                                 // we know that \r will be ignored by
1638                                 // InsertStringA. Of course, it is a dirty
1639                                 // trick, but it works...
1640                                 linestr[i - 1] = '\r';
1641                                 linestr[i] = '\n';
1642                         } else {
1643                                 linestr[i] = ' ';
1644                                 newline_inserted = true;
1645                         }
1646                 } else if (IsPrintable(linestr[i])) {
1647                         newline_inserted = false;
1648                 }
1649         }
1650         insertStringAsLines(bview, linestr);
1651 }
1652
1653
1654 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1655                              pos_type pos)
1656 {
1657         LyXCursor tmpcursor;
1658
1659         int y = 0;
1660         pos_type z;
1661         Row * row = getRow(par, pos, y);
1662
1663         // is there a break one row above
1664         if (row->previous() && row->previous()->par() == row->par()) {
1665                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1666                 if (z >= row->pos()) {
1667                         // set the dimensions of the row above
1668                         y -= row->previous()->height();
1669                         refresh_y = y;
1670                         refresh_row = row->previous();
1671                         status(bview, LyXText::NEED_MORE_REFRESH);
1672
1673                         breakAgain(bview, row->previous());
1674
1675                         // set the cursor again. Otherwise
1676                         // dangling pointers are possible
1677                         setCursor(bview, cursor.par(), cursor.pos(),
1678                                   false, cursor.boundary());
1679                         selection.cursor = cursor;
1680                         return;
1681                 }
1682         }
1683
1684         int const tmpheight = row->height();
1685         pos_type const tmplast = rowLast(row);
1686         refresh_y = y;
1687         refresh_row = row;
1688
1689         breakAgain(bview, row);
1690         if (row->height() == tmpheight && rowLast(row) == tmplast)
1691                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1692         else
1693                 status(bview, LyXText::NEED_MORE_REFRESH);
1694
1695         // check the special right address boxes
1696         if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1697                 tmpcursor.par(par);
1698                 tmpcursor.row(row);
1699                 tmpcursor.y(y);
1700                 tmpcursor.x(0);
1701                 tmpcursor.x_fix(0);
1702                 tmpcursor.pos(pos);
1703                 redoDrawingOfParagraph(bview, tmpcursor);
1704         }
1705
1706         // set the cursor again. Otherwise dangling pointers are possible
1707         // also set the selection
1708
1709         if (selection.set()) {
1710                 tmpcursor = cursor;
1711                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1712                                 false, selection.cursor.boundary());
1713                 selection.cursor = cursor;
1714                 setCursorIntern(bview, selection.start.par(),
1715                                 selection.start.pos(),
1716                                 false, selection.start.boundary());
1717                 selection.start = cursor;
1718                 setCursorIntern(bview, selection.end.par(),
1719                                 selection.end.pos(),
1720                                 false, selection.end.boundary());
1721                 selection.end = cursor;
1722                 setCursorIntern(bview, last_sel_cursor.par(),
1723                                 last_sel_cursor.pos(),
1724                                 false, last_sel_cursor.boundary());
1725                 last_sel_cursor = cursor;
1726                 cursor = tmpcursor;
1727         }
1728         setCursorIntern(bview, cursor.par(), cursor.pos(),
1729                         false, cursor.boundary());
1730 }
1731
1732
1733 // returns false if inset wasn't found
1734 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1735 {
1736         // first check the current paragraph
1737         int pos = cursor.par()->getPositionOfInset(inset);
1738         if (pos != -1) {
1739                 checkParagraph(bview, cursor.par(), pos);
1740                 return true;
1741         }
1742
1743         // check every paragraph
1744
1745         Paragraph * par = ownerParagraph();
1746         do {
1747                 pos = par->getPositionOfInset(inset);
1748                 if (pos != -1) {
1749                         checkParagraph(bview, par, pos);
1750                         return true;
1751                 }
1752                 par = par->next();
1753         } while (par);
1754
1755         return false;
1756 }
1757
1758
1759 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1760                         pos_type pos,
1761                         bool setfont, bool boundary) const
1762 {
1763         LyXCursor old_cursor = cursor;
1764         setCursorIntern(bview, par, pos, setfont, boundary);
1765         return deleteEmptyParagraphMechanism(bview, old_cursor);
1766 }
1767
1768
1769 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1770                         pos_type pos, bool boundary) const
1771 {
1772         lyx::Assert(par);
1773         lyx::Assert(bview);
1774
1775         cur.par(par);
1776         cur.pos(pos);
1777         cur.boundary(boundary);
1778
1779         // get the cursor y position in text
1780         int y = 0;
1781         Row * row = getRow(par, pos, y);
1782         Row * old_row = row;
1783         cur.irow(row);
1784         // if we are before the first char of this row and are still in the
1785         // same paragraph and there is a previous row then put the cursor on
1786         // the end of the previous row
1787         cur.iy(y + row->baseline());
1788         Inset * ins;
1789         if (row->previous() && pos &&
1790                 row->previous()->par() == row->par() &&
1791                 par->getChar(pos) == Paragraph::META_INSET &&
1792                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1793         {
1794                 row = row->previous();
1795                 y -= row->height();
1796         }
1797
1798         cur.row(row);
1799         // y is now the beginning of the cursor row
1800         y += row->baseline();
1801         // y is now the cursor baseline
1802         cur.y(y);
1803
1804         pos_type last = rowLastPrintable(old_row);
1805
1806         if (pos > last + 1) {
1807                 // This shouldn't happen.
1808                 pos = last + 1;
1809                 cur.pos(pos);
1810         } else if (pos < row->pos()) {
1811                 pos = row->pos();
1812                 cur.pos(pos);
1813         }
1814
1815         // now get the cursors x position
1816         float x = getCursorX(bview, row, pos, last, boundary);
1817         cur.x(int(x));
1818         cur.x_fix(cur.x());
1819         if (old_row != row) {
1820                 x = getCursorX(bview, old_row, pos, last, boundary);
1821                 cur.ix(int(x));
1822         } else
1823                 cur.ix(cur.x());
1824 }
1825
1826
1827 float LyXText::getCursorX(BufferView * bview, Row * row,
1828                                                   pos_type pos, pos_type last, bool boundary) const
1829 {
1830         pos_type cursor_vpos = 0;
1831         float x;
1832         float fill_separator;
1833         float fill_hfill;
1834         float fill_label_hfill;
1835         // This call HAS to be here because of the BidiTables!!!
1836         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1837                        fill_label_hfill);
1838
1839         if (last < row->pos())
1840                 cursor_vpos = row->pos();
1841         else if (pos > last && !boundary)
1842                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1843                         ? row->pos() : last + 1;
1844         else if (pos > row->pos() &&
1845                  (pos > last || boundary))
1846                 /// Place cursor after char at (logical) position pos - 1
1847                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1848                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1849         else
1850                 /// Place cursor before char at (logical) position pos
1851                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1852                         ? log2vis(pos) : log2vis(pos) + 1;
1853
1854         pos_type main_body =
1855                 beginningOfMainBody(bview->buffer(), row->par());
1856         if ((main_body > 0) &&
1857             ((main_body-1 > last) ||
1858              !row->par()->isLineSeparator(main_body-1)))
1859                 main_body = 0;
1860
1861         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1862                 pos_type pos = vis2log(vpos);
1863                 if (main_body > 0 && pos == main_body - 1) {
1864                         x += fill_label_hfill +
1865                                 font_metrics::width(
1866                                         row->par()->layout()->labelsep,
1867                                         getLabelFont(bview->buffer(),
1868                                                      row->par()));
1869                         if (row->par()->isLineSeparator(main_body - 1))
1870                                 x -= singleWidth(bview,
1871                                                  row->par(), main_body - 1);
1872                 }
1873                 if (hfillExpansion(bview->buffer(), row, pos)) {
1874                         x += singleWidth(bview, row->par(), pos);
1875                         if (pos >= main_body)
1876                                 x += fill_hfill;
1877                         else
1878                                 x += fill_label_hfill;
1879                 } else if (row->par()->isSeparator(pos)) {
1880                         x += singleWidth(bview, row->par(), pos);
1881                         if (pos >= main_body)
1882                                 x += fill_separator;
1883                 } else
1884                         x += singleWidth(bview, row->par(), pos);
1885         }
1886         return x;
1887 }
1888
1889
1890 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1891                               pos_type pos, bool setfont, bool boundary) const
1892 {
1893         InsetText * it = static_cast<InsetText *>(par->inInset());
1894         if (it) {
1895                 if (it != inset_owner) {
1896                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1897                                               << endl
1898                                               << "inset_owner is "
1899                                               << inset_owner << endl;
1900 #ifdef WITH_WARNINGS
1901 #warning I believe this code is wrong. (Lgb)
1902 #warning Jürgen, have a look at this. (Lgb)
1903 #warning Hmmm, I guess you are right but we
1904 #warning should verify when this is needed
1905 #endif
1906                         // Jürgen, would you like to have a look?
1907                         // I guess we need to move the outer cursor
1908                         // and open and lock the inset (bla bla bla)
1909                         // stuff I don't know... so can you have a look?
1910                         // (Lgb)
1911                         // I moved the lyxerr stuff in here so we can see if
1912                         // this is actually really needed and where!
1913                         // (Jug)
1914                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1915                         return;
1916                 }
1917         }
1918
1919         setCursor(bview, cursor, par, pos, boundary);
1920         if (setfont)
1921                 setCurrentFont(bview);
1922 }
1923
1924
1925 void LyXText::setCurrentFont(BufferView * bview) const
1926 {
1927         pos_type pos = cursor.pos();
1928         if (cursor.boundary() && pos > 0)
1929                 --pos;
1930
1931         if (pos > 0) {
1932                 if (pos == cursor.par()->size())
1933                         --pos;
1934                 else // potentional bug... BUG (Lgb)
1935                         if (cursor.par()->isSeparator(pos)) {
1936                                 if (pos > cursor.row()->pos() &&
1937                                     bidi_level(pos) % 2 ==
1938                                     bidi_level(pos - 1) % 2)
1939                                         --pos;
1940                                 else if (pos + 1 < cursor.par()->size())
1941                                         ++pos;
1942                         }
1943         }
1944
1945         current_font =
1946                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1947         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1948
1949         if (cursor.pos() == cursor.par()->size() &&
1950             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1951             !cursor.boundary()) {
1952                 Language const * lang =
1953                         cursor.par()->getParLanguage(bview->buffer()->params);
1954                 current_font.setLanguage(lang);
1955                 current_font.setNumber(LyXFont::OFF);
1956                 real_current_font.setLanguage(lang);
1957                 real_current_font.setNumber(LyXFont::OFF);
1958         }
1959 }
1960
1961
1962 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1963 {
1964         LyXCursor old_cursor = cursor;
1965
1966         setCursorFromCoordinates(bview, cursor, x, y);
1967         setCurrentFont(bview);
1968         deleteEmptyParagraphMechanism(bview, old_cursor);
1969 }
1970
1971
1972 namespace {
1973
1974         /**
1975          * return true if the cursor given is at the end of a row,
1976          * and the next row is filled by an inset that spans an entire
1977          * row.
1978          */
1979         bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1980                 if (!row.next())
1981                         return false;
1982                 Row const & next = *row.next();
1983
1984                 if (next.pos() != cur.pos() || next.par() != cur.par())
1985                         return false;
1986                 if (!cur.par()->isInset(cur.pos()))
1987                         return false;
1988                 Inset const * inset = cur.par()->getInset(cur.pos());
1989                 if (inset->needFullRow() || inset->display())
1990                         return true;
1991                 return false;
1992         }
1993 }
1994
1995
1996 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
1997                                        int x, int y) const
1998 {
1999         // Get the row first.
2000
2001         Row * row = getRowNearY(y);
2002         bool bound = false;
2003         pos_type const column = getColumnNearX(bview, row, x, bound);
2004         cur.par(row->par());
2005         cur.pos(row->pos() + column);
2006         cur.x(x);
2007         cur.y(y + row->baseline());
2008         cur.row(row);
2009
2010         if (beforeFullRowInset(*row, cur)) {
2011                 pos_type last = rowLastPrintable(row);
2012                 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2013                 cur.ix(int(x));
2014                 cur.iy(y + row->height() + row->next()->baseline());
2015                 cur.irow(row->next());
2016         } else {
2017                 cur.iy(cur.y());
2018                 cur.ix(cur.x());
2019                 cur.irow(row);
2020         }
2021         cur.boundary(bound);
2022 }
2023
2024
2025 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2026 {
2027         if (cursor.pos() > 0) {
2028                 bool boundary = cursor.boundary();
2029                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2030                 if (!internal && !boundary &&
2031                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2032                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2033         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2034                 Paragraph * par = cursor.par()->previous();
2035                 setCursor(bview, par, par->size());
2036         }
2037 }
2038
2039
2040 void LyXText::cursorRight(BufferView * bview, bool internal) const
2041 {
2042         if (!internal && cursor.boundary() &&
2043             !cursor.par()->isNewline(cursor.pos()))
2044                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2045         else if (cursor.pos() < cursor.par()->size()) {
2046                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2047                 if (!internal &&
2048                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2049                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2050         } else if (cursor.par()->next())
2051                 setCursor(bview, cursor.par()->next(), 0);
2052 }
2053
2054
2055 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2056 {
2057 #if 1
2058         int x = cursor.x_fix();
2059         int y = cursor.y() - cursor.row()->baseline() - 1;
2060         setCursorFromCoordinates(bview, x, y);
2061         if (!selecting) {
2062                 int y1 = cursor.iy() - first_y;
2063                 int y2 = y1;
2064                 y -= first_y;
2065                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2066                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2067                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2068                 }
2069         }
2070 #else
2071         setCursorFromCoordinates(bview, cursor.x_fix(),
2072                                  cursor.y() - cursor.row()->baseline() - 1);
2073 #endif
2074 }
2075
2076
2077 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2078 {
2079 #if 1
2080         int x = cursor.x_fix();
2081         int y = cursor.y() - cursor.row()->baseline() +
2082                 cursor.row()->height() + 1;
2083         setCursorFromCoordinates(bview, x, y);
2084         if (!selecting && cursor.row() == cursor.irow()) {
2085                 int y1 = cursor.iy() - first_y;
2086                 int y2 = y1;
2087                 y -= first_y;
2088                 Inset * inset_hit = checkInsetHit(bview, x, y1);
2089                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2090                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2091                 }
2092         }
2093 #else
2094         setCursorFromCoordinates(bview, cursor.x_fix(),
2095                                  cursor.y() - cursor.row()->baseline()
2096                                  + cursor.row()->height() + 1);
2097 #endif
2098 }
2099
2100
2101 void LyXText::cursorUpParagraph(BufferView * bview) const
2102 {
2103         if (cursor.pos() > 0) {
2104                 setCursor(bview, cursor.par(), 0);
2105         }
2106         else if (cursor.par()->previous()) {
2107                 setCursor(bview, cursor.par()->previous(), 0);
2108         }
2109 }
2110
2111
2112 void LyXText::cursorDownParagraph(BufferView * bview) const
2113 {
2114         if (cursor.par()->next()) {
2115                 setCursor(bview, cursor.par()->next(), 0);
2116         } else {
2117                 setCursor(bview, cursor.par(), cursor.par()->size());
2118         }
2119 }
2120
2121 // fix the cursor `cur' after a characters has been deleted at `where'
2122 // position. Called by deleteEmptyParagraphMechanism
2123 void LyXText::fixCursorAfterDelete(BufferView * bview,
2124                                    LyXCursor & cur,
2125                                    LyXCursor const & where) const
2126 {
2127         // if cursor is not in the paragraph where the delete occured,
2128         // do nothing
2129         if (cur.par() != where.par())
2130                 return;
2131
2132         // if cursor position is after the place where the delete occured,
2133         // update it
2134         if (cur.pos() > where.pos())
2135                 cur.pos(cur.pos()-1);
2136
2137         // check also if we don't want to set the cursor on a spot behind the
2138         // pagragraph because we erased the last character.
2139         if (cur.pos() > cur.par()->size())
2140                 cur.pos(cur.par()->size());
2141
2142         // recompute row et al. for this cursor
2143         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2144 }
2145
2146
2147 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2148                                             LyXCursor const & old_cursor) const
2149 {
2150         // Would be wrong to delete anything if we have a selection.
2151         if (selection.set())
2152                 return false;
2153
2154         // We allow all kinds of "mumbo-jumbo" when freespacing.
2155         if (old_cursor.par()->layout()->free_spacing
2156             || old_cursor.par()->isFreeSpacing()) {
2157                 return false;
2158         }
2159
2160         /* Ok I'll put some comments here about what is missing.
2161            I have fixed BackSpace (and thus Delete) to not delete
2162            double-spaces automagically. I have also changed Cut,
2163            Copy and Paste to hopefully do some sensible things.
2164            There are still some small problems that can lead to
2165            double spaces stored in the document file or space at
2166            the beginning of paragraphs. This happens if you have
2167            the cursor betwenn to spaces and then save. Or if you
2168            cut and paste and the selection have a space at the
2169            beginning and then save right after the paste. I am
2170            sure none of these are very hard to fix, but I will
2171            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2172            that I can get some feedback. (Lgb)
2173         */
2174
2175         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2176         // delete the LineSeparator.
2177         // MISSING
2178
2179         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2180         // delete the LineSeparator.
2181         // MISSING
2182
2183         // If the pos around the old_cursor were spaces, delete one of them.
2184         if (old_cursor.par() != cursor.par()
2185             || old_cursor.pos() != cursor.pos()) {
2186                 // Only if the cursor has really moved
2187
2188                 if (old_cursor.pos() > 0
2189                     && old_cursor.pos() < old_cursor.par()->size()
2190                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2191                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2192                         old_cursor.par()->erase(old_cursor.pos() - 1);
2193                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2194
2195 #ifdef WITH_WARNINGS
2196 #warning This will not work anymore when we have multiple views of the same buffer
2197 // In this case, we will have to correct also the cursors held by
2198 // other bufferviews. It will probably be easier to do that in a more
2199 // automated way in LyXCursor code. (JMarc 26/09/2001)
2200 #endif
2201                         // correct all cursors held by the LyXText
2202                         fixCursorAfterDelete(bview, cursor, old_cursor);
2203                         fixCursorAfterDelete(bview, selection.cursor,
2204                                              old_cursor);
2205                         fixCursorAfterDelete(bview, selection.start,
2206                                              old_cursor);
2207                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2208                         fixCursorAfterDelete(bview, last_sel_cursor,
2209                                              old_cursor);
2210                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2211                         fixCursorAfterDelete(bview, toggle_end_cursor,
2212                                              old_cursor);
2213                         return false;
2214                 }
2215         }
2216
2217         // don't delete anything if this is the ONLY paragraph!
2218         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2219                 return false;
2220
2221         // Do not delete empty paragraphs with keepempty set.
2222         if (old_cursor.par()->layout()->keepempty)
2223                 return false;
2224
2225         // only do our magic if we changed paragraph
2226         if (old_cursor.par() == cursor.par())
2227                 return false;
2228
2229         // record if we have deleted a paragraph
2230         // we can't possibly have deleted a paragraph before this point
2231         bool deleted = false;
2232
2233         if ((old_cursor.par()->empty()
2234              || (old_cursor.par()->size() == 1
2235                  && old_cursor.par()->isLineSeparator(0)))) {
2236                 // ok, we will delete anything
2237                 LyXCursor tmpcursor;
2238
2239                 // make sure that you do not delete any environments
2240                 status(bview, LyXText::NEED_MORE_REFRESH);
2241                 deleted = true;
2242
2243                 if (old_cursor.row()->previous()) {
2244                         refresh_row = old_cursor.row()->previous();
2245                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2246                         tmpcursor = cursor;
2247                         cursor = old_cursor; // that undo can restore the right cursor position
2248                         Paragraph * endpar = old_cursor.par()->next();
2249                         if (endpar && endpar->getDepth()) {
2250                                 while (endpar && endpar->getDepth()) {
2251                                         endpar = endpar->next();
2252                                 }
2253                         }
2254                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2255                         cursor = tmpcursor;
2256
2257                         // delete old row
2258                         removeRow(old_cursor.row());
2259                         if (ownerParagraph() == old_cursor.par()) {
2260                                 ownerParagraph(ownerParagraph()->next());
2261                         }
2262                         // delete old par
2263                         delete old_cursor.par();
2264
2265                         /* Breakagain the next par. Needed because of
2266                          * the parindent that can occur or dissappear.
2267                          * The next row can change its height, if
2268                          * there is another layout before */
2269                         if (refresh_row->next()) {
2270                                 breakAgain(bview, refresh_row->next());
2271                                 updateCounters(bview);
2272                         }
2273                         setHeightOfRow(bview, refresh_row);
2274                 } else {
2275                         refresh_row = old_cursor.row()->next();
2276                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2277
2278                         tmpcursor = cursor;
2279                         cursor = old_cursor; // that undo can restore the right cursor position
2280                         Paragraph * endpar = old_cursor.par()->next();
2281                         if (endpar && endpar->getDepth()) {
2282                                 while (endpar && endpar->getDepth()) {
2283                                         endpar = endpar->next();
2284                                 }
2285                         }
2286                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2287                         cursor = tmpcursor;
2288
2289                         // delete old row
2290                         removeRow(old_cursor.row());
2291                         // delete old par
2292                         if (ownerParagraph() == old_cursor.par()) {
2293                                 ownerParagraph(ownerParagraph()->next());
2294                         }
2295
2296                         delete old_cursor.par();
2297
2298                         /* Breakagain the next par. Needed because of
2299                            the parindent that can occur or dissappear.
2300                            The next row can change its height, if
2301                            there is another layout before */
2302                         if (refresh_row) {
2303                                 breakAgain(bview, refresh_row);
2304                                 updateCounters(bview);
2305                         }
2306                 }
2307
2308                 // correct cursor y
2309                 setCursorIntern(bview, cursor.par(), cursor.pos());
2310
2311                 if (selection.cursor.par()  == old_cursor.par()
2312                     && selection.cursor.pos() == old_cursor.pos()) {
2313                         // correct selection
2314                         selection.cursor = cursor;
2315                 }
2316         }
2317         if (!deleted) {
2318                 if (old_cursor.par()->stripLeadingSpaces()) {
2319                         redoParagraphs(bview, old_cursor,
2320                                        old_cursor.par()->next());
2321                         // correct cursor y
2322                         setCursorIntern(bview, cursor.par(), cursor.pos());
2323                         selection.cursor = cursor;
2324                 }
2325         }
2326         return deleted;
2327 }
2328
2329
2330 Paragraph * LyXText::ownerParagraph() const
2331 {
2332         if (inset_owner) {
2333                 return inset_owner->paragraph();
2334         }
2335         return &*(bv_owner->buffer()->paragraphs.begin());
2336 }
2337
2338
2339 void LyXText::ownerParagraph(Paragraph * p) const
2340 {
2341         if (inset_owner) {
2342                 inset_owner->paragraph(p);
2343         } else {
2344                 bv_owner->buffer()->paragraphs.set(p);
2345         }
2346 }
2347
2348
2349 void LyXText::ownerParagraph(int id, Paragraph * p) const
2350 {
2351         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2352         if (op && op->inInset()) {
2353                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2354         } else {
2355                 ownerParagraph(p);
2356         }
2357 }
2358
2359
2360 LyXText::text_status LyXText::status() const
2361 {
2362         return status_;
2363 }
2364
2365
2366 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2367 {
2368         LyXText * t = bview->text;
2369
2370         // We should only go up with refreshing code so this means that if
2371         // we have a MORE refresh we should never set it to LITTLE if we still
2372         // didn't handle it (and then it will be UNCHANGED. Now as long as
2373         // we stay inside one LyXText this may work but we need to tell the
2374         // outermost LyXText that it should REALLY draw us if there is some
2375         // change in a Inset::LyXText. So you see that when we are inside a
2376         // inset's LyXText we give the LITTLE to the outermost LyXText to
2377         // tell'em that it should redraw the actual row (where the inset
2378         // resides! Capito?!
2379
2380         if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2381                 status_ = st;
2382                 if (inset_owner && st != UNCHANGED) {
2383                         t->status(bview, NEED_VERY_LITTLE_REFRESH);
2384                         if (!t->refresh_row) {
2385                                 t->refresh_row = t->cursor.row();
2386                                 t->refresh_y = t->cursor.y() -
2387                                         t->cursor.row()->baseline();
2388                         }
2389                 }
2390         }
2391 }