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