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