]> git.lyx.org Git - lyx.git/blob - src/text2.C
Does this fix the "random cursor jumping"?
[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, cursor.row());
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, cursor.row());
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, cursor.row());
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 }
887
888
889 void LyXText::fullRebreak(BufferView * bview)
890 {
891         if (!firstrow) {
892                 init(bview);
893                 return;
894         }
895         if (need_break_row) {
896                 breakAgain(bview, need_break_row);
897                 need_break_row = 0;
898                 return;
899         }
900 }
901
902
903 // important for the screen
904
905
906 // the cursor set functions have a special mechanism. When they
907 // realize, that you left an empty paragraph, they will delete it.
908 // They also delete the corresponding row
909
910 // need the selection cursor:
911 void LyXText::setSelection(BufferView * bview)
912 {
913         bool const lsel = selection.set();
914
915         if (!selection.set()) {
916                 last_sel_cursor = selection.cursor;
917                 selection.start = selection.cursor;
918                 selection.end = selection.cursor;
919         }
920
921         selection.set(true);
922
923         // first the toggling area
924         if (cursor.y() < last_sel_cursor.y()
925             || (cursor.y() == last_sel_cursor.y()
926                 && cursor.x() < last_sel_cursor.x())) {
927                 toggle_end_cursor = last_sel_cursor;
928                 toggle_cursor = cursor;
929         } else {
930                 toggle_end_cursor = cursor;
931                 toggle_cursor = last_sel_cursor;
932         }
933
934         last_sel_cursor = cursor;
935
936         // and now the whole selection
937
938         if (selection.cursor.par() == cursor.par())
939                 if (selection.cursor.pos() < cursor.pos()) {
940                         selection.end = cursor;
941                         selection.start = selection.cursor;
942                 } else {
943                         selection.end = selection.cursor;
944                         selection.start = cursor;
945                 }
946         else if (selection.cursor.y() < cursor.y() ||
947                  (selection.cursor.y() == cursor.y()
948                   && selection.cursor.x() < cursor.x())) {
949                 selection.end = cursor;
950                 selection.start = selection.cursor;
951         }
952         else {
953                 selection.end = selection.cursor;
954                 selection.start = cursor;
955         }
956
957         // a selection with no contents is not a selection
958         if (selection.start.par() == selection.end.par() &&
959             selection.start.pos() == selection.end.pos())
960                 selection.set(false);
961
962         if (inset_owner && (selection.set() || lsel))
963                 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
964 }
965
966
967 string const LyXText::selectionAsString(Buffer const * buffer,
968                                         bool label) const
969 {
970         if (!selection.set()) return string();
971
972         // should be const ...
973         Paragraph * startpar(selection.start.par());
974         Paragraph * endpar(selection.end.par());
975         pos_type const startpos(selection.start.pos());
976         pos_type const endpos(selection.end.pos());
977          
978         if (startpar == endpar) {
979                 return startpar->asString(buffer, startpos, endpos, label);
980         }
981
982         string result;
983
984         // First paragraph in selection
985         result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
986
987         // The paragraphs in between (if any)
988         LyXCursor tmpcur(selection.start);
989         tmpcur.par(tmpcur.par()->next());
990         while (tmpcur.par() != endpar) {
991                 result += tmpcur.par()->asString(buffer, 0,
992                                                  tmpcur.par()->size(),
993                                                  label) + "\n\n";
994                 tmpcur.par(tmpcur.par()->next());
995         }
996
997         // Last paragraph in selection
998         result += endpar->asString(buffer, 0, endpos, label);
999
1000         return result;
1001 }
1002
1003
1004 void LyXText::clearSelection() const
1005 {
1006         selection.set(false);
1007         selection.mark(false);
1008         last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1009         // reset this in the bv_owner!
1010         if (bv_owner && bv_owner->text)
1011                 bv_owner->text->xsel_cache.set(false);
1012 }
1013
1014
1015 void LyXText::cursorHome(BufferView * bview) const
1016 {
1017         setCursor(bview, cursor.par(), cursor.row()->pos());
1018 }
1019
1020
1021 void LyXText::cursorEnd(BufferView * bview) const
1022 {
1023         if (!cursor.row()->next()
1024             || cursor.row()->next()->par() != cursor.row()->par()) {
1025                 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1026         } else {
1027                 if (cursor.par()->size() &&
1028                     (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1029                      || cursor.par()->isNewline(rowLast(cursor.row())))) {
1030                         setCursor(bview, cursor.par(), rowLast(cursor.row()));
1031                 } else {
1032                         setCursor(bview,cursor.par(),
1033                                   rowLast(cursor.row()) + 1);
1034                 }
1035         }
1036 }
1037
1038
1039 void LyXText::cursorTop(BufferView * bview) const
1040 {
1041         while (cursor.par()->previous())
1042                 cursor.par(cursor.par()->previous());
1043         setCursor(bview, cursor.par(), 0);
1044 }
1045
1046
1047 void LyXText::cursorBottom(BufferView * bview) const
1048 {
1049         while (cursor.par()->next())
1050                 cursor.par(cursor.par()->next());
1051         setCursor(bview, cursor.par(), cursor.par()->size());
1052 }
1053
1054
1055 void LyXText::toggleFree(BufferView * bview,
1056                          LyXFont const & font, bool toggleall)
1057 {
1058         // If the mask is completely neutral, tell user
1059         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1060                 // Could only happen with user style
1061                 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1062                 return;
1063         }
1064
1065         // Try implicit word selection
1066         // If there is a change in the language the implicit word selection
1067         // is disabled.
1068         LyXCursor resetCursor = cursor;
1069         bool implicitSelection = (font.language() == ignore_language
1070                                   && font.number() == LyXFont::IGNORE)
1071                 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1072
1073         // Set font
1074         setFont(bview, font, toggleall);
1075
1076         // Implicit selections are cleared afterwards
1077         //and cursor is set to the original position.
1078         if (implicitSelection) {
1079                 clearSelection();
1080                 cursor = resetCursor;
1081                 setCursor(bview, cursor.par(), cursor.pos());
1082                 selection.cursor = cursor;
1083         }
1084         if (inset_owner)
1085                 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1086 }
1087
1088
1089 string LyXText::getStringToIndex(BufferView * bview)
1090 {
1091         string idxstring;
1092
1093         // Try implicit word selection
1094         // If there is a change in the language the implicit word selection
1095         // is disabled.
1096         LyXCursor const reset_cursor = cursor;
1097         bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1098
1099         if (!selection.set()) {
1100                 bview->owner()->message(_("Nothing to index!"));
1101                 return string();
1102         }
1103         if (selection.start.par() != selection.end.par()) {
1104                 bview->owner()->message(_("Cannot index more than one paragraph!"));
1105                 return string();
1106         }
1107
1108         idxstring = selectionAsString(bview->buffer(), false);
1109
1110         // Implicit selections are cleared afterwards
1111         //and cursor is set to the original position.
1112         if (implicitSelection) {
1113                 clearSelection();
1114                 cursor = reset_cursor;
1115                 setCursor(bview, cursor.par(), cursor.pos());
1116                 selection.cursor = cursor;
1117         }
1118         return idxstring;
1119 }
1120
1121
1122 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1123                              Paragraph const * par) const
1124 {
1125         if (par->layout()->labeltype != LABEL_MANUAL)
1126                 return 0;
1127         else
1128                 return par->beginningOfMainBody();
1129 }
1130
1131
1132 // the DTP switches for paragraphs. LyX will store them in the first
1133 // physicla paragraph. When a paragraph is broken, the top settings rest,
1134 // the bottom settings are given to the new one. So I can make shure,
1135 // they do not duplicate themself and you cannnot make dirty things with
1136 // them!
1137
1138 void LyXText::setParagraph(BufferView * bview,
1139                            bool line_top, bool line_bottom,
1140                            bool pagebreak_top, bool pagebreak_bottom,
1141                            VSpace const & space_top,
1142                            VSpace const & space_bottom,
1143                            Spacing const & spacing,
1144                            LyXAlignment align,
1145                            string labelwidthstring,
1146                            bool noindent)
1147 {
1148         LyXCursor tmpcursor = cursor;
1149         if (!selection.set()) {
1150                 selection.start = cursor;
1151                 selection.end = cursor;
1152         }
1153
1154         // make sure that the depth behind the selection are restored, too
1155         Paragraph * endpar = selection.end.par()->next();
1156         Paragraph * undoendpar = endpar;
1157
1158         if (endpar && endpar->getDepth()) {
1159                 while (endpar && endpar->getDepth()) {
1160                         endpar = endpar->next();
1161                         undoendpar = endpar;
1162                 }
1163         }
1164         else if (endpar) {
1165                 // because of parindents etc.
1166                 endpar = endpar->next();
1167         }
1168
1169         setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1170
1171
1172         Paragraph * tmppar = selection.end.par();
1173
1174         while (tmppar != selection.start.par()->previous()) {
1175                 setCursor(bview, tmppar, 0);
1176                 status(bview, LyXText::NEED_MORE_REFRESH);
1177                 refresh_row = cursor.row();
1178                 refresh_y = cursor.y() - cursor.row()->baseline();
1179                 cursor.par()->params().lineTop(line_top);
1180                 cursor.par()->params().lineBottom(line_bottom);
1181                 cursor.par()->params().pagebreakTop(pagebreak_top);
1182                 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1183                 cursor.par()->params().spaceTop(space_top);
1184                 cursor.par()->params().spaceBottom(space_bottom);
1185                 cursor.par()->params().spacing(spacing);
1186                 // does the layout allow the new alignment?
1187                 LyXLayout_ptr const & layout = cursor.par()->layout();
1188
1189                 if (align == LYX_ALIGN_LAYOUT)
1190                         align = layout->align;
1191                 if (align & layout->alignpossible) {
1192                         if (align == layout->align)
1193                                 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1194                         else
1195                                 cursor.par()->params().align(align);
1196                 }
1197                 cursor.par()->setLabelWidthString(labelwidthstring);
1198                 cursor.par()->params().noindent(noindent);
1199                 tmppar = cursor.par()->previous();
1200         }
1201
1202         redoParagraphs(bview, selection.start, endpar);
1203
1204         clearSelection();
1205         setCursor(bview, selection.start.par(), selection.start.pos());
1206         selection.cursor = cursor;
1207         setCursor(bview, selection.end.par(), selection.end.pos());
1208         setSelection(bview);
1209         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1210         if (inset_owner)
1211                 bview->updateInset(inset_owner, true);
1212 }
1213
1214
1215 // set the counter of a paragraph. This includes the labels
1216 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1217 {
1218         LyXTextClass const & textclass = buf->params.getLyXTextClass();
1219         LyXLayout_ptr const & layout = par->layout();
1220
1221         // copy the prev-counters to this one,
1222         // unless this is the first paragraph
1223         if (par->previous()) {
1224
1225                 par->counters().copy(par->previous()->counters(), par->counters(), "");
1226                 
1227                 par->params().appendix(par->previous()->params().appendix());
1228                 if (!par->params().appendix() && par->params().startOfAppendix()) {
1229                         par->params().appendix(true);
1230                         par->counters().reset("");
1231                 }
1232                 par->enumdepth = par->previous()->enumdepth;
1233                 par->itemdepth = par->previous()->itemdepth;
1234         } else {
1235                 par->counters().reset("");
1236                 par->params().appendix(par->params().startOfAppendix());
1237                 par->enumdepth = 0;
1238                 par->itemdepth = 0;
1239         }
1240
1241         /* Maybe we have to increment the enumeration depth.
1242          * BUT, enumeration in a footnote is considered in isolation from its
1243          *      surrounding paragraph so don't increment if this is the
1244          *      first line of the footnote
1245          * AND, bibliographies can't have their depth changed ie. they
1246          *      are always of depth 0
1247          */
1248         if (par->previous()
1249             && par->previous()->getDepth() < par->getDepth()
1250             && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1251             && par->enumdepth < 3
1252             && layout->labeltype != LABEL_BIBLIO) {
1253                 par->enumdepth++;
1254         }
1255
1256         // Maybe we have to decrement the enumeration depth, see note above
1257         if (par->previous()
1258             && par->previous()->getDepth() > par->getDepth()
1259             && layout->labeltype != LABEL_BIBLIO) {
1260                 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1261                 par->counters().set(par->counters().enums[par->enumdepth],
1262                         par->depthHook(par->getDepth())->counters().value(par->counters().enums[par->enumdepth]));
1263         }
1264
1265         if (!par->params().labelString().empty()) {
1266                 par->params().labelString(string());
1267         }
1268
1269         if (layout->margintype == MARGIN_MANUAL) {
1270                 if (par->params().labelWidthString().empty()) {
1271                         par->setLabelWidthString(layout->labelstring());
1272                 }
1273         } else {
1274                 par->setLabelWidthString(string());
1275         }
1276    
1277         // is it a layout that has an automatic label?
1278         if (layout->labeltype >=  LABEL_COUNTER_CHAPTER) {
1279
1280                 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1281                 string numbertype, langtype;    
1282                 ostringstream s;
1283
1284                 if (i >= 0 && i<= buf->params.secnumdepth) {
1285
1286                         par->counters().step(par->counters().sects[i]);
1287
1288                         // Is there a label? Useful for Chapter layout
1289                         if (!par->params().appendix()) {
1290                                 if (!layout->labelstring().empty())
1291                                         par->params().labelString(layout->labelstring());
1292                                 else
1293                                         par->params().labelString(string());
1294                         } else {
1295                                 if (!layout->labelstring_appendix().empty())
1296                                         par->params().labelString(layout->labelstring_appendix());
1297                                 else
1298                                         par->params().labelString(string());
1299                         }
1300
1301                         // Use if an integer is here less than elegant. For now.
1302                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1303                         if (!par->params().appendix()) {
1304                                 numbertype = "sectioning";
1305                         } else {
1306                                 numbertype = "appendix";
1307                                 if (par->isRightToLeftPar(buf->params))
1308                                         langtype = "hebrew";
1309                                 else
1310                                         langtype = "latin";
1311                         }
1312                 
1313                         s << par->counters().numberLabel(par->counters().sects[i], 
1314                                 numbertype, langtype, head);
1315
1316                         par->params().labelString(par->params().labelString() +s.str().c_str());
1317                         // We really want to remove the c_str as soon as
1318                         // possible...
1319
1320                         // reset enum counters
1321                         par->counters().reset("enum");
1322                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1323                         par->counters().reset("enum");
1324                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1325                         par->counters().step(par->counters().enums[par->enumdepth]);
1326
1327                         s << par->counters().numberLabel(par->counters().enums[par->enumdepth], 
1328                                 "enumeration", langtype);
1329                         par->params().labelString(s.str().c_str());
1330
1331                 }
1332         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1333                 par->counters().step("bibitem");
1334                 int number = par->counters().value("bibitem");
1335                 if (!par->bibkey) {
1336                         InsetCommandParams p("bibitem" );
1337                         par->bibkey = new InsetBibKey(p);
1338                 }
1339                 par->bibkey->setCounter(number);
1340                 par->params().labelString(layout->labelstring());
1341
1342                 // In biblio should't be following counters but...
1343         } else {
1344                 string s = layout->labelstring();
1345
1346                 // the caption hack:
1347                 if (layout->labeltype == LABEL_SENSITIVE) {
1348                         bool isOK (par->inInset() && par->inInset()->owner() &&
1349                                    (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1350
1351                         if (isOK) {
1352                                 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1353                                 Floating const & fl
1354                                         = floatList.getType(tmp->type());
1355
1356                                 // Why doesn't it work? -- MV
1357                                 par->counters().step(fl.name());
1358                                 // We should get the correct number here too.
1359                                 ostringstream o;
1360                                 o << fl.name() << " " << par->counters().value(fl.name()) << ":";
1361                                 s = o.str();
1362                         } else {
1363                                 /* par->SetLayout(0);
1364                                    s = layout->labelstring;  */
1365                                 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1366                                         ? " :úåòîùî Ã¸Ã±Ã§" : "Senseless: ";
1367                         }
1368                 }
1369                 par->params().labelString(s);
1370
1371                 /* reset the enumeration counter. They are always resetted
1372                  * when there is any other layout between */
1373                 for (int i = par->enumdepth + 1; i < 4; i++) {
1374                         par->counters().set(par->counters().enums[i], 0);
1375                 }
1376         }
1377 }
1378
1379
1380 // Updates all counters BEHIND the row. Changed paragraphs
1381 // with a dynamic left margin will be rebroken.
1382 void LyXText::updateCounters(BufferView * bview, Row * row) const
1383 {
1384         Paragraph * par;
1385
1386         if (!row) {
1387                 row = firstrow;
1388                 par = row->par();
1389         } else {
1390                 par = row->par()->next();
1391         }
1392
1393         while (par) {
1394                 while (row->par() != par)
1395                         row = row->next();
1396
1397                 setCounter(bview->buffer(), par);
1398
1399                 // now check for the headline layouts. remember that they
1400                 // have a dynamic left margin
1401                 LyXLayout_ptr const & layout = par->layout();
1402
1403                 if (layout->margintype == MARGIN_DYNAMIC
1404                     || layout->labeltype == LABEL_SENSITIVE) {
1405                         // Rebreak the paragraph
1406                         removeParagraph(row);
1407                         appendParagraph(bview, row);
1408                 }
1409                 par = par->next();
1410         }
1411 }
1412
1413
1414 void LyXText::insertInset(BufferView * bview, Inset * inset)
1415 {
1416         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1417                 return;
1418         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1419         freezeUndo();
1420         cursor.par()->insertInset(cursor.pos(), inset);
1421         // Just to rebreak and refresh correctly.
1422         // The character will not be inserted a second time
1423         insertChar(bview, Paragraph::META_INSET);
1424         // If we enter a highly editable inset the cursor should be to before
1425         // the inset. This couldn't happen before as Undo was not handled inside
1426         // inset now after the Undo LyX tries to call inset->Edit(...) again
1427         // and cannot do this as the cursor is behind the inset and GetInset
1428         // does not return the inset!
1429         if (isHighlyEditableInset(inset)) {
1430                 cursorLeft(bview, true);
1431         }
1432         unFreezeUndo();
1433 }
1434
1435
1436 void LyXText::copyEnvironmentType()
1437 {
1438         copylayouttype = cursor.par()->layout()->name();
1439 }
1440
1441
1442 void LyXText::pasteEnvironmentType(BufferView * bview)
1443 {
1444         setLayout(bview, copylayouttype);
1445 }
1446
1447
1448 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1449 {
1450         // Stuff what we got on the clipboard. Even if there is no selection.
1451
1452         // There is a problem with having the stuffing here in that the
1453         // larger the selection the slower LyX will get. This can be
1454         // solved by running the line below only when the selection has
1455         // finished. The solution used currently just works, to make it
1456         // faster we need to be more clever and probably also have more
1457         // calls to stuffClipboard. (Lgb)
1458         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1459
1460         // This doesn't make sense, if there is no selection
1461         if (!selection.set())
1462                 return;
1463
1464         // OK, we have a selection. This is always between selection.start
1465         // and selection.end
1466
1467         // make sure that the depth behind the selection are restored, too
1468         Paragraph * endpar = selection.end.par()->next();
1469         Paragraph * undoendpar = endpar;
1470
1471         if (endpar && endpar->getDepth()) {
1472                 while (endpar && endpar->getDepth()) {
1473                         endpar = endpar->next();
1474                         undoendpar = endpar;
1475                 }
1476         } else if (endpar) {
1477                 endpar = endpar->next(); // because of parindents etc.
1478         }
1479
1480         setUndo(bview, Undo::DELETE,
1481                 selection.start.par(), undoendpar);
1482
1483         // there are two cases: cut only within one paragraph or
1484         // more than one paragraph
1485         if (selection.start.par() == selection.end.par()) {
1486                 // only within one paragraph
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                 selection.end.pos(pos);
1494         } else {
1495                 endpar = selection.end.par();
1496                 int pos = selection.end.pos();
1497                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1498                                           selection.start.pos(), pos,
1499                                           bview->buffer()->params.textclass,
1500                                           doclear, realcut);
1501                 cursor.par(endpar);
1502                 selection.end.par(endpar);
1503                 selection.end.pos(pos);
1504                 cursor.pos(selection.end.pos());
1505         }
1506         endpar = endpar->next();
1507
1508         // sometimes necessary
1509         if (doclear)
1510                 selection.start.par()->stripLeadingSpaces();
1511
1512         redoParagraphs(bview, selection.start, endpar);
1513
1514         // cutSelection can invalidate the cursor so we need to set
1515         // it anew. (Lgb)
1516         cursor = selection.start;
1517
1518         // need a valid cursor. (Lgb)
1519         clearSelection();
1520
1521         setCursor(bview, cursor.par(), cursor.pos());
1522         selection.cursor = cursor;
1523         updateCounters(bview, cursor.row());
1524 }
1525
1526
1527 void LyXText::copySelection(BufferView * bview)
1528 {
1529         // stuff the selection onto the X clipboard, from an explicit copy request
1530         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1531
1532         // this doesnt make sense, if there is no selection
1533         if (!selection.set())
1534                 return;
1535
1536         // ok we have a selection. This is always between selection.start
1537         // and sel_end cursor
1538
1539         // copy behind a space if there is one
1540         while (selection.start.par()->size() > selection.start.pos()
1541                && selection.start.par()->isLineSeparator(selection.start.pos())
1542                && (selection.start.par() != selection.end.par()
1543                    || selection.start.pos() < selection.end.pos()))
1544                 selection.start.pos(selection.start.pos() + 1);
1545
1546         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1547                                    selection.start.pos(), selection.end.pos(),
1548                                    bview->buffer()->params.textclass);
1549 }
1550
1551
1552 void LyXText::pasteSelection(BufferView * bview)
1553 {
1554         // this does not make sense, if there is nothing to paste
1555         if (!CutAndPaste::checkPastePossible(cursor.par()))
1556                 return;
1557
1558         setUndo(bview, Undo::INSERT,
1559                 cursor.par(), cursor.par()->next());
1560
1561         Paragraph * endpar;
1562         Paragraph * actpar = cursor.par();
1563         int pos = cursor.pos();
1564
1565         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1566                                     bview->buffer()->params.textclass);
1567
1568         redoParagraphs(bview, cursor, endpar);
1569
1570         setCursor(bview, cursor.par(), cursor.pos());
1571         clearSelection();
1572
1573         selection.cursor = cursor;
1574         setCursor(bview, actpar, pos);
1575         setSelection(bview);
1576         updateCounters(bview, cursor.row());
1577 }
1578
1579
1580 // sets the selection over the number of characters of string, no check!!
1581 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1582 {
1583         if (str.empty())
1584                 return;
1585
1586         selection.cursor = cursor;
1587         for (string::size_type i = 0; i < str.length(); ++i)
1588                 cursorRight(bview);
1589         setSelection(bview);
1590 }
1591
1592
1593 // simple replacing. The font of the first selected character is used
1594 void LyXText::replaceSelectionWithString(BufferView * bview,
1595                                          string const & str)
1596 {
1597         setCursorParUndo(bview);
1598         freezeUndo();
1599
1600         if (!selection.set()) { // create a dummy selection
1601                 selection.end = cursor;
1602                 selection.start = cursor;
1603         }
1604
1605         // Get font setting before we cut
1606         pos_type pos = selection.end.pos();
1607         LyXFont const font = selection.start.par()
1608                 ->getFontSettings(bview->buffer()->params,
1609                                   selection.start.pos());
1610
1611         // Insert the new string
1612         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1613                 selection.end.par()->insertChar(pos, (*cit), font);
1614                 ++pos;
1615         }
1616
1617         // Cut the selection
1618         cutSelection(bview, true, false);
1619
1620         unFreezeUndo();
1621 }
1622
1623
1624 // needed to insert the selection
1625 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1626 {
1627         Paragraph * par = cursor.par();
1628         pos_type pos = cursor.pos();
1629         Paragraph * endpar = cursor.par()->next();
1630
1631         setCursorParUndo(bview);
1632
1633         // only to be sure, should not be neccessary
1634         clearSelection();
1635
1636         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1637
1638         redoParagraphs(bview, cursor, endpar);
1639         setCursor(bview, cursor.par(), cursor.pos());
1640         selection.cursor = cursor;
1641         setCursor(bview, par, pos);
1642         setSelection(bview);
1643 }
1644
1645
1646 // turns double-CR to single CR, others where converted into one
1647 // blank. Then InsertStringAsLines is called
1648 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1649 {
1650         string linestr(str);
1651         bool newline_inserted = false;
1652         for (string::size_type i = 0; i < linestr.length(); ++i) {
1653                 if (linestr[i] == '\n') {
1654                         if (newline_inserted) {
1655                                 // we know that \r will be ignored by
1656                                 // InsertStringA. Of course, it is a dirty
1657                                 // trick, but it works...
1658                                 linestr[i - 1] = '\r';
1659                                 linestr[i] = '\n';
1660                         } else {
1661                                 linestr[i] = ' ';
1662                                 newline_inserted = true;
1663                         }
1664                 } else if (IsPrintable(linestr[i])) {
1665                         newline_inserted = false;
1666                 }
1667         }
1668         insertStringAsLines(bview, linestr);
1669 }
1670
1671
1672 bool LyXText::gotoNextInset(BufferView * bview,
1673                             vector<Inset::Code> const & codes,
1674                             string const & contents) const
1675 {
1676         LyXCursor res = cursor;
1677         Inset * inset;
1678         do {
1679                 if (res.pos() < res.par()->size() - 1) {
1680                         res.pos(res.pos() + 1);
1681                 } else  {
1682                         res.par(res.par()->next());
1683                         res.pos(0);
1684                 }
1685
1686         } while (res.par() &&
1687                  !(res.par()->isInset(res.pos())
1688                    && (inset = res.par()->getInset(res.pos())) != 0
1689                    && find(codes.begin(), codes.end(), inset->lyxCode())
1690                    != codes.end()
1691                    && (contents.empty() ||
1692                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1693                        == contents)));
1694
1695         if (res.par()) {
1696                 setCursor(bview, res.par(), res.pos(), false);
1697                 return true;
1698         }
1699         return false;
1700 }
1701
1702
1703 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1704                              pos_type pos)
1705 {
1706         LyXCursor tmpcursor;
1707
1708         int y = 0;
1709         pos_type z;
1710         Row * row = getRow(par, pos, y);
1711
1712         // is there a break one row above
1713         if (row->previous() && row->previous()->par() == row->par()) {
1714                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1715                 if (z >= row->pos()) {
1716                         // set the dimensions of the row above
1717                         y -= row->previous()->height();
1718                         refresh_y = y;
1719                         refresh_row = row->previous();
1720                         status(bview, LyXText::NEED_MORE_REFRESH);
1721
1722                         breakAgain(bview, row->previous());
1723
1724                         // set the cursor again. Otherwise
1725                         // dangling pointers are possible
1726                         setCursor(bview, cursor.par(), cursor.pos(),
1727                                   false, cursor.boundary());
1728                         selection.cursor = cursor;
1729                         return;
1730                 }
1731         }
1732
1733         int const tmpheight = row->height();
1734         pos_type const tmplast = rowLast(row);
1735         refresh_y = y;
1736         refresh_row = row;
1737
1738         breakAgain(bview, row);
1739         if (row->height() == tmpheight && rowLast(row) == tmplast)
1740                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1741         else
1742                 status(bview, LyXText::NEED_MORE_REFRESH);
1743
1744         // check the special right address boxes
1745         if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1746                 tmpcursor.par(par);
1747                 tmpcursor.row(row);
1748                 tmpcursor.y(y);
1749                 tmpcursor.x(0);
1750                 tmpcursor.x_fix(0);
1751                 tmpcursor.pos(pos);
1752                 redoDrawingOfParagraph(bview, tmpcursor);
1753         }
1754
1755         // set the cursor again. Otherwise dangling pointers are possible
1756         // also set the selection
1757
1758         if (selection.set()) {
1759                 tmpcursor = cursor;
1760                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1761                                 false, selection.cursor.boundary());
1762                 selection.cursor = cursor;
1763                 setCursorIntern(bview, selection.start.par(),
1764                                 selection.start.pos(),
1765                                 false, selection.start.boundary());
1766                 selection.start = cursor;
1767                 setCursorIntern(bview, selection.end.par(),
1768                                 selection.end.pos(),
1769                                 false, selection.end.boundary());
1770                 selection.end = cursor;
1771                 setCursorIntern(bview, last_sel_cursor.par(),
1772                                 last_sel_cursor.pos(),
1773                                 false, last_sel_cursor.boundary());
1774                 last_sel_cursor = cursor;
1775                 cursor = tmpcursor;
1776         }
1777         setCursorIntern(bview, cursor.par(), cursor.pos(),
1778                         false, cursor.boundary());
1779 }
1780
1781
1782 // returns false if inset wasn't found
1783 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1784 {
1785         // first check the current paragraph
1786         int pos = cursor.par()->getPositionOfInset(inset);
1787         if (pos != -1) {
1788                 checkParagraph(bview, cursor.par(), pos);
1789                 return true;
1790         }
1791
1792         // check every paragraph
1793
1794         Paragraph * par = ownerParagraph();
1795         do {
1796                 pos = par->getPositionOfInset(inset);
1797                 if (pos != -1) {
1798                         checkParagraph(bview, par, pos);
1799                         return true;
1800                 }
1801                 par = par->next();
1802         } while (par);
1803
1804         return false;
1805 }
1806
1807
1808 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1809                         pos_type pos,
1810                         bool setfont, bool boundary) const
1811 {
1812         LyXCursor old_cursor = cursor;
1813         setCursorIntern(bview, par, pos, setfont, boundary);
1814         return deleteEmptyParagraphMechanism(bview, old_cursor);
1815 }
1816
1817
1818 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1819                         pos_type pos, bool boundary) const
1820 {
1821         lyx::Assert(par);
1822         lyx::Assert(bview);
1823
1824         cur.par(par);
1825         cur.pos(pos);
1826         cur.boundary(boundary);
1827
1828         // get the cursor y position in text
1829         int y = 0;
1830         Row * row = getRow(par, pos, y);
1831         Row * old_row = row;
1832         cur.irow(row);
1833         // if we are before the first char of this row and are still in the
1834         // same paragraph and there is a previous row then put the cursor on
1835         // the end of the previous row
1836         cur.iy(y + row->baseline());
1837         Inset * ins;
1838         if (row->previous() && pos &&
1839                 row->previous()->par() == row->par() &&
1840                 par->getChar(pos) == Paragraph::META_INSET &&
1841                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1842         {
1843                 row = row->previous();
1844                 y -= row->height();
1845         }
1846
1847         cur.row(row);
1848         // y is now the beginning of the cursor row
1849         y += row->baseline();
1850         // y is now the cursor baseline
1851         cur.y(y);
1852
1853         pos_type last = rowLastPrintable(old_row);
1854
1855         if (pos > last + 1) {
1856                 // This shouldn't happen.
1857                 pos = last + 1;
1858                 cur.pos(pos);
1859         } else if (pos < row->pos()) {
1860                 pos = row->pos();
1861                 cur.pos(pos);
1862         }
1863
1864         // now get the cursors x position
1865         float x = getCursorX(bview, row, pos, last, boundary);
1866         cur.x(int(x));
1867         cur.x_fix(cur.x());
1868         if (old_row != row) {
1869                 x = getCursorX(bview, old_row, pos, last, boundary);
1870                 cur.ix(int(x));
1871         } else
1872                 cur.ix(cur.x());
1873 }
1874
1875
1876 float LyXText::getCursorX(BufferView * bview, Row * row,
1877                                                   pos_type pos, pos_type last, bool boundary) const
1878 {
1879         pos_type cursor_vpos = 0;
1880         float x;
1881         float fill_separator;
1882         float fill_hfill;
1883         float fill_label_hfill;
1884         // This call HAS to be here because of the BidiTables!!!
1885         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1886                        fill_label_hfill);
1887
1888         if (last < row->pos())
1889                 cursor_vpos = row->pos();
1890         else if (pos > last && !boundary)
1891                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1892                         ? row->pos() : last + 1;
1893         else if (pos > row->pos() &&
1894                  (pos > last || boundary))
1895                 /// Place cursor after char at (logical) position pos - 1
1896                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1897                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1898         else
1899                 /// Place cursor before char at (logical) position pos
1900                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1901                         ? log2vis(pos) : log2vis(pos) + 1;
1902
1903         pos_type main_body =
1904                 beginningOfMainBody(bview->buffer(), row->par());
1905         if ((main_body > 0) &&
1906             ((main_body-1 > last) ||
1907              !row->par()->isLineSeparator(main_body-1)))
1908                 main_body = 0;
1909
1910         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1911                 pos_type pos = vis2log(vpos);
1912                 if (main_body > 0 && pos == main_body - 1) {
1913                         x += fill_label_hfill +
1914                                 font_metrics::width(
1915                                         row->par()->layout()->labelsep,
1916                                         getLabelFont(bview->buffer(),
1917                                                      row->par()));
1918                         if (row->par()->isLineSeparator(main_body - 1))
1919                                 x -= singleWidth(bview,
1920                                                  row->par(), main_body - 1);
1921                 }
1922                 if (hfillExpansion(bview->buffer(), row, pos)) {
1923                         x += singleWidth(bview, row->par(), pos);
1924                         if (pos >= main_body)
1925                                 x += fill_hfill;
1926                         else
1927                                 x += fill_label_hfill;
1928                 } else if (row->par()->isSeparator(pos)) {
1929                         x += singleWidth(bview, row->par(), pos);
1930                         if (pos >= main_body)
1931                                 x += fill_separator;
1932                 } else
1933                         x += singleWidth(bview, row->par(), pos);
1934         }
1935         return x;
1936 }
1937
1938
1939 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1940                               pos_type pos, bool setfont, bool boundary) const
1941 {
1942         InsetText * it = static_cast<InsetText *>(par->inInset());
1943         if (it) {
1944                 if (it != inset_owner) {
1945                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1946                                               << endl
1947                                               << "inset_owner is "
1948                                               << inset_owner << endl;
1949 #ifdef WITH_WARNINGS
1950 #warning I believe this code is wrong. (Lgb)
1951 #warning Jürgen, have a look at this. (Lgb)
1952 #warning Hmmm, I guess you are right but we
1953 #warning should verify when this is needed
1954 #endif
1955                         // Jürgen, would you like to have a look?
1956                         // I guess we need to move the outer cursor
1957                         // and open and lock the inset (bla bla bla)
1958                         // stuff I don't know... so can you have a look?
1959                         // (Lgb)
1960                         // I moved the lyxerr stuff in here so we can see if
1961                         // this is actually really needed and where!
1962                         // (Jug)
1963                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1964                         return;
1965                 }
1966         }
1967
1968         setCursor(bview, cursor, par, pos, boundary);
1969         if (setfont)
1970                 setCurrentFont(bview);
1971 }
1972
1973
1974 void LyXText::setCurrentFont(BufferView * bview) const
1975 {
1976         pos_type pos = cursor.pos();
1977         if (cursor.boundary() && pos > 0)
1978                 --pos;
1979
1980         if (pos > 0) {
1981                 if (pos == cursor.par()->size())
1982                         --pos;
1983                 else // potentional bug... BUG (Lgb)
1984                         if (cursor.par()->isSeparator(pos)) {
1985                                 if (pos > cursor.row()->pos() &&
1986                                     bidi_level(pos) % 2 ==
1987                                     bidi_level(pos - 1) % 2)
1988                                         --pos;
1989                                 else if (pos + 1 < cursor.par()->size())
1990                                         ++pos;
1991                         }
1992         }
1993
1994         current_font =
1995                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1996         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1997
1998         if (cursor.pos() == cursor.par()->size() &&
1999             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2000             !cursor.boundary()) {
2001                 Language const * lang =
2002                         cursor.par()->getParLanguage(bview->buffer()->params);
2003                 current_font.setLanguage(lang);
2004                 current_font.setNumber(LyXFont::OFF);
2005                 real_current_font.setLanguage(lang);
2006                 real_current_font.setNumber(LyXFont::OFF);
2007         }
2008 }
2009
2010
2011 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2012 {
2013         LyXCursor old_cursor = cursor;
2014
2015         setCursorFromCoordinates(bview, cursor, x, y);
2016         setCurrentFont(bview);
2017         deleteEmptyParagraphMechanism(bview, old_cursor);
2018 }
2019
2020
2021 namespace {
2022
2023         /**
2024          * return true if the cursor given is at the end of a row,
2025          * and the next row is filled by an inset that spans an entire
2026          * row.
2027          */
2028         bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2029                 if (!row.next())
2030                         return false;
2031                 Row const & next = *row.next();
2032
2033                 if (next.pos() != cur.pos() || next.par() != cur.par())
2034                         return false;
2035                 if (!cur.par()->isInset(cur.pos()))
2036                         return false;
2037                 Inset const * inset = cur.par()->getInset(cur.pos());
2038                 if (inset->needFullRow() || inset->display())
2039                         return true;
2040                 return false;
2041         }
2042 }
2043  
2044
2045 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2046                                        int x, int y) const
2047 {
2048         // Get the row first.
2049
2050         Row * row = getRowNearY(y);
2051         bool bound = false;
2052         pos_type const column = getColumnNearX(bview, row, x, bound);
2053         cur.par(row->par());
2054         cur.pos(row->pos() + column);
2055         cur.x(x);
2056         cur.y(y + row->baseline());
2057         cur.row(row);
2058  
2059         if (beforeFullRowInset(*row, cur)) {
2060                 pos_type last = rowLastPrintable(row);
2061                 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2062                 cur.ix(int(x));
2063                 cur.iy(y + row->height() + row->next()->baseline());
2064                 cur.irow(row->next());
2065         } else {
2066                 cur.iy(cur.y());
2067                 cur.ix(cur.x());
2068                 cur.irow(row);
2069         }
2070         cur.boundary(bound);
2071 }
2072
2073
2074 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2075 {
2076         if (cursor.pos() > 0) {
2077                 bool boundary = cursor.boundary();
2078                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2079                 if (!internal && !boundary &&
2080                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2081                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2082         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2083                 Paragraph * par = cursor.par()->previous();
2084                 setCursor(bview, par, par->size());
2085         }
2086 }
2087
2088
2089 void LyXText::cursorRight(BufferView * bview, bool internal) const
2090 {
2091         if (!internal && cursor.boundary() &&
2092             !cursor.par()->isNewline(cursor.pos()))
2093                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2094         else if (cursor.pos() < cursor.par()->size()) {
2095                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2096                 if (!internal &&
2097                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2098                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2099         } else if (cursor.par()->next())
2100                 setCursor(bview, cursor.par()->next(), 0);
2101 }
2102
2103
2104 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2105 {
2106 #if 1
2107         int x = cursor.x_fix();
2108         int y = cursor.y() - cursor.row()->baseline() - 1;
2109         setCursorFromCoordinates(bview, x, y);
2110         if (!selecting) {
2111                 int y1 = cursor.iy() - first_y;
2112                 int y2 = y1;
2113                 y -= first_y;
2114                 Inset * inset_hit =
2115                         bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2116                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2117                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2118                 }
2119         }
2120 #else
2121         setCursorFromCoordinates(bview, cursor.x_fix(),
2122                                  cursor.y() - cursor.row()->baseline() - 1);
2123 #endif
2124 }
2125
2126
2127 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2128 {
2129 #if 1
2130         int x = cursor.x_fix();
2131         int y = cursor.y() - cursor.row()->baseline() +
2132                 cursor.row()->height() + 1;
2133         setCursorFromCoordinates(bview, x, y);
2134         if (!selecting && cursor.row() == cursor.irow()) {
2135                 int y1 = cursor.iy() - first_y;
2136                 int y2 = y1;
2137                 y -= first_y;
2138                 Inset * inset_hit =
2139                         bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2140                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2141                         inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2142                 }
2143         }
2144 #else
2145         setCursorFromCoordinates(bview, cursor.x_fix(),
2146                                  cursor.y() - cursor.row()->baseline()
2147                                  + cursor.row()->height() + 1);
2148 #endif
2149 }
2150
2151
2152 void LyXText::cursorUpParagraph(BufferView * bview) const
2153 {
2154         if (cursor.pos() > 0) {
2155                 setCursor(bview, cursor.par(), 0);
2156         }
2157         else if (cursor.par()->previous()) {
2158                 setCursor(bview, cursor.par()->previous(), 0);
2159         }
2160 }
2161
2162
2163 void LyXText::cursorDownParagraph(BufferView * bview) const
2164 {
2165         if (cursor.par()->next()) {
2166                 setCursor(bview, cursor.par()->next(), 0);
2167         } else {
2168                 setCursor(bview, cursor.par(), cursor.par()->size());
2169         }
2170 }
2171
2172 // fix the cursor `cur' after a characters has been deleted at `where'
2173 // position. Called by deleteEmptyParagraphMechanism
2174 void LyXText::fixCursorAfterDelete(BufferView * bview,
2175                                    LyXCursor & cur,
2176                                    LyXCursor const & where) const
2177 {
2178         // if cursor is not in the paragraph where the delete occured,
2179         // do nothing
2180         if (cur.par() != where.par())
2181                 return;
2182
2183         // if cursor position is after the place where the delete occured,
2184         // update it
2185         if (cur.pos() > where.pos())
2186                 cur.pos(cur.pos()-1);
2187
2188         // check also if we don't want to set the cursor on a spot behind the
2189         // pagragraph because we erased the last character.
2190         if (cur.pos() > cur.par()->size())
2191                 cur.pos(cur.par()->size());
2192
2193         // recompute row et al. for this cursor
2194         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2195 }
2196
2197
2198 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2199                                             LyXCursor const & old_cursor) const
2200 {
2201         // Would be wrong to delete anything if we have a selection.
2202         if (selection.set())
2203                 return false;
2204
2205         // We allow all kinds of "mumbo-jumbo" when freespacing.
2206         if (old_cursor.par()->layout()->free_spacing
2207             || old_cursor.par()->isFreeSpacing()) {
2208                 return false;
2209         }
2210
2211         /* Ok I'll put some comments here about what is missing.
2212            I have fixed BackSpace (and thus Delete) to not delete
2213            double-spaces automagically. I have also changed Cut,
2214            Copy and Paste to hopefully do some sensible things.
2215            There are still some small problems that can lead to
2216            double spaces stored in the document file or space at
2217            the beginning of paragraphs. This happens if you have
2218            the cursor betwenn to spaces and then save. Or if you
2219            cut and paste and the selection have a space at the
2220            beginning and then save right after the paste. I am
2221            sure none of these are very hard to fix, but I will
2222            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2223            that I can get some feedback. (Lgb)
2224         */
2225
2226         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2227         // delete the LineSeparator.
2228         // MISSING
2229
2230         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2231         // delete the LineSeparator.
2232         // MISSING
2233
2234         // If the pos around the old_cursor were spaces, delete one of them.
2235         if (old_cursor.par() != cursor.par()
2236             || old_cursor.pos() != cursor.pos()) {
2237                 // Only if the cursor has really moved
2238
2239                 if (old_cursor.pos() > 0
2240                     && old_cursor.pos() < old_cursor.par()->size()
2241                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2242                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2243                         old_cursor.par()->erase(old_cursor.pos() - 1);
2244                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2245
2246 #ifdef WITH_WARNINGS
2247 #warning This will not work anymore when we have multiple views of the same buffer
2248 // In this case, we will have to correct also the cursors held by
2249 // other bufferviews. It will probably be easier to do that in a more
2250 // automated way in LyXCursor code. (JMarc 26/09/2001)
2251 #endif
2252                         // correct all cursors held by the LyXText
2253                         fixCursorAfterDelete(bview, cursor, old_cursor);
2254                         fixCursorAfterDelete(bview, selection.cursor,
2255                                              old_cursor);
2256                         fixCursorAfterDelete(bview, selection.start,
2257                                              old_cursor);
2258                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2259                         fixCursorAfterDelete(bview, last_sel_cursor,
2260                                              old_cursor);
2261                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2262                         fixCursorAfterDelete(bview, toggle_end_cursor,
2263                                              old_cursor);
2264                         return false;
2265                 }
2266         }
2267
2268         // don't delete anything if this is the ONLY paragraph!
2269         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2270                 return false;
2271
2272         // Do not delete empty paragraphs with keepempty set.
2273         if (old_cursor.par()->layout()->keepempty)
2274                 return false;
2275
2276         // only do our magic if we changed paragraph
2277         if (old_cursor.par() == cursor.par())
2278                 return false;
2279
2280         // record if we have deleted a paragraph
2281         // we can't possibly have deleted a paragraph before this point
2282         bool deleted = false;
2283
2284         if ((old_cursor.par()->size() == 0
2285              || (old_cursor.par()->size() == 1
2286                  && old_cursor.par()->isLineSeparator(0)))) {
2287                 // ok, we will delete anything
2288                 LyXCursor tmpcursor;
2289
2290                 // make sure that you do not delete any environments
2291                 status(bview, LyXText::NEED_MORE_REFRESH);
2292                 deleted = true;
2293
2294                 if (old_cursor.row()->previous()) {
2295                         refresh_row = old_cursor.row()->previous();
2296                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2297                         tmpcursor = cursor;
2298                         cursor = old_cursor; // that undo can restore the right cursor position
2299                         Paragraph * endpar = old_cursor.par()->next();
2300                         if (endpar && endpar->getDepth()) {
2301                                 while (endpar && endpar->getDepth()) {
2302                                         endpar = endpar->next();
2303                                 }
2304                         }
2305                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2306                         cursor = tmpcursor;
2307
2308                         // delete old row
2309                         removeRow(old_cursor.row());
2310                         if (ownerParagraph() == old_cursor.par()) {
2311                                 ownerParagraph(ownerParagraph()->next());
2312                         }
2313                         // delete old par
2314                         delete old_cursor.par();
2315
2316                         /* Breakagain the next par. Needed because of
2317                          * the parindent that can occur or dissappear.
2318                          * The next row can change its height, if
2319                          * there is another layout before */
2320                         if (refresh_row->next()) {
2321                                 breakAgain(bview, refresh_row->next());
2322                                 updateCounters(bview, refresh_row);
2323                         }
2324                         setHeightOfRow(bview, refresh_row);
2325                 } else {
2326                         refresh_row = old_cursor.row()->next();
2327                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2328
2329                         tmpcursor = cursor;
2330                         cursor = old_cursor; // that undo can restore the right cursor position
2331                         Paragraph * endpar = old_cursor.par()->next();
2332                         if (endpar && endpar->getDepth()) {
2333                                 while (endpar && endpar->getDepth()) {
2334                                         endpar = endpar->next();
2335                                 }
2336                         }
2337                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2338                         cursor = tmpcursor;
2339
2340                         // delete old row
2341                         removeRow(old_cursor.row());
2342                         // delete old par
2343                         if (ownerParagraph() == old_cursor.par()) {
2344                                 ownerParagraph(ownerParagraph()->next());
2345                         }
2346
2347                         delete old_cursor.par();
2348
2349                         /* Breakagain the next par. Needed because of
2350                            the parindent that can occur or dissappear.
2351                            The next row can change its height, if
2352                            there is another layout before */
2353                         if (refresh_row) {
2354                                 breakAgain(bview, refresh_row);
2355                                 updateCounters(bview, refresh_row->previous());
2356                         }
2357                 }
2358
2359                 // correct cursor y
2360                 setCursorIntern(bview, cursor.par(), cursor.pos());
2361
2362                 if (selection.cursor.par()  == old_cursor.par()
2363                     && selection.cursor.pos() == old_cursor.pos()) {
2364                         // correct selection
2365                         selection.cursor = cursor;
2366                 }
2367         }
2368         if (!deleted) {
2369                 if (old_cursor.par()->stripLeadingSpaces()) {
2370                         redoParagraphs(bview, old_cursor,
2371                                        old_cursor.par()->next());
2372                         // correct cursor y
2373                         setCursorIntern(bview, cursor.par(), cursor.pos());
2374                         selection.cursor = cursor;
2375                 }
2376         }
2377         return deleted;
2378 }
2379
2380
2381 void LyXText::toggleAppendix(BufferView * bview)
2382 {
2383         Paragraph * par = cursor.par();
2384         bool start = !par->params().startOfAppendix();
2385
2386         // ensure that we have only one start_of_appendix in this document
2387         Paragraph * tmp = ownerParagraph();
2388         for (; tmp; tmp = tmp->next()) {
2389                 tmp->params().startOfAppendix(false);
2390         }
2391
2392         par->params().startOfAppendix(start);
2393
2394         // we can set the refreshing parameters now
2395         status(bview, LyXText::NEED_MORE_REFRESH);
2396         refresh_y = 0;
2397         refresh_row = 0; // not needed for full update
2398         updateCounters(bview, 0);
2399         setCursor(bview, cursor.par(), cursor.pos());
2400 }
2401
2402
2403 Paragraph * LyXText::ownerParagraph() const
2404 {
2405         if (inset_owner) {
2406                 return inset_owner->paragraph();
2407         }
2408         return bv_owner->buffer()->paragraph;
2409 }
2410
2411
2412 void LyXText::ownerParagraph(Paragraph * p) const
2413 {
2414         if (inset_owner) {
2415                 inset_owner->paragraph(p);
2416         } else {
2417                 bv_owner->buffer()->paragraph = p;
2418         }
2419 }
2420
2421
2422 void LyXText::ownerParagraph(int id, Paragraph * p) const
2423 {
2424         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2425         if (op && op->inInset()) {
2426                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2427         } else {
2428                 ownerParagraph(p);
2429         }
2430 }
2431
2432
2433 LyXText::text_status LyXText::status() const
2434 {
2435         return status_;
2436 }
2437
2438
2439 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2440 {
2441         LyXText * t = bview->text;
2442  
2443         // We should only go up with refreshing code so this means that if
2444         // we have a MORE refresh we should never set it to LITTLE if we still
2445         // didn't handle it (and then it will be UNCHANGED. Now as long as
2446         // we stay inside one LyXText this may work but we need to tell the
2447         // outermost LyXText that it should REALLY draw us if there is some
2448         // change in a Inset::LyXText. So you see that when we are inside a
2449         // inset's LyXText we give the LITTLE to the outermost LyXText to
2450         // tell'em that it should redraw the actual row (where the inset
2451         // resides! Capito?!
2452
2453         if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2454                 status_ = st;
2455                 if (inset_owner && st != UNCHANGED) {
2456                         t->status(bview, NEED_VERY_LITTLE_REFRESH);
2457                         if (!t->refresh_row) {
2458                                 t->refresh_row = t->cursor.row();
2459                                 t->refresh_y = t->cursor.y() -
2460                                         t->cursor.row()->baseline();
2461                         }
2462                 }
2463         }
2464 }