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