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