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