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