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