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