]> git.lyx.org Git - lyx.git/blob - src/text2.C
remove stuff from LyXText::checkParagraph() I don't understand
[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         breakAgain(getRow(pit, pos));
1460         postPaint();
1461         setCursorIntern(cursor.par(), cursor.pos(), false, cursor.boundary());
1462 }
1463
1464
1465 // returns false if inset wasn't found
1466 bool LyXText::updateInset(InsetOld * inset)
1467 {
1468         // first check the current paragraph
1469         int pos = cursor.par()->getPositionOfInset(inset);
1470         if (pos != -1) {
1471                 checkParagraph(cursor.par(), pos);
1472                 return true;
1473         }
1474
1475         // check every paragraph
1476         ParagraphList::iterator par = ownerParagraphs().begin();
1477         ParagraphList::iterator end = ownerParagraphs().end();
1478         for (; par != end; ++par) {
1479                 pos = par->getPositionOfInset(inset);
1480                 if (pos != -1) {
1481                         checkParagraph(par, pos);
1482                         return true;
1483                 }
1484         }
1485
1486         return false;
1487 }
1488
1489
1490 bool LyXText::setCursor(ParagraphList::iterator pit,
1491                         pos_type pos,
1492                         bool setfont, bool boundary)
1493 {
1494         LyXCursor old_cursor = cursor;
1495         setCursorIntern(pit, pos, setfont, boundary);
1496         return deleteEmptyParagraphMechanism(old_cursor);
1497 }
1498
1499
1500 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1501                         pos_type pos, bool boundary)
1502 {
1503         Assert(pit != ownerParagraphs().end());
1504
1505         cur.par(pit);
1506         cur.pos(pos);
1507         cur.boundary(boundary);
1508         if (rows().empty())
1509                 return;
1510
1511         // get the cursor y position in text
1512         int y = 0;
1513         RowList::iterator row = getRow(pit, pos, y);
1514         RowList::iterator beg = rows().begin();
1515
1516         RowList::iterator old_row = row;
1517         // if we are before the first char of this row and are still in the
1518         // same paragraph and there is a previous row then put the cursor on
1519         // the end of the previous row
1520         cur.iy(y + row->baseline());
1521         if (row != beg &&
1522             pos &&
1523             boost::prior(row)->par() == row->par() &&
1524             pos < pit->size() &&
1525             pit->getChar(pos) == Paragraph::META_INSET) {
1526                 InsetOld * ins = pit->getInset(pos);
1527                 if (ins && (ins->needFullRow() || ins->display())) {
1528                         --row;
1529                         y -= row->height();
1530                 }
1531         }
1532
1533         // y is now the beginning of the cursor row
1534         y += row->baseline();
1535         // y is now the cursor baseline
1536         cur.y(y);
1537
1538         pos_type last = lastPrintablePos(*this, old_row);
1539
1540         // None of these should happen, but we're scaredy-cats
1541         if (pos > pit->size()) {
1542                 lyxerr << "dont like 1 please report" << endl;
1543                 pos = 0;
1544                 cur.pos(0);
1545         } else if (pos > last + 1) {
1546                 lyxerr << "dont like 2 please report" << endl;
1547                 // This shouldn't happen.
1548                 pos = last + 1;
1549                 cur.pos(pos);
1550         } else if (pos < row->pos()) {
1551                 lyxerr << "dont like 3 please report" << endl;
1552                 pos = row->pos();
1553                 cur.pos(pos);
1554         }
1555
1556         // now get the cursors x position
1557         float x = getCursorX(row, pos, last, boundary);
1558         cur.x(int(x));
1559         cur.x_fix(cur.x());
1560         if (old_row != row) {
1561                 x = getCursorX(old_row, pos, last, boundary);
1562                 cur.ix(int(x));
1563         } else
1564                 cur.ix(cur.x());
1565 /* We take out this for the time being because 1) the redraw code is not
1566    prepared to this yet and 2) because some good policy has yet to be decided
1567    while editting: for instance how to act on rows being created/deleted
1568    because of DEPM.
1569 */
1570 #if 0
1571         //if the cursor is in a visible row, anchor to it
1572         int topy = top_y();
1573         if (topy < y && y < topy + bv()->workHeight())
1574                 anchor_row(row);
1575 #endif
1576 }
1577
1578
1579 float LyXText::getCursorX(RowList::iterator rit,
1580                           pos_type pos, pos_type last, bool boundary) const
1581 {
1582         pos_type cursor_vpos = 0;
1583         int x;
1584         int fill_separator;
1585         int fill_hfill;
1586         int fill_label_hfill;
1587         // This call HAS to be here because of the BidiTables!!!
1588         prepareToPrint(rit, x, fill_separator, fill_hfill,
1589                        fill_label_hfill);
1590
1591         ParagraphList::iterator rit_par = rit->par();
1592         pos_type const rit_pos = rit->pos();
1593
1594         if (last < rit_pos)
1595                 cursor_vpos = rit_pos;
1596         else if (pos > last && !boundary)
1597                 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1598                         ? rit_pos : last + 1;
1599         else if (pos > rit_pos && (pos > last || boundary))
1600                 /// Place cursor after char at (logical) position pos - 1
1601                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1602                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1603         else
1604                 /// Place cursor before char at (logical) position pos
1605                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1606                         ? log2vis(pos) : log2vis(pos) + 1;
1607
1608         pos_type body_pos = rit_par->beginningOfBody();
1609         if ((body_pos > 0) &&
1610             ((body_pos - 1 > last) || !rit_par->isLineSeparator(body_pos - 1)))
1611                 body_pos = 0;
1612
1613         for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1614                 pos_type pos = vis2log(vpos);
1615                 if (body_pos > 0 && pos == body_pos - 1) {
1616                         x += fill_label_hfill +
1617                                 font_metrics::width(
1618                                         rit_par->layout()->labelsep,
1619                                         getLabelFont(bv()->buffer(), rit_par));
1620                         if (rit_par->isLineSeparator(body_pos - 1))
1621                                 x -= singleWidth(rit_par, body_pos - 1);
1622                 }
1623
1624                 if (hfillExpansion(*this, rit, pos)) {
1625                         x += singleWidth(rit_par, pos);
1626                         if (pos >= body_pos)
1627                                 x += fill_hfill;
1628                         else
1629                                 x += fill_label_hfill;
1630                 } else if (rit_par->isSeparator(pos)) {
1631                         x += singleWidth(rit_par, pos);
1632                         if (pos >= body_pos)
1633                                 x += fill_separator;
1634                 } else
1635                         x += singleWidth(rit_par, pos);
1636         }
1637         return x;
1638 }
1639
1640
1641 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1642                               pos_type pos, bool setfont, bool boundary)
1643 {
1644         UpdatableInset * it = pit->inInset();
1645         if (it) {
1646                 if (it != inset_owner) {
1647                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1648                                               << endl
1649                                               << "inset_owner is "
1650                                               << inset_owner << endl;
1651 #ifdef WITH_WARNINGS
1652 #warning I believe this code is wrong. (Lgb)
1653 #warning Jürgen, have a look at this. (Lgb)
1654 #warning Hmmm, I guess you are right but we
1655 #warning should verify when this is needed
1656 #endif
1657                         // Jürgen, would you like to have a look?
1658                         // I guess we need to move the outer cursor
1659                         // and open and lock the inset (bla bla bla)
1660                         // stuff I don't know... so can you have a look?
1661                         // (Lgb)
1662                         // I moved the lyxerr stuff in here so we can see if
1663                         // this is actually really needed and where!
1664                         // (Jug)
1665                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1666                         return;
1667                 }
1668         }
1669
1670         setCursor(cursor, pit, pos, boundary);
1671         if (setfont)
1672                 setCurrentFont();
1673 }
1674
1675
1676 void LyXText::setCurrentFont()
1677 {
1678         pos_type pos = cursor.pos();
1679         ParagraphList::iterator pit = cursor.par();
1680
1681         if (cursor.boundary() && pos > 0)
1682                 --pos;
1683
1684         if (pos > 0) {
1685                 if (pos == pit->size())
1686                         --pos;
1687                 else // potentional bug... BUG (Lgb)
1688                         if (pit->isSeparator(pos)) {
1689                                 if (pos > cursorRow()->pos() &&
1690                                     bidi_level(pos) % 2 ==
1691                                     bidi_level(pos - 1) % 2)
1692                                         --pos;
1693                                 else if (pos + 1 < pit->size())
1694                                         ++pos;
1695                         }
1696         }
1697
1698         current_font =
1699                 pit->getFontSettings(bv()->buffer()->params, pos);
1700         real_current_font = getFont(bv()->buffer(), pit, pos);
1701
1702         if (cursor.pos() == pit->size() &&
1703             isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1704             !cursor.boundary()) {
1705                 Language const * lang =
1706                         pit->getParLanguage(bv()->buffer()->params);
1707                 current_font.setLanguage(lang);
1708                 current_font.setNumber(LyXFont::OFF);
1709                 real_current_font.setLanguage(lang);
1710                 real_current_font.setNumber(LyXFont::OFF);
1711         }
1712 }
1713
1714
1715 // returns the column near the specified x-coordinate of the row
1716 // x is set to the real beginning of this column
1717 pos_type
1718 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1719 {
1720         int tmpx = 0;
1721         int fill_separator;
1722         int fill_hfill;
1723         int fill_label_hfill;
1724
1725         prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1726
1727         pos_type vc = rit->pos();
1728         pos_type last = lastPrintablePos(*this, rit);
1729         pos_type c = 0;
1730
1731         ParagraphList::iterator rit_par = rit->par();
1732         LyXLayout_ptr const & layout = rit->par()->layout();
1733
1734         bool left_side = false;
1735
1736         pos_type body_pos = rit_par->beginningOfBody();
1737         int last_tmpx = tmpx;
1738
1739         if (body_pos > 0 &&
1740             (body_pos - 1 > last ||
1741              !rit_par->isLineSeparator(body_pos - 1)))
1742                 body_pos = 0;
1743
1744         // check for empty row
1745         if (!rit_par->size()) {
1746                 x = tmpx;
1747                 return 0;
1748         }
1749
1750         while (vc <= last && tmpx <= x) {
1751                 c = vis2log(vc);
1752                 last_tmpx = tmpx;
1753                 if (body_pos > 0 && c == body_pos - 1) {
1754                         tmpx += fill_label_hfill +
1755                                 font_metrics::width(layout->labelsep,
1756                                                getLabelFont(bv()->buffer(), rit_par));
1757                         if (rit_par->isLineSeparator(body_pos - 1))
1758                                 tmpx -= singleWidth(rit_par, body_pos - 1);
1759                 }
1760
1761                 if (hfillExpansion(*this, rit, c)) {
1762                         tmpx += singleWidth(rit_par, c);
1763                         if (c >= body_pos)
1764                                 tmpx += fill_hfill;
1765                         else
1766                                 tmpx += fill_label_hfill;
1767                 } else if (rit_par->isSeparator(c)) {
1768                         tmpx += singleWidth(rit_par, c);
1769                         if (c >= body_pos)
1770                                 tmpx += fill_separator;
1771                 } else {
1772                         tmpx += singleWidth(rit_par, c);
1773                 }
1774                 ++vc;
1775         }
1776
1777         if ((tmpx + last_tmpx) / 2 > x) {
1778                 tmpx = last_tmpx;
1779                 left_side = true;
1780         }
1781
1782         if (vc > last + 1)  // This shouldn't happen.
1783                 vc = last + 1;
1784
1785         boundary = false;
1786         // This (rtl_support test) is not needed, but gives
1787         // some speedup if rtl_support=false
1788         RowList::iterator next_rit = boost::next(rit);
1789
1790         bool const lastrow = lyxrc.rtl_support &&
1791                 (next_rit == rowlist_.end() ||
1792                  next_rit->par() != rit_par);
1793
1794         // If lastrow is false, we don't need to compute
1795         // the value of rtl.
1796         bool const rtl = (lastrow)
1797                 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1798                 : false;
1799         if (lastrow &&
1800                  ((rtl &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
1801                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
1802                 c = last + 1;
1803         else if (vc == rit->pos()) {
1804                 c = vis2log(vc);
1805                 if (bidi_level(c) % 2 == 1)
1806                         ++c;
1807         } else {
1808                 c = vis2log(vc - 1);
1809                 bool const rtl = (bidi_level(c) % 2 == 1);
1810                 if (left_side == rtl) {
1811                         ++c;
1812                         boundary = isBoundary(bv()->buffer(), *rit_par, c);
1813                 }
1814         }
1815
1816         if (rit->pos() <= last && c > last
1817             && rit_par->isNewline(last)) {
1818                 if (bidi_level(last) % 2 == 0)
1819                         tmpx -= singleWidth(rit_par, last);
1820                 else
1821                         tmpx += singleWidth(rit_par, last);
1822                 c = last;
1823         }
1824
1825         c -= rit->pos();
1826         x = int(tmpx);
1827         return c;
1828 }
1829
1830
1831 void LyXText::setCursorFromCoordinates(int x, int y)
1832 {
1833         //LyXCursor old_cursor = cursor;
1834         setCursorFromCoordinates(cursor, x, y);
1835         setCurrentFont();
1836 #warning DEPM disabled, otherwise crash when entering new table
1837         //deleteEmptyParagraphMechanism(old_cursor);
1838 }
1839
1840
1841 namespace {
1842
1843         /**
1844          * return true if the cursor given is at the end of a row,
1845          * and the next row is filled by an inset that spans an entire
1846          * row.
1847          */
1848         bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1849         {
1850                 RowList::iterator row = lt.getRow(cur);
1851                 if (boost::next(row) == lt.rows().end())
1852                         return false;
1853
1854                 Row const & next = *boost::next(row);
1855
1856                 if (next.pos() != cur.pos() || next.par() != cur.par())
1857                         return false;
1858
1859                 if (cur.pos() == cur.par()->size()
1860                     || !cur.par()->isInset(cur.pos()))
1861                         return false;
1862
1863                 InsetOld const * inset = cur.par()->getInset(cur.pos());
1864                 if (inset->needFullRow() || inset->display())
1865                         return true;
1866
1867                 return false;
1868         }
1869 }
1870
1871
1872 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1873 {
1874         // Get the row first.
1875
1876         RowList::iterator row = getRowNearY(y);
1877         bool bound = false;
1878         pos_type const column = getColumnNearX(row, x, bound);
1879         cur.par(row->par());
1880         cur.pos(row->pos() + column);
1881         cur.x(x);
1882         cur.y(y + row->baseline());
1883
1884         if (beforeFullRowInset(*this, cur)) {
1885                 pos_type const last = lastPrintablePos(*this, row);
1886                 RowList::iterator next_row = boost::next(row);
1887
1888                 float x = getCursorX(next_row, cur.pos(), last, bound);
1889                 cur.ix(int(x));
1890                 cur.iy(y + row->height() + next_row->baseline());
1891         } else {
1892                 cur.iy(cur.y());
1893                 cur.ix(cur.x());
1894         }
1895         cur.boundary(bound);
1896 }
1897
1898
1899 void LyXText::cursorLeft(bool internal)
1900 {
1901         if (cursor.pos() > 0) {
1902                 bool boundary = cursor.boundary();
1903                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1904                 if (!internal && !boundary &&
1905                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1906                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
1907         } else if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph.
1908                 ParagraphList::iterator pit = boost::prior(cursor.par());
1909                 setCursor(pit, pit->size());
1910         }
1911 }
1912
1913
1914 void LyXText::cursorRight(bool internal)
1915 {
1916         bool const at_end = (cursor.pos() == cursor.par()->size());
1917         bool const at_newline = !at_end &&
1918                 cursor.par()->isNewline(cursor.pos());
1919
1920         if (!internal && cursor.boundary() && !at_newline)
1921                 setCursor(cursor.par(), cursor.pos(), true, false);
1922         else if (!at_end) {
1923                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1924                 if (!internal &&
1925                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1926                         setCursor(cursor.par(), cursor.pos(), true, true);
1927         } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1928                 setCursor(boost::next(cursor.par()), 0);
1929 }
1930
1931
1932 void LyXText::cursorUp(bool selecting)
1933 {
1934 #if 1
1935         int x = cursor.x_fix();
1936         int y = cursor.y() - cursorRow()->baseline() - 1;
1937         setCursorFromCoordinates(x, y);
1938         if (!selecting) {
1939                 int topy = top_y();
1940                 int y1 = cursor.iy() - topy;
1941                 int y2 = y1;
1942                 y -= topy;
1943                 InsetOld * inset_hit = checkInsetHit(x, y1);
1944                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1945                         inset_hit->localDispatch(
1946                                 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1947                 }
1948         }
1949 #else
1950         setCursorFromCoordinates(bv(), cursor.x_fix(),
1951                                  cursor.y() - cursorRow()->baseline() - 1);
1952 #endif
1953 }
1954
1955
1956 void LyXText::cursorDown(bool selecting)
1957 {
1958 #if 1
1959         int x = cursor.x_fix();
1960         int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1961         setCursorFromCoordinates(x, y);
1962         if (!selecting && cursorRow() == cursorIRow()) {
1963                 int topy = top_y();
1964                 int y1 = cursor.iy() - topy;
1965                 int y2 = y1;
1966                 y -= topy;
1967                 InsetOld * inset_hit = checkInsetHit(x, y1);
1968                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1969                         FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1970                         inset_hit->localDispatch(cmd);
1971                 }
1972         }
1973 #else
1974         setCursorFromCoordinates(bv(), cursor.x_fix(),
1975                                  cursor.y() - cursorRow()->baseline()
1976                                  + cursorRow()->height() + 1);
1977 #endif
1978 }
1979
1980
1981 void LyXText::cursorUpParagraph()
1982 {
1983         if (cursor.pos() > 0) {
1984                 setCursor(cursor.par(), 0);
1985         }
1986         else if (cursor.par() != ownerParagraphs().begin()) {
1987                 setCursor(boost::prior(cursor.par()), 0);
1988         }
1989 }
1990
1991
1992 void LyXText::cursorDownParagraph()
1993 {
1994         ParagraphList::iterator par = cursor.par();
1995         ParagraphList::iterator next_par = boost::next(par);
1996
1997         if (next_par != ownerParagraphs().end()) {
1998                 setCursor(next_par, 0);
1999         } else {
2000                 setCursor(par, par->size());
2001         }
2002 }
2003
2004 // fix the cursor `cur' after a characters has been deleted at `where'
2005 // position. Called by deleteEmptyParagraphMechanism
2006 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2007                                    LyXCursor const & where)
2008 {
2009         // if cursor is not in the paragraph where the delete occured,
2010         // do nothing
2011         if (cur.par() != where.par())
2012                 return;
2013
2014         // if cursor position is after the place where the delete occured,
2015         // update it
2016         if (cur.pos() > where.pos())
2017                 cur.pos(cur.pos()-1);
2018
2019         // check also if we don't want to set the cursor on a spot behind the
2020         // pagragraph because we erased the last character.
2021         if (cur.pos() > cur.par()->size())
2022                 cur.pos(cur.par()->size());
2023
2024         // recompute row et al. for this cursor
2025         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2026 }
2027
2028
2029 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2030 {
2031         // Would be wrong to delete anything if we have a selection.
2032         if (selection.set())
2033                 return false;
2034
2035         // We allow all kinds of "mumbo-jumbo" when freespacing.
2036         if (old_cursor.par()->layout()->free_spacing
2037             || old_cursor.par()->isFreeSpacing()) {
2038                 return false;
2039         }
2040
2041         /* Ok I'll put some comments here about what is missing.
2042            I have fixed BackSpace (and thus Delete) to not delete
2043            double-spaces automagically. I have also changed Cut,
2044            Copy and Paste to hopefully do some sensible things.
2045            There are still some small problems that can lead to
2046            double spaces stored in the document file or space at
2047            the beginning of paragraphs. This happens if you have
2048            the cursor betwenn to spaces and then save. Or if you
2049            cut and paste and the selection have a space at the
2050            beginning and then save right after the paste. I am
2051            sure none of these are very hard to fix, but I will
2052            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2053            that I can get some feedback. (Lgb)
2054         */
2055
2056         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2057         // delete the LineSeparator.
2058         // MISSING
2059
2060         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2061         // delete the LineSeparator.
2062         // MISSING
2063
2064         // If the pos around the old_cursor were spaces, delete one of them.
2065         if (old_cursor.par() != cursor.par()
2066             || old_cursor.pos() != cursor.pos()) {
2067                 // Only if the cursor has really moved
2068
2069                 if (old_cursor.pos() > 0
2070                     && old_cursor.pos() < old_cursor.par()->size()
2071                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2072                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2073                         old_cursor.par()->erase(old_cursor.pos() - 1);
2074                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2075
2076 #ifdef WITH_WARNINGS
2077 #warning This will not work anymore when we have multiple views of the same buffer
2078 // In this case, we will have to correct also the cursors held by
2079 // other bufferviews. It will probably be easier to do that in a more
2080 // automated way in LyXCursor code. (JMarc 26/09/2001)
2081 #endif
2082                         // correct all cursors held by the LyXText
2083                         fixCursorAfterDelete(cursor, old_cursor);
2084                         fixCursorAfterDelete(selection.cursor,
2085                                              old_cursor);
2086                         fixCursorAfterDelete(selection.start,
2087                                              old_cursor);
2088                         fixCursorAfterDelete(selection.end, old_cursor);
2089                         fixCursorAfterDelete(last_sel_cursor,
2090                                              old_cursor);
2091                         fixCursorAfterDelete(toggle_cursor, old_cursor);
2092                         fixCursorAfterDelete(toggle_end_cursor,
2093                                              old_cursor);
2094                         return false;
2095                 }
2096         }
2097
2098         // don't delete anything if this is the ONLY paragraph!
2099         if (ownerParagraphs().size() == 1)
2100                 return false;
2101
2102         // Do not delete empty paragraphs with keepempty set.
2103         if (old_cursor.par()->allowEmpty())
2104                 return false;
2105
2106         // only do our magic if we changed paragraph
2107         if (old_cursor.par() == cursor.par())
2108                 return false;
2109
2110         // record if we have deleted a paragraph
2111         // we can't possibly have deleted a paragraph before this point
2112         bool deleted = false;
2113
2114         if (old_cursor.par()->empty() ||
2115             (old_cursor.par()->size() == 1 &&
2116              old_cursor.par()->isLineSeparator(0))) {
2117                 // ok, we will delete anything
2118                 LyXCursor tmpcursor;
2119
2120                 deleted = true;
2121
2122                 bool selection_position_was_oldcursor_position = (
2123                         selection.cursor.par()  == old_cursor.par()
2124                         && selection.cursor.pos() == old_cursor.pos());
2125
2126                 if (getRow(old_cursor) != rows().begin()) {
2127                         RowList::iterator prevrow = boost::prior(getRow(old_cursor));
2128                         postPaint();
2129                         tmpcursor = cursor;
2130                         cursor = old_cursor; // that undo can restore the right cursor position
2131                         #warning FIXME. --end() iterator is usable here
2132                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2133                         while (endpit != ownerParagraphs().end() &&
2134                                endpit->getDepth()) {
2135                                 ++endpit;
2136                         }
2137
2138                         recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2139                                 boost::prior(endpit));
2140                         cursor = tmpcursor;
2141
2142                         // delete old row
2143                         removeRow(getRow(old_cursor));
2144                         // delete old par
2145                         ownerParagraphs().erase(old_cursor.par());
2146
2147                         /* Breakagain the next par. Needed because of
2148                          * the parindent that can occur or dissappear.
2149                          * The next row can change its height, if
2150                          * there is another layout before */
2151                         RowList::iterator tmprit = boost::next(prevrow);
2152                         if (tmprit != rows().end()) {
2153                                 breakAgain(tmprit);
2154                                 updateCounters();
2155                         }
2156                         setHeightOfRow(prevrow);
2157                 } else {
2158                         RowList::iterator nextrow = boost::next(getRow(old_cursor));
2159                         postPaint();
2160
2161                         tmpcursor = cursor;
2162                         cursor = old_cursor; // that undo can restore the right cursor position
2163 #warning FIXME. --end() iterator is usable here
2164                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2165                         while (endpit != ownerParagraphs().end() &&
2166                                endpit->getDepth()) {
2167                                 ++endpit;
2168                         }
2169
2170                         recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2171                         cursor = tmpcursor;
2172
2173                         // delete old row
2174                         removeRow(getRow(old_cursor));
2175                         // delete old par
2176                         ownerParagraphs().erase(old_cursor.par());
2177
2178                         /* Breakagain the next par. Needed because of
2179                            the parindent that can occur or dissappear.
2180                            The next row can change its height, if
2181                            there is another layout before */
2182                         if (nextrow != rows().end()) {
2183                                 breakAgain(nextrow);
2184                                 updateCounters();
2185                         }
2186                 }
2187
2188                 // correct cursor y
2189                 setCursorIntern(cursor.par(), cursor.pos());
2190
2191                 if (selection_position_was_oldcursor_position) {
2192                         // correct selection
2193                         selection.cursor = cursor;
2194                 }
2195         }
2196         if (!deleted) {
2197                 if (old_cursor.par()->stripLeadingSpaces()) {
2198                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2199                         // correct cursor y
2200                         setCursorIntern(cursor.par(), cursor.pos());
2201                         selection.cursor = cursor;
2202                 }
2203         }
2204         return deleted;
2205 }
2206
2207
2208 ParagraphList & LyXText::ownerParagraphs() const
2209 {
2210         if (inset_owner) {
2211                 return inset_owner->paragraphs;
2212         }
2213         return bv_owner->buffer()->paragraphs;
2214 }
2215
2216
2217 bool LyXText::needRefresh() const
2218 {
2219         return need_refresh_;
2220 }
2221
2222
2223 void LyXText::clearPaint()
2224 {
2225         need_refresh_ = false;
2226 }
2227
2228
2229 void LyXText::postPaint()
2230 {
2231         need_refresh_ = true;
2232
2233         // We are an inset's lyxtext. Tell the top-level lyxtext
2234         // it needs to update the row we're in.
2235         if (inset_owner)
2236                 bv()->text->postPaint();
2237 }
2238
2239
2240 bool LyXText::isInInset() const
2241 {
2242         // Sub-level has non-null bv owner and
2243         // non-null inset owner.
2244         return inset_owner != 0 && bv_owner != 0;
2245 }
2246
2247
2248 int defaultRowHeight()
2249 {
2250         LyXFont const font(LyXFont::ALL_SANE);
2251         return int(font_metrics::maxAscent(font)
2252                  + font_metrics::maxDescent(font) * 1.5);
2253 }