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